comparison xarray_mapplot.py @ 0:fea8a53f8099 draft

"planemo upload for repository https://github.com/galaxyecology/tools-ecology/tree/master/tools/data_manipulation/xarray/ commit 57b6d23e3734d883e71081c78e77964d61be82ba"
author ecology
date Sun, 06 Jun 2021 08:50:43 +0000
parents
children 3e73f657a998
comparison
equal deleted inserted replaced
-1:000000000000 0:fea8a53f8099
1 #!/usr/bin/env python3
2 #
3 #
4 # usage: xarray_mapplot.py [-h] [--proj PROJ]
5 # [--cmap CMAP]
6 # [--output OUTPUT]
7 # [--time TIMES]
8 # [--nrow NROW]
9 # [--ncol NCOL]
10 # [--title title]
11 # [--latitude LATITUDE]
12 # [--longitude LONGITUDE]
13 # [--land ALPHA-LAND]
14 # [--ocean ALPHA-OCEAN]
15 # [--coastline ALPHA-COASTLINE]
16 # [--borders ALPHA-BORDERS]
17 # [--xlim "x1,x2"]
18 # [--ylim "y1,y2"]
19 # [--range "valmin,valmax"]
20 # [--threshold VAL]
21 # [--label label-colorbar]
22 # [--shift]
23 # [-v]
24 # input varname
25 #
26 # positional arguments:
27 # input input filename with geographical coordinates (netCDF
28 # format)
29 # varname Specify which variable to plot (case sensitive)
30 #
31 # optional arguments:
32 # -h, --help show this help message and exit
33 # --proj PROJ Specify the projection on which we draw
34 # --cmap CMAP Specify which colormap to use for plotting
35 # --output OUTPUT output filename to store resulting image (png format)
36 # --time TIMES time index from the file for multiple plots ("0 1 2 3")
37 # --title plot or subplot title
38 # --latitude variable name for latitude
39 # --longitude variable name for longitude
40 # --land add land on plot with alpha value [0-1]
41 # --ocean add oceans on plot with alpha value [0-1]
42 # --coastline add coastline with alpha value [0-1]
43 # --borders add country borders with alpha value [0-1]
44 # --xlim limited geographical area longitudes "x1,x2"
45 # --ylim limited geographical area latitudes "y1,y2"
46 # --range "valmin,valmax" for plotting
47 # --threshold do not plot values below threshold
48 # --label set a label for colormap
49 # --shift shift longitudes if specified
50 # -v, --verbose switch on verbose mode
51 #
52
53 import argparse
54 import ast
55 import warnings
56 from pathlib import Path
57
58 import cartopy.crs as ccrs
59 import cartopy.feature as feature
60
61 from cmcrameri import cm
62
63 import matplotlib as mpl
64 mpl.use('Agg')
65 from matplotlib import pyplot # noqa: I202,E402
66
67 import xarray as xr # noqa: E402
68
69
70 class MapPlotXr ():
71 def __init__(self, input, proj, varname, cmap, output, verbose=False,
72 time=[], title="", latitude="latitude",
73 longitude="longitude", land=0, ocean=0,
74 coastline=0, borders=0, xlim=[], ylim=[],
75 threshold="", label="", shift=False,
76 range_values=[]):
77 self.input = input
78 print("PROJ", proj)
79 if proj != "" and proj is not None:
80 self.proj = proj.replace('X', ':')
81 else:
82 self.proj = proj
83 self.varname = varname
84 self.get_cmap(cmap)
85 self.time = time
86 self.latitude = latitude
87 self.longitude = longitude
88 self.land = land
89 self.ocean = ocean
90 self.coastline = coastline
91 self.borders = borders
92 self.xlim = xlim
93 self.ylim = ylim
94 self.range = range_values
95 self.threshold = threshold
96 self.shift = shift
97 self.xylim_supported = False
98 self.colorbar = True
99 self.title = title
100 if output is None:
101 self.output = Path(input).stem + '.png'
102 else:
103 self.output = output
104 self.verbose = verbose
105 self.dset = xr.open_dataset(self.input, use_cftime=True)
106
107 self.label = {}
108 if label != "" and label is not None:
109 self.label['label'] = label
110 if verbose:
111 print("input: ", self.input)
112 print("proj: ", self.proj)
113 print("varname: ", self.varname)
114 print("time: ", self.time)
115 print("minval, maxval: ", self.range)
116 print("title: ", self.title)
117 print("output: ", self.output)
118 print("label: ", self.label)
119 print("shift: ", self.shift)
120 print("ocean: ", self.ocean)
121 print("land: ", self.land)
122 print("coastline: ", self.coastline)
123 print("borders: ", self.borders)
124 print("latitude: ", self.latitude)
125 print("longitude: ", self.longitude)
126 print("xlim: ", self.xlim)
127 print("ylim: ", self.ylim)
128
129 def get_cmap(self, cmap):
130 if cmap[0:3] == 'cm.':
131 self.cmap = cm.__dict__[cmap[3:]]
132 else:
133 self.cmap = cmap
134
135 def projection(self):
136 if self.proj is None:
137 return ccrs.PlateCarree()
138
139 proj_dict = ast.literal_eval(self.proj)
140
141 user_proj = proj_dict.pop("proj")
142 if user_proj == 'PlateCarree':
143 self.xylim_supported = True
144 return ccrs.PlateCarree(**proj_dict)
145 elif user_proj == 'AlbersEqualArea':
146 return ccrs.AlbersEqualArea(**proj_dict)
147 elif user_proj == 'AzimuthalEquidistant':
148 return ccrs.AzimuthalEquidistant(**proj_dict)
149 elif user_proj == 'EquidistantConic':
150 return ccrs.EquidistantConic(**proj_dict)
151 elif user_proj == 'LambertConformal':
152 return ccrs.LambertConformal(**proj_dict)
153 elif user_proj == 'LambertCylindrical':
154 return ccrs.LambertCylindrical(**proj_dict)
155 elif user_proj == 'Mercator':
156 return ccrs.Mercator(**proj_dict)
157 elif user_proj == 'Miller':
158 return ccrs.Miller(**proj_dict)
159 elif user_proj == 'Mollweide':
160 return ccrs.Mollweide(**proj_dict)
161 elif user_proj == 'Orthographic':
162 return ccrs.Orthographic(**proj_dict)
163 elif user_proj == 'Robinson':
164 return ccrs.Robinson(**proj_dict)
165 elif user_proj == 'Sinusoidal':
166 return ccrs.Sinusoidal(**proj_dict)
167 elif user_proj == 'Stereographic':
168 return ccrs.Stereographic(**proj_dict)
169 elif user_proj == 'TransverseMercator':
170 return ccrs.TransverseMercator(**proj_dict)
171 elif user_proj == 'UTM':
172 return ccrs.UTM(**proj_dict)
173 elif user_proj == 'InterruptedGoodeHomolosine':
174 return ccrs.InterruptedGoodeHomolosine(**proj_dict)
175 elif user_proj == 'RotatedPole':
176 return ccrs.RotatedPole(**proj_dict)
177 elif user_proj == 'OSGB':
178 self.xylim_supported = False
179 return ccrs.OSGB(**proj_dict)
180 elif user_proj == 'EuroPP':
181 self.xylim_supported = False
182 return ccrs.EuroPP(**proj_dict)
183 elif user_proj == 'Geostationary':
184 return ccrs.Geostationary(**proj_dict)
185 elif user_proj == 'NearsidePerspective':
186 return ccrs.NearsidePerspective(**proj_dict)
187 elif user_proj == 'EckertI':
188 return ccrs.EckertI(**proj_dict)
189 elif user_proj == 'EckertII':
190 return ccrs.EckertII(**proj_dict)
191 elif user_proj == 'EckertIII':
192 return ccrs.EckertIII(**proj_dict)
193 elif user_proj == 'EckertIV':
194 return ccrs.EckertIV(**proj_dict)
195 elif user_proj == 'EckertV':
196 return ccrs.EckertV(**proj_dict)
197 elif user_proj == 'EckertVI':
198 return ccrs.EckertVI(**proj_dict)
199 elif user_proj == 'EqualEarth':
200 return ccrs.EqualEarth(**proj_dict)
201 elif user_proj == 'Gnomonic':
202 return ccrs.Gnomonic(**proj_dict)
203 elif user_proj == 'LambertAzimuthalEqualArea':
204 return ccrs.LambertAzimuthalEqualArea(**proj_dict)
205 elif user_proj == 'NorthPolarStereo':
206 return ccrs.NorthPolarStereo(**proj_dict)
207 elif user_proj == 'OSNI':
208 return ccrs.OSNI(**proj_dict)
209 elif user_proj == 'SouthPolarStereo':
210 return ccrs.SouthPolarStereo(**proj_dict)
211
212 def plot(self, ts=None):
213 if self.shift:
214 if self.longitude == 'longitude':
215 self.dset = self.dset.assign_coords(
216 longitude=(((
217 self.dset[self.longitude]
218 + 180) % 360) - 180))
219 elif self.longitude == 'lon':
220 self.dset = self.dset.assign_coords(
221 lon=(((self.dset[self.longitude]
222 + 180) % 360) - 180))
223
224 pyplot.figure(1, figsize=[20, 10])
225
226 # Set the projection to use for plotting
227 ax = pyplot.subplot(1, 1, 1, projection=self.projection())
228 if self.land:
229 ax.add_feature(feature.LAND, alpha=self.land)
230
231 if self.ocean:
232 ax.add_feature(feature.OCEAN, alpha=self.ocean)
233 if self.coastline:
234 ax.coastlines(resolution='10m', alpha=self.coastline)
235 if self.borders:
236 ax.add_feature(feature.BORDERS, linestyle=':', alpha=self.borders)
237
238 if self.xlim:
239 min_lon = min(self.xlim[0], self.xlim[1])
240 max_lon = max(self.xlim[0], self.xlim[1])
241 else:
242 min_lon = self.dset[self.longitude].min()
243 max_lon = self.dset[self.longitude].max()
244
245 if self.ylim:
246 min_lat = min(self.ylim[0], self.ylim[1])
247 max_lat = max(self.ylim[0], self.ylim[1])
248 else:
249 min_lat = self.dset[self.latitude].min()
250 max_lat = self.dset[self.latitude].max()
251
252 if self.xylim_supported:
253 pyplot.xlim(min_lon, max_lon)
254 pyplot.ylim(min_lat, max_lat)
255
256 # Fix extent
257 if self.threshold == "" or self.threshold is None:
258 threshold = self.dset[self.varname].min()
259 else:
260 threshold = float(self.threshold)
261
262 if self.range == []:
263 minval = self.dset[self.varname].min()
264 maxval = self.dset[self.varname].max()
265 else:
266 minval = self.range[0]
267 maxval = self.range[1]
268
269 if self.verbose:
270 print("minval: ", minval)
271 print("maxval: ", maxval)
272
273 # pass extent with vmin and vmax parameters
274 proj_t = ccrs.PlateCarree()
275 if ts is None:
276 self.dset.where(
277 self.dset[self.varname] > threshold
278 )[self.varname].plot(ax=ax,
279 vmin=minval,
280 vmax=maxval,
281 transform=proj_t,
282 cmap=self.cmap,
283 cbar_kwargs=self.label
284 )
285 if self.title != "" and self.title is not None:
286 pyplot.title(self.title)
287 pyplot.savefig(self.output)
288 else:
289 if self.colorbar:
290 self.dset.where(
291 self.dset[self.varname] > threshold
292 )[self.varname].isel(time=ts).plot(ax=ax,
293 vmin=minval,
294 vmax=maxval,
295 transform=proj_t,
296 cmap=self.cmap,
297 cbar_kwargs=self.label
298 )
299 else:
300 self.dset.where(
301 self.dset[self.varname] > minval
302 )[self.varname].isel(time=ts).plot(ax=ax,
303 vmin=minval,
304 vmax=maxval,
305 transform=proj_t,
306 cmap=self.cmap,
307 add_colorbar=False)
308 if self.title != "" and self.title is not None:
309 pyplot.title(self.title + "(time = " + str(ts) + ')')
310 pyplot.savefig(self.output[:-4] + "_time" + str(ts) +
311 self.output[-4:]) # assume png format
312
313
314 if __name__ == '__main__':
315 warnings.filterwarnings("ignore")
316 parser = argparse.ArgumentParser()
317 parser.add_argument(
318 'input',
319 help='input filename with geographical coordinates (netCDF format)'
320 )
321
322 parser.add_argument(
323 '--proj',
324 help='Specify the projection on which we draw'
325 )
326 parser.add_argument(
327 'varname',
328 help='Specify which variable to plot (case sensitive)'
329 )
330 parser.add_argument(
331 '--cmap',
332 help='Specify which colormap to use for plotting'
333 )
334 parser.add_argument(
335 '--output',
336 help='output filename to store resulting image (png format)'
337 )
338 parser.add_argument(
339 '--time',
340 help='list of times to plot for multiple plots'
341 )
342 parser.add_argument(
343 '--title',
344 help='plot title'
345 )
346 parser.add_argument(
347 '--latitude',
348 help='variable name for latitude'
349 )
350 parser.add_argument(
351 '--longitude',
352 help='variable name for longitude'
353 )
354 parser.add_argument(
355 '--land',
356 help='add land on plot with alpha value [0-1]'
357 )
358 parser.add_argument(
359 '--ocean',
360 help='add oceans on plot with alpha value [0-1]'
361 )
362 parser.add_argument(
363 '--coastline',
364 help='add coastline with alpha value [0-1]'
365 )
366 parser.add_argument(
367 '--borders',
368 help='add country borders with alpha value [0-1]'
369 )
370 parser.add_argument(
371 '--xlim',
372 help='limited geographical area longitudes "x1,x2"'
373 )
374 parser.add_argument(
375 '--ylim',
376 help='limited geographical area latitudes "y1,y2"'
377 )
378 parser.add_argument(
379 '--range',
380 help='min and max values for plotting "minval,maxval"'
381 )
382 parser.add_argument(
383 '--threshold',
384 help='do not plot values below threshold'
385 )
386 parser.add_argument(
387 '--label',
388 help='set a label for colorbar'
389 )
390 parser.add_argument(
391 '--shift',
392 help='shift longitudes if specified',
393 action="store_true"
394 )
395 parser.add_argument(
396 "-v", "--verbose",
397 help="switch on verbose mode",
398 action="store_true")
399 args = parser.parse_args()
400
401 if args.time is None:
402 time = []
403 else:
404 time = list(map(int, args.time.split(",")))
405 if args.xlim is None:
406 xlim = []
407 else:
408 xlim = list(map(float, args.xlim.split(",")))
409 if args.ylim is None:
410 ylim = []
411 else:
412 ylim = list(map(float, args.ylim.split(",")))
413 if args.range is None:
414 range_values = []
415 else:
416 range_values = list(map(float, args.range.split(",")))
417 if args.latitude is None:
418 latitude = "latitude"
419 else:
420 latitude = args.latitude
421 if args.longitude is None:
422 longitude = "longitude"
423 else:
424 longitude = args.longitude
425 if args.land is None:
426 land = 0
427 else:
428 land = float(args.land)
429 if args.ocean is None:
430 ocean = 0
431 else:
432 ocean = float(args.ocean)
433 if args.coastline is None:
434 coastline = 0
435 else:
436 coastline = float(args.coastline)
437 if args.borders is None:
438 borders = 0
439 else:
440 borders = float(args.borders)
441
442 dset = MapPlotXr(input=args.input, proj=args.proj, varname=args.varname,
443 cmap=args.cmap, output=args.output, verbose=args.verbose,
444 time=time, title=args.title,
445 latitude=latitude, longitude=longitude, land=land,
446 ocean=ocean, coastline=coastline, borders=borders,
447 xlim=xlim, ylim=ylim, threshold=args.threshold,
448 label=args.label, shift=args.shift,
449 range_values=range_values)
450
451 if dset.time == []:
452 dset.plot()
453 else:
454 for t in dset.time:
455 dset.plot(t)
456 dset.shift = False # only shift once
457 dset.colorbar = True