Coverage for pyguymer3/geo/_add_elevation.py: 2%
49 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def _add_elevation(
5 ax,
6 /,
7 *,
8 debug = __debug__,
9 fov = None,
10 maxElev = 8850.0,
11 onlyValid = False,
12 repair = False,
13 resolution = "10m",
14):
15 """Add elevation to a Cartopy axis.
17 Parameters
18 ----------
19 axis : cartopy.mpl.geoaxes.GeoAxesSubplot
20 the axis to add the elevation to
21 debug : bool, optional
22 print debug messages
23 fov : None or shapely.geometry.polygon.Polygon, optional
24 clip the plotted shapes to the provided field-of-view to work around
25 occaisional MatPlotLib or Cartopy plotting errors when shapes much
26 larger than the field-of-view are plotted
27 maxElev : float, optional
28 the maximum elevation of the colour scale and acts as an upper bound or
29 clip (in metres)
30 onlyValid : bool, optional
31 only add valid Polygons (checks for validity can take a while, if being
32 being called often)
33 repair : bool, optional
34 attempt to repair invalid Polygons
35 resolution : str, optional
36 the resolution of the elevation
38 Notes
39 -----
40 This function uses `CSS4 named colours
41 <https://matplotlib.org/stable/gallery/color/named_colors.html>`_ .
43 Copyright 2017 Thomas Guymer [1]_
45 References
46 ----------
47 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3
48 """
50 # Import standard modules ...
51 import os
53 # Import special modules ...
54 try:
55 import cartopy
56 cartopy.config.update(
57 {
58 "cache_dir" : os.path.expanduser("~/.local/share/cartopy_cache"),
59 }
60 )
61 except:
62 raise Exception("\"cartopy\" is not installed; run \"pip install --user Cartopy\"") from None
63 try:
64 import geojson
65 geojson.geometry.Geometry.__init__.__defaults__ = (None, False, 12) # NOTE: See https://github.com/jazzband/geojson/issues/135#issuecomment-596509669
66 except:
67 raise Exception("\"geojson\" is not installed; run \"pip install --user geojson\"") from None
68 try:
69 import matplotlib
70 matplotlib.rcParams.update(
71 {
72 "backend" : "Agg", # NOTE: See https://matplotlib.org/stable/gallery/user_interfaces/canvasagg.html
73 "figure.dpi" : 300,
74 "figure.figsize" : (9.6, 7.2), # NOTE: See https://github.com/Guymer/misc/blob/main/README.md#matplotlib-figure-sizes
75 "font.size" : 8,
76 }
77 )
78 except:
79 raise Exception("\"matplotlib\" is not installed; run \"pip install --user matplotlib\"") from None
80 try:
81 import shapely
82 import shapely.geometry
83 except:
84 raise Exception("\"shapely\" is not installed; run \"pip install --user Shapely\"") from None
86 # Import sub-functions ...
87 from .extract_polys import extract_polys
89 # **************************************************************************
91 # Create suitable colour map ...
92 cmap = matplotlib.colors.LinearSegmentedColormap.from_list(
93 "elevation",
94 [
95 matplotlib.colors.to_rgba(matplotlib.colors.CSS4_COLORS["olivedrab"]),
96 matplotlib.colors.to_rgba(matplotlib.colors.CSS4_COLORS["lightgrey"]),
97 ]
98 )
100 # Define scales ...
101 scale = {
102 "10m" : "08km",
103 "50m" : "16km",
104 "110m" : "32km",
105 }
106 if resolution not in scale:
107 raise Exception(f"\"{resolution}\" is not a recognised resolution; \"{__name__}\" needs updating") from None
109 # Loop over elevations ...
110 # NOTE: Rounded to the nearest integer, Mount Everest is 8,849m ASL.
111 for elevation in range(250, 9000, 250):
112 # Create short-hand ...
113 name = f"{elevation:04d}m"
115 # Create suitable colour ...
116 # NOTE: Rounded to the nearest integer, Mount Everest is 8,849m ASL.
117 facecolor = cmap(float(elevation) / maxElev)
118 if debug:
119 print(f"INFO: \"{name}\" is ({facecolor[0]:.6f},{facecolor[1]:.6f},{facecolor[2]:.6f},{facecolor[3]:.6f}).")
121 # Find file containing the shapes ...
122 sfile = f"{os.path.dirname(__file__)}/../data/geojson/GLOBE/scale={scale[resolution]}/elev={elevation:04d}m.geojson"
123 if not os.path.exists(sfile):
124 continue
125 if debug:
126 print(f"INFO: \"{name}\" is \"{sfile}\".")
128 # Load the GeoJSON geometry collection and convert it to a Shapely
129 # geometry collection ...
130 with open(sfile, "rt", encoding = "utf-8") as fObj:
131 coll = geojson.load(fObj)
132 coll = shapely.geometry.shape(coll)
134 # Create a list of Polygons to plot (taking in to account if the user
135 # provided a field-of-view to clip them by) ...
136 polys = []
137 for poly in extract_polys(
138 coll,
139 onlyValid = onlyValid,
140 repair = repair,
141 ):
142 if fov is None:
143 polys.append(poly)
144 continue
145 if poly.disjoint(fov):
146 continue
147 polys.append(poly.intersection(fov))
149 # Plot geometry ...
150 ax.add_geometries(
151 polys,
152 cartopy.crs.PlateCarree(),
153 edgecolor = "none",
154 facecolor = facecolor,
155 )