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

1#!/usr/bin/env python3 

2 

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. 

16 

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 

37 

38 Notes 

39 ----- 

40 This function uses `CSS4 named colours 

41 <https://matplotlib.org/stable/gallery/color/named_colors.html>`_ . 

42 

43 Copyright 2017 Thomas Guymer [1]_ 

44 

45 References 

46 ---------- 

47 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3 

48 """ 

49 

50 # Import standard modules ... 

51 import os 

52 

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 

85 

86 # Import sub-functions ... 

87 from .extract_polys import extract_polys 

88 

89 # ************************************************************************** 

90 

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 ) 

99 

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 

108 

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" 

114 

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}).") 

120 

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}\".") 

127 

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) 

133 

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)) 

148 

149 # Plot geometry ... 

150 ax.add_geometries( 

151 polys, 

152 cartopy.crs.PlateCarree(), 

153 edgecolor = "none", 

154 facecolor = facecolor, 

155 )