OSGeoLive-Notebooks/Cartopy/cartopy-un-flag.ipynb

228 lines
9.8 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"UN Flag\n",
"-------\n",
"\n",
"A demonstration of the power of Matplotlib combined with cartopy's Azimuthal\n",
"Equidistant projection to reproduce the UN flag.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import cartopy.crs as ccrs\n",
"import cartopy.feature as cfeature\n",
"import matplotlib.pyplot as plt\n",
"from matplotlib.patches import PathPatch\n",
"import matplotlib.path\n",
"import matplotlib.ticker\n",
"from matplotlib.transforms import BboxTransform, Bbox\n",
"import numpy as np\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"# When drawing the flag, we can either use white filled land, or be a little\n",
"# more fancy and use the Natural Earth shaded relief imagery.\n",
"filled_land = True\n",
"\n",
"\n",
"def olive_path():\n",
" \"\"\"\n",
" Return a Matplotlib path representing a single olive branch from the\n",
" UN Flag. The path coordinates were extracted from the SVG at\n",
" https://commons.wikimedia.org/wiki/File:Flag_of_the_United_Nations.svg.\n",
"\n",
" \"\"\"\n",
" olives_verts = np.array(\n",
" [[0, 2, 6, 9, 30, 55, 79, 94, 104, 117, 134, 157, 177,\n",
" 188, 199, 207, 191, 167, 149, 129, 109, 87, 53, 22, 0, 663,\n",
" 245, 223, 187, 158, 154, 150, 146, 149, 154, 158, 181, 184, 197,\n",
" 181, 167, 153, 142, 129, 116, 119, 123, 127, 151, 178, 203, 220,\n",
" 237, 245, 663, 280, 267, 232, 209, 205, 201, 196, 196, 201, 207,\n",
" 211, 224, 219, 230, 220, 212, 207, 198, 195, 176, 197, 220, 239,\n",
" 259, 277, 280, 663, 295, 293, 264, 250, 247, 244, 240, 240, 243,\n",
" 244, 249, 251, 250, 248, 242, 245, 233, 236, 230, 228, 224, 222,\n",
" 234, 249, 262, 275, 285, 291, 295, 296, 295, 663, 294, 293, 292,\n",
" 289, 294, 277, 271, 269, 268, 265, 264, 264, 264, 272, 260, 248,\n",
" 245, 243, 242, 240, 243, 245, 247, 252, 256, 259, 258, 257, 258,\n",
" 267, 285, 290, 294, 297, 294, 663, 285, 285, 277, 266, 265, 265,\n",
" 265, 277, 266, 268, 269, 269, 269, 268, 268, 267, 267, 264, 248,\n",
" 235, 232, 229, 228, 229, 232, 236, 246, 266, 269, 271, 285, 285,\n",
" 663, 252, 245, 238, 230, 246, 245, 250, 252, 255, 256, 256, 253,\n",
" 249, 242, 231, 214, 208, 208, 227, 244, 252, 258, 262, 262, 261,\n",
" 262, 264, 265, 252, 663, 185, 197, 206, 215, 223, 233, 242, 237,\n",
" 237, 230, 220, 202, 185, 663],\n",
" [8, 5, 3, 0, 22, 46, 46, 46, 35, 27, 16, 10, 18,\n",
" 22, 28, 38, 27, 26, 33, 41, 52, 52, 52, 30, 8, 595,\n",
" 77, 52, 61, 54, 53, 52, 53, 55, 55, 57, 65, 90, 106,\n",
" 96, 81, 68, 58, 54, 51, 50, 51, 50, 44, 34, 43, 48,\n",
" 61, 77, 595, 135, 104, 102, 83, 79, 76, 74, 74, 79, 84,\n",
" 90, 109, 135, 156, 145, 133, 121, 100, 77, 62, 69, 67, 80,\n",
" 92, 113, 135, 595, 198, 171, 156, 134, 129, 124, 120, 123, 126,\n",
" 129, 138, 149, 161, 175, 188, 202, 177, 144, 116, 110, 105, 99,\n",
" 108, 116, 126, 136, 147, 162, 173, 186, 198, 595, 249, 255, 261,\n",
" 267, 241, 222, 200, 192, 183, 175, 175, 175, 175, 199, 221, 240,\n",
" 245, 250, 256, 245, 233, 222, 207, 194, 180, 172, 162, 153, 154,\n",
" 171, 184, 202, 216, 233, 249, 595, 276, 296, 312, 327, 327, 327,\n",
" 327, 308, 284, 262, 240, 240, 239, 239, 242, 244, 247, 265, 277,\n",
" 290, 293, 296, 300, 291, 282, 274, 253, 236, 213, 235, 252, 276,\n",
" 595, 342, 349, 355, 357, 346, 326, 309, 303, 297, 291, 290, 297,\n",
" 304, 310, 321, 327, 343, 321, 305, 292, 286, 278, 270, 276, 281,\n",
" 287, 306, 328, 342, 595, 379, 369, 355, 343, 333, 326, 318, 328,\n",
" 340, 349, 366, 373, 379, 595]]).T\n",
" olives_codes = np.array([1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 4, 4, 4, 4, 4, 4, 4, 4, 79, 1, 4, 4, 4, 4, 4,\n",
" 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 4, 4, 4, 4, 4, 79, 1, 4, 4, 4, 4, 4, 4, 2, 4,\n",
" 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 79, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 79, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4,\n",
" 4, 4, 4, 4, 4, 79, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 4, 4, 4, 4, 4, 79, 1, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 4, 4, 4, 79, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n",
" 4, 4, 79], dtype=np.uint8)\n",
"\n",
" return matplotlib.path.Path(olives_verts, olives_codes)\n",
"\n",
"\n",
"def main():\n",
" blue = '#4b92db'\n",
"\n",
" # We're drawing a flag with a 3:5 aspect ratio.\n",
" fig = plt.figure(figsize=[7.5, 4.5], facecolor=blue)\n",
" # Put a blue background on the figure.\n",
" blue_background = PathPatch(matplotlib.path.Path.unit_rectangle(),\n",
" transform=fig.transFigure, color=blue,\n",
" zorder=-1)\n",
" fig.patches.append(blue_background)\n",
"\n",
" # Set up the Azimuthal Equidistant and Plate Carree projections\n",
" # for later use.\n",
" az_eq = ccrs.AzimuthalEquidistant(central_latitude=90)\n",
" pc = ccrs.PlateCarree()\n",
"\n",
" # Pick a suitable location for the map (which is in an Azimuthal\n",
" # Equidistant projection).\n",
" ax = fig.add_axes([0.25, 0.24, 0.5, 0.54], projection=az_eq)\n",
"\n",
" # The background patch and outline patch are not needed in this example.\n",
" ax.patch.set_facecolor('none')\n",
" # The Axes frame produces the outer meridian line.\n",
" for spine in ax.spines.values():\n",
" spine.update({'edgecolor': 'white', 'linewidth': 2})\n",
"\n",
" # We want the map to go down to -60 degrees latitude.\n",
" ax.set_extent([-180, 180, -60, 90], ccrs.PlateCarree())\n",
"\n",
" # Importantly, we want the axes to be circular at the -60 latitude\n",
" # rather than cartopy's default behaviour of zooming in and becoming\n",
" # square.\n",
" _, patch_radius = az_eq.transform_point(0, -60, pc)\n",
" circular_path = matplotlib.path.Path.circle(0, patch_radius)\n",
" ax.set_boundary(circular_path)\n",
"\n",
" if filled_land:\n",
" ax.add_feature(\n",
" cfeature.LAND, facecolor='white', edgecolor='none')\n",
" else:\n",
" ax.stock_img()\n",
"\n",
" gl = ax.gridlines(crs=pc, linewidth=3, color='white', linestyle='-')\n",
" # Meridians every 45 degrees, and 5 parallels.\n",
" gl.xlocator = matplotlib.ticker.FixedLocator(np.arange(0, 361, 45))\n",
" parallels = np.linspace(-60, 70, 5, endpoint=True)\n",
" gl.ylocator = matplotlib.ticker.FixedLocator(parallels)\n",
"\n",
" # Now add the olive branches around the axes. We do this in normalised\n",
" # figure coordinates\n",
" olive_leaf = olive_path()\n",
"\n",
" olives_bbox = Bbox.null()\n",
" olives_bbox.update_from_path(olive_leaf)\n",
"\n",
" # The first olive branch goes from left to right.\n",
" olive1_axes_bbox = Bbox([[0.45, 0.15], [0.725, 0.75]])\n",
" olive1_trans = BboxTransform(olives_bbox, olive1_axes_bbox)\n",
"\n",
" # THe second olive branch goes from right to left (mirroring the first).\n",
" olive2_axes_bbox = Bbox([[0.55, 0.15], [0.275, 0.75]])\n",
" olive2_trans = BboxTransform(olives_bbox, olive2_axes_bbox)\n",
"\n",
" olive1 = PathPatch(olive_leaf, facecolor='white', edgecolor='none',\n",
" transform=olive1_trans + fig.transFigure)\n",
" olive2 = PathPatch(olive_leaf, facecolor='white', edgecolor='none',\n",
" transform=olive2_trans + fig.transFigure)\n",
"\n",
" fig.patches.append(olive1)\n",
" fig.patches.append(olive2)\n",
"\n",
" plt.show()\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"main()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}