1818from shapely .geometry import Polygon , mapping
1919from shapely .ops import transform
2020from shapely .vectorized import contains
21+ import cartopy .crs as ccrs
2122
2223
23- def plot_mesh_edge (msh_t : jigsaw_msh_t , ax : Axes = None , ** kwargs ) -> Axes :
24+ def plot_mesh_edge (
25+ msh_t : jigsaw_msh_t ,
26+ ax = None ,
27+ to_geo = None ,
28+ ** kwargs
29+ ) -> None :
2430 """
2531 Plots the edges of a triangular mesh on a given set of axes.
2632
@@ -32,75 +38,87 @@ def plot_mesh_edge(msh_t: jigsaw_msh_t, ax: Axes = None, **kwargs) -> Axes:
3238 - 'tria3['index']' containing the indices of the triangles
3339 ax : Axes, optional
3440 The axes to plot on. If None, a new plot is created. Default is None.
41+ to_geo : callable, optional
42+ A function to transform (x, y) coordinates from projected to geographic CRS.
3543 **kwargs : keyword arguments, optional
3644 Additional keyword arguments passed to the `triplot` function.
3745 These can be used to customize the plot (e.g., color, line style).
3846
39- Returns
40- -------
41- ax : Axes
42- The axes object with the plotted mesh edges.
47+
4348 """
4449
45- crd = msh_t .vert2 ["coord" ]
50+ crd = np . array ( msh_t .vert2 ["coord" ], copy = True )
4651 cnn = msh_t .tria3 ["index" ]
4752
48- if ax is None :
49- _fig , ax = plt .subplots ()
50- ax .triplot (crd [:, 0 ], crd [:, 1 ], cnn , ** kwargs )
51- ax .set_title ("Mesh Design Criteria" )
52- ax .set_xlabel ("X UTM" )
53- ax .set_ylabel ("Y UTM" )
53+ if to_geo is not None :
54+ crd [:, 0 ], crd [:, 1 ] = to_geo (crd [:, 0 ], crd [:, 1 ])
55+ bnd = [crd [:, 0 ].min (), crd [:, 0 ].max (), crd [:, 1 ].min (), crd [:, 1 ].max ()]
5456
55- return ax
57+ ax .triplot (crd [:, 0 ], crd [:, 1 ], cnn , ** kwargs )
58+ ax .set_extent ([* bnd ], crs = ccrs .PlateCarree ())
59+ gl = ax .gridlines (draw_labels = True )
60+ gl .top_labels = False
61+ gl .right_labels = False
5662
5763
5864def plot_mesh_vals (
5965 msh_t : jigsaw_msh_t ,
6066 ax : Axes = None ,
6167 colorbar : bool = True ,
62- clim : tuple = None ,
68+ clim : Tuple [float , float ] = None ,
69+ to_geo : callable = None ,
6370 ** kwargs ,
6471) -> Axes :
6572 """
66- Plots the mesh values on a triangular mesh.
73+ Plots the mesh values on a triangular mesh, with optional transformation
74+ from UTM to geographic coordinates.
6775
6876 Parameters
6977 ----------
7078 msh_t : jigsaw_msh_t
71- An object containing the mesh data. It must have :
72- - ' vert2['coord']' containing the coordinates of the mesh vertices.
73- - ' tria3['index']' containing the indices of the triangles.
74- - ' value' containing the mesh values to be plotted.
75- ax : Axes, optional
76- The axes to plot on. If None, a new plot is created. Default is None .
79+ An object containing the mesh data. Must include :
80+ - vert2['coord']: coordinates of mesh vertices (N, 2)
81+ - tria3['index']: triangle connectivity (M, 3)
82+ - value: values to plot (length M or Mx1)
83+ ax : matplotlib Axes, optional
84+ Axes to draw on. If None, a new one is created.
7785 colorbar : bool, optional
78- Whether to display the colorbar. Default is True.
86+ If True, show colorbar. Default is True.
7987 clim : tuple, optional
80- The limits for the color scale. If None, the limits are automatically
81- determined from the data. Default is None.
82- **kwargs : keyword arguments, optional
83- Additional keyword arguments passed to the `tricontourf` function.
84- These can be used to customize the plot (e.g., color, line style).
88+ Color limits (vmin, vmax). If None, autoscale.
89+ to_geo : callable, optional
90+ Function to transform (x, y) in projected coordinates to (lon, lat),
91+ **kwargs : additional keyword args for tricontourf
8592
8693 Returns
8794 -------
88- ax : Axes
89- The axes object with the plotted mesh values .
95+ ax : matplotlib Axes
96+ The axes with the plot .
9097 """
9198
92- crd = msh_t .vert2 ["coord" ]
99+ # Copy coordinates to avoid modifying original mesh
100+ crd = np .array (msh_t .vert2 ["coord" ], copy = True )
93101 cnn = msh_t .tria3 ["index" ]
94102 val = msh_t .value .flatten ()
95103
104+ # Transform to geographic coordinates if needed
105+ if to_geo is not None :
106+ crd [:, 0 ], crd [:, 1 ] = to_geo (crd [:, 0 ], crd [:, 1 ])
107+
96108 if ax is None :
97- _fig , ax = plt .subplots ()
109+ _ , ax = plt .subplots ()
110+
98111 mappable = ax .tricontourf (crd [:, 0 ], crd [:, 1 ], cnn , val , ** kwargs )
112+
99113 if colorbar :
100114 if clim is not None :
101115 mappable .set_clim (* clim )
102- _cb = plt .colorbar (mappable , ax = ax )
116+ cbar = plt .colorbar (mappable , ax = ax )
117+ cbar .set_label ("Mesh spacing conditioning (m)" )
103118
119+ gl = ax .gridlines (draw_labels = True )
120+ gl .top_labels = False
121+ gl .right_labels = False
104122 return ax
105123
106124
@@ -138,19 +156,18 @@ def plot_bathymetry(rasters_path: List[str], polygon: Polygon, ax: Axes) -> Axes
138156 height , width = data [0 ].shape
139157 cols , rows = np .meshgrid (np .arange (width ), np .arange (height ))
140158 xs , ys = rasterio .transform .xy (transform , rows , cols )
141-
159+
142160 im = ax .imshow (
143- data [0 ], cmap = "terrain " , extent = (np .min (xs ), np .max (xs ), np .min (ys ), np .max (ys ))
161+ data [0 ], cmap = "gist_earth " , extent = (np .min (xs ), np .max (xs ), np .min (ys ), np .max (ys ))
144162 )
145163 cbar = plt .colorbar (im , ax = ax )
146164 cbar .set_label ("Depth (m)" )
147- ax .set_xlabel ("Longitude" )
148- ax .set_ylabel ("Latitude" )
149165 ax .set_title ("Raster" )
150166
151167 ax .plot (x_polygon , y_polygon , color = "red" , linewidth = 1 )
152- ax .axis ("equal" )
153-
168+ gl = ax .gridlines (draw_labels = True )
169+ gl .top_labels = False
170+ gl .right_labels = False
154171 return ax
155172
156173
@@ -271,53 +288,7 @@ def clip_bati_manning(
271288 dest .write (out_image )
272289
273290
274- def plot_poly (largest_polygon : Polygon , final_polygon : Polygon ) -> Axes :
275- """
276- Plots two polygons on a map: the largest polygon and the final polygon.
277-
278- Parameters
279- ----------
280- largest_polygon : Polygon
281- The largest polygon to plot.
282- final_polygon : Polygon
283- The final polygon to plot.
284-
285- Returns
286- -------
287- ax : Axes
288- The axes object with the plotted polygons.
289- """
290-
291- exterior_points = list (largest_polygon .exterior .coords )
292- interior_points = [list (interior .coords ) for interior in largest_polygon .interiors ]
293- all_points = exterior_points + [
294- point for island in interior_points for point in island
295- ]
296-
297- exterior_points_1 = list (final_polygon .exterior .coords )
298- interior_points_1 = [list (interior .coords ) for interior in final_polygon .interiors ]
299- all_points_1 = exterior_points_1 + [
300- point for island in interior_points_1 for point in island
301- ]
302-
303- x1 , y1 = zip (* all_points_1 )
304- x , y = zip (* all_points )
305- xx , yy = largest_polygon .exterior .xy
306-
307- _fig , ax = plt .subplots ()
308- ax .fill (xx , yy , alpha = 0.5 , fc = "lightgrey" , ec = "black" )
309- ax .scatter (x , y , color = "red" , s = 1 , label = "Initial Polygon Points" )
310- ax .scatter (x1 , y1 , color = "black" , s = 1 , label = "Final Polygon Points" )
311- ax .axis ("equal" )
312- ax .set_title ("Polygon Domain" )
313- ax .set_xlabel ("Longitude" )
314- ax .set_ylabel ("Latitude" )
315- ax .legend ()
316-
317- return ax
318-
319-
320- def plot_boundaries (mesh : jigsaw_msh_t , ax : Axes ) -> Axes :
291+ def plot_boundaries (mesh : jigsaw_msh_t , ax : Axes , to_geo = None ) -> Axes :
321292 """
322293 Plots the boundaries of a mesh, including ocean, interior (islands), and land areas.
323294
@@ -327,38 +298,30 @@ def plot_boundaries(mesh: jigsaw_msh_t, ax: Axes) -> Axes:
327298 The mesh object containing the mesh data and boundaries.
328299 ax : Axes
329300 The axes on which to plot the boundaries.
330-
331- Returns
332- -------
333- ax : Axes
334- The axes object with the plotted boundaries.
301+ to_geo : callable, optional
302+ A function to transform coordinates from projected to geographic CRS.
335303 """
336304
337- plot_mesh_edge (mesh .msh_t , ax = ax , lw = 0.2 , color = "c" )
305+ plot_mesh_edge (mesh .msh_t ,to_geo = to_geo , ax = ax , color = "gray" , lw = 0.5 )
338306
339- try :
340- mesh .boundaries .ocean ().plot (ax = ax , color = "b" , label = "Ocean" )
341- except Exception as e :
342- print (f"No Ocean boundaries available. Error: { e } " )
343- try :
344- mesh .boundaries .interior ().plot (ax = ax , color = "g" , label = "Islands" )
345- except Exception as e :
346- print (f"No Islands boundaries available. Error: { e } " )
347- try :
348- mesh .boundaries .land ().plot (ax = ax , color = "r" , label = "Land" )
349- except Exception as e :
350- print (f"No Land boundaries available. Error: { e } " )
307+ def plot_boundary (gdf , color , label ):
308+ try :
309+ if to_geo :
310+ gdf = gdf .copy ()
311+ gdf ["geometry" ] = gdf ["geometry" ].apply (lambda geom : transform (to_geo , geom ))
312+ gdf .plot (ax = ax , color = color , label = label )
313+ except Exception as e :
314+ print (f"No { label } boundaries available. Error: { e } " )
351315
352- ax .legend ()
353- ax .axis ("equal" )
354- ax .set_title ("Mesh Boundaries" )
355- ax .set_xlabel ("X UTM" )
356- ax .set_ylabel ("Y UTM" )
316+ plot_boundary (mesh .boundaries .ocean (), color = "b" , label = "Ocean" )
317+ plot_boundary (mesh .boundaries .interior (), color = "g" , label = "Islands" )
318+ plot_boundary (mesh .boundaries .land (), color = "r" , label = "Land" )
357319
358- return ax
320+ ax .set_title ("Mesh Boundaries" )
321+ ax .legend ()
359322
360323
361- def plot_bathymetry_interp (mesh : jigsaw_msh_t , ax : Axes ) -> Axes :
324+ def plot_bathymetry_interp (mesh : jigsaw_msh_t ,to_geo , ax : Axes ) -> Axes :
362325 """
363326 Plots the interpolated bathymetry data on a mesh.
364327
@@ -368,30 +331,30 @@ def plot_bathymetry_interp(mesh: jigsaw_msh_t, ax: Axes) -> Axes:
368331 The mesh object containing the bathymetry values and mesh structure.
369332 ax : Axes
370333 The axes on which to plot the interpolated bathymetry.
371-
372- Returns
373- -------
374- ax : Axes
375- The axes object with the plotted interpolated bathymetry.
334+ to_geo : callable
335+ A function to transform coordinates from projected to geographic CRS.
376336 """
337+ crd = np .array (mesh .msh_t .vert2 ["coord" ], copy = True )
338+
339+ if to_geo is not None :
340+ crd [:, 0 ], crd [:, 1 ] = to_geo (crd [:, 0 ], crd [:, 1 ])
341+ bnd = [crd [:, 0 ].min (), crd [:, 0 ].max (), crd [:, 1 ].min (), crd [:, 1 ].max ()]
377342
378343 im = ax .tricontourf (
379344 Triangulation (
380- mesh . msh_t . vert2 [ "coord" ][ :, 0 ],
381- mesh . msh_t . vert2 [ "coord" ] [:, 1 ],
345+ crd [ :, 0 ],
346+ crd [:, 1 ],
382347 triangles = mesh .msh_t .tria3 ["index" ],
383348 ),
384349 mesh .msh_t .value .flatten (),
385350 )
386351 ax .set_title ("Interpolated Bathymetry" )
387- ax .axis ( "equal" )
388- ax . set_xlabel ( "X UTM" )
389- ax . set_ylabel ( "Y UTM" )
352+ gl = ax .gridlines ( draw_labels = True )
353+ gl . top_labels = False
354+ gl . right_labels = False
390355 cbar = plt .colorbar (im , ax = ax )
391356 cbar .set_label ("Depth (m)" )
392-
393- return ax
394-
357+ ax .set_extent ([* bnd ], crs = ccrs .PlateCarree ())
395358
396359def simply_polygon (base_shape : Polygon , simpl_UTM : float , project ) -> Polygon :
397360 """
0 commit comments