diff --git a/LICENSE b/LICENSE index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d75091f8e781453b4ab5c8875336bf6858858e8b 100644 --- a/LICENSE +++ b/LICENSE @@ -0,0 +1,192 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2021 Barcelona Supercomputing Center + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + diff --git a/bin/map_main.py b/bin/map_main.py index a4c204ae6ea42ed0bffcf91b0c2433128606f5c9..477d92f162438b9555927bd766f63ffc9d6e7186 100755 --- a/bin/map_main.py +++ b/bin/map_main.py @@ -1,22 +1,3 @@ -#!/usr/bin/env python - -# Copyright 2016 Earth Sciences Department, BSC-CNS - -# This file is part of MapGenerator. - -# MapGenerator is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# MapGenerator is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with MapGenerator. If not, see . - """ Main module for MapGenerator. Only contains an interface class to all functionality implemented on MapGenerator. """ diff --git a/mapgenerator/mg_exceptions.py b/mapgenerator/mg_exceptions.py index 1d51c94fe55bcd9d478a9fa6f6ebdf3d9aeb7210..b8e518ab246ef42e5eb37d47152421a89bac0333 100644 --- a/mapgenerator/mg_exceptions.py +++ b/mapgenerator/mg_exceptions.py @@ -1,21 +1,4 @@ -#!/usr/bin/env python -# Copyright 2016 Earth Sciences Department, BSC-CNS - -# This file is part of MapGenerator. - -# MapGenerator is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# MapGenerator is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with MapGenerator. If not, see . class ArgumentParserException(Exception): diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 8607b5b74b4f9c43520ece80fd6b8f4293f38a9f..97783d0b7834b152f96fc373863efa58732763f1 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -16,6 +16,8 @@ from datetime import timedelta import os import os.path import copy +from cartopy.img_transform import mesh_projection, regrid +from cartopy.crs import PlateCarree import logging @@ -58,37 +60,30 @@ def do_interpolation(data, lon, lat): """ Takes a not regular grid (curvilinear, whatever kind) and interpolate to regular one derived from the first. Very demanding operation, try to avoid """ - # lon as second dimension - # lat as first dimension LOG.info("Irregular grid, performing interpolation ...") - lat_s = lat.shape[0] - lon_s = lon.shape[1] - reglon = np.linspace(lon.min(), lon.max(), lon_s) - reglat = np.linspace(lat.min(), lat.max(), lat_s) - LOG.info("LAT: %s, LON: %s, DATA: %s", reglon.shape, reglat.shape, - data.shape) - regx, regy = np.meshgrid(reglon, reglat) - result = np.empty(data.shape)*np.nan - if ma.is_masked(data): - data = data.filled(np.nan) - + projection = PlateCarree() + new_lon, new_lat, _ = mesh_projection( + projection, data.shape[-1], data.shape[-2], + # x_extents=(lon.min(), lon.max()), + # y_extents=(lat.min(), lat.max()) + ) + + def _interpolate(to_regrid): + return regrid(to_regrid, lon, lat, projection, projection, new_lon, new_lat) + + result = np.empty_like(data) + result.fill(np.nan) # assuming that lat and lon are the latest dimensions if len(data.shape) == 2: - result = griddata((lon.ravel(), lat.ravel()), data.ravel(), (regx, - regy)) + result = _interpolate(data) elif len(data.shape) == 3: for i in range(data.shape[0]): - tmp = griddata((lon.ravel(), lat.ravel()), data[i].ravel(), (regx, - regy)) - result[i] = tmp.reshape(result[i].shape) + result[i, :, :] = _interpolate(data[i]) elif len(data.shape) == 4: for i in range(data.shape[0]): for j in range(data.shape[1]): - tmp = griddata((lon.ravel(), lat.ravel()), data[i, j].ravel(), - (regx, regy)) - result[i, j, :, :] = tmp.reshape(result[i, j, :, :].shape) - # elif len(data.shape) == 5: - return np.ma.masked_where(np.isnan(result), result), regx, regy + result[i, j, :, :] = _interpolate(data[i, j]) + return result, new_lon[0], new_lat[:, 0] def extract_coords(self, var, coord='lons'): @@ -127,7 +122,7 @@ def parse_parameters_list(plist): if len(plist) == 1: plist = plist[0] else: - return list(plist) + return [float(val) for val in plist] if str(plist).find('-') > 0: tmp = plist.strip('[]') try: @@ -715,6 +710,7 @@ class MapGenerator(object): self.orig_projection_kwargs = kwargs.get('orig_projection_kwargs', dict()) self.projection = kwargs.get('projection', 'PlateCarree') self.projection_kwargs = kwargs.get('projection_kwargs', dict()) + self.extent = kwargs.get('extent', 'auto') self.subsetting = kwargs.get('subsetting', True) self.dimension = kwargs.get('dimension', None) self.continents = kwargs.get('continents', None) @@ -740,9 +736,9 @@ class MapGenerator(object): if cube.attributes.get('plot_name', None): show_name = cube.attributes['plot_name'] elif cube.long_name and len(cube.long_name) < 45: - show_name = cube.long_name + show_name = f"{cube.long_name} ({cube.var_name})" elif cube.standard_name and len(cube.standard_name) < 45: - show_name = cube.standard_name + show_name = f"{cube.standard_name} ({cube.var_name})" else: show_name = cube.var_name return f"{show_name} ({cube.units})" @@ -770,6 +766,7 @@ class MapCross(MapGenerator): self.contours_int = kwargs.get('contours_int', 1) self.smooth = kwargs.get('smooth', False) self.colorbar = kwargs.get('colorbar', True) + self.colorbar_location = kwargs.get('colorbar_location', 'right') self.formats = kwargs.get('formats', None) self.noruntime = kwargs.get('noruntime', False) self.timesteps = kwargs.get('timesteps', '0') diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 5dc08363a82506662d32a455c3341eb4e48c3753..037df997aa2d772a5c2bd8e8cd0cfba9fadd407c 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -6,12 +6,14 @@ import matplotlib as mpl import matplotlib.pyplot as plt +import matplotlib.ticker as mticker import cartopy.crs as ccrs import cartopy.feature as cfeature import cartopy.io.shapereader as shpreader + +from cartopy.util import add_cyclic_point from matplotlib.cbook import delete_masked_points from matplotlib.path import Path -# from matplotlib.image import imread import numpy as np from datetime import datetime import os.path @@ -33,10 +35,6 @@ from .tools import set_title from .tools import set_resolution from .tools import gen_kml from .tools import gen_anim -# try: -# from urllib2 import urlopen -# except ImportError: -# from urllib.request import urlopen import copy from glob import glob import logging @@ -47,14 +45,12 @@ LOG = logging.getLogger(__name__) class PlotCross(object): """ Main class for plotting cross sections """ - def __init__(self, loglevel='WARNING', **kwargs): pass class PlotMap(MapCross, MapDrawOptions): """ Main class for plotting maps """ - def __init__(self, loglevel='WARNING', **kwargs): """ Initialize class with attributes """ MapCross.__init__(self, loglevel, **kwargs) @@ -75,22 +71,17 @@ class PlotMap(MapCross, MapDrawOptions): self.mgplot = None self.stamen_terrain = None self.zoom_level = None - # self.map = None #Map self.first_image = False # Is is the first image of the batch? self.map_name = None # name of the figure to map animation colors -# self.drawopts = {'coastlines': -# {'linewidth': 0.5, 'color': 'grey', }, -# 'countries': -# {'linewidth': 0.3, 'color': 'grey', }, -# } + self.draw_labels = None def __setattr__(self, key, value): LOG.debug("SETATTR: %s - %s", key, value) super(PlotMap, self).__setattr__(key, parse_parameter(key, value)) def _build_orig_projection(self): - self._orig_crs = getattr(ccrs, self.orig_projection)( - **self.orig_projection_kwargs) + self._orig_crs = getattr( + ccrs, self.orig_projection)(**self.orig_projection_kwargs) def _build_projection(self): if self.background: @@ -98,36 +89,56 @@ class PlotMap(MapCross, MapDrawOptions): self.stamen_terrain = cimgt.Stamen('terrain-background') self._crs = self.stamen_terrain.crs self.projection = 'Stamen' + elif self.projection.lower().startswith('epsg'): + self._crs = ccrs.epsg(self.projection[4:]) else: - self._crs = getattr(ccrs, self.projection)( - **self.projection_kwargs) + self._crs = getattr(ccrs, + self.projection)(**self.projection_kwargs) + + def set_resolution(self): + """ 110m, 50m, 10m """ + minlat, maxlat = self.lat[0], self.lat[-1] + minlon, maxlon = self.lon[0], self.lon[-1] + lats = maxlat + abs(minlat) + lons = maxlon + abs(minlon) + if lats > 100 and lons > 200: + self.resolution = '110m' + elif lats > 45 and lons > 75: + self.resolution = '50m' + elif lats <= 45 and lons <= 75: + self.resolution = '10m' + else: + self._crs = getattr(ccrs, + self.projection)(**self.projection_kwargs) def set_color_map(self): """ Create color map """ extend_opts = { 'max': { - 'cmap_arg': lambda colors: colors[:-1], + 'cmap_arg': + lambda colors: colors[:-1], 'cmap_ext': ('set_over'), 'ext_arg': (lambda colors: colors[-1]), - 'norm_arg': (lambda bounds: bounds+[np.inf], - lambda cmap: cmap.N+1) + 'norm_arg': + (lambda bounds: bounds + [np.inf], lambda cmap: cmap.N + 1) }, 'min': { - 'cmap_arg': lambda colors: colors[1:], + 'cmap_arg': + lambda colors: colors[1:], 'cmap_ext': ('set_under'), 'ext_arg': (lambda colors: colors[0]), - 'norm_arg': (lambda bounds: [-np.inf]+bounds, - lambda cmap: cmap.N+1) + 'norm_arg': + (lambda bounds: [-np.inf] + bounds, lambda cmap: cmap.N + 1) }, 'both': { - 'cmap_arg': self.colors[1:-1], + 'cmap_arg': + self.colors[1:-1], 'cmap_ext': ('set_over', 'set_under'), - 'ext_arg': (lambda colors: colors[-1], - lambda colors: colors[0]), - 'norm_arg': (lambda bounds: [-np.inf]+bounds+[np.inf], + 'ext_arg': + (lambda colors: colors[-1], lambda colors: colors[0]), + 'norm_arg': (lambda bounds: [-np.inf] + bounds + [np.inf], lambda cmap: cmap.N + 2) - } } @@ -139,8 +150,8 @@ class PlotMap(MapCross, MapDrawOptions): if len(self.bounds) < len(self.colors): self.colors = self.colors[:len(self.bounds)] elif self.extend == 'both': - if len(self.bounds)+1 < len(self.colors): - self.colors = self.colors[:len(self.bounds)+1] + if len(self.bounds) + 1 < len(self.colors): + self.colors = self.colors[:len(self.bounds) + 1] if self.extend in extend_opts: self.cmap = mpl.colors.ListedColormap( @@ -168,16 +179,16 @@ class PlotMap(MapCross, MapDrawOptions): if self.extend in ('min', 'both'): self.cmap.set_under(color=(0, 0, 0, 0)) else: - self.cmap.colors = [(0, 0, 0, 0), ] + [c for c in - self.cmap.colors[1:]] + self.cmap.colors = [ + (0, 0, 0, 0), + ] + [c for c in self.cmap.colors[1:]] # normalize colormap if self.bounds and self.smooth and not custom_cmap: if self.extend in ('min', 'max', 'both'): self.norm = mpl.colors.BoundaryNorm( extend_opts[self.extend]['norm_arg'][0](self.bounds), - extend_opts[self.extend]['norm_arg'][1](self.cmap) - ) + extend_opts[self.extend]['norm_arg'][1](self.cmap)) else: self.norm = mpl.colors.BoundaryNorm(self.bounds, self.cmap.N) elif self.bounds: @@ -185,10 +196,6 @@ class PlotMap(MapCross, MapDrawOptions): def set_color_bar(self, mco, location='right', drawedges=False, cax=None): """ Create color bar """ -# xs = self.xsize -# nap = .8-((1-xs)*0.4) -# a = plt.axes([nap, 0, .14, 1])#, frameon=False) -# plt.axis('off') mpl.rcParams['axes.linewidth'] = 0.1 mpl.rcParams['axes.formatter.useoffset'] = False if self.ticks: @@ -200,13 +207,21 @@ class PlotMap(MapCross, MapDrawOptions): self.ticks = mco.levels LOG.debug("***** Formats: %s *****", self.formats) - cax, kwargs = mpl.colorbar.make_axes(self.mgaxis, location=location, - pad=0.02, shrink=0.8) - cbar = self.mgplot.colorbar(mco, cax=cax, ax=self.mgaxis, - ticks=self.ticks, format=self.formats, - pad=.06, extend=self.extend, - drawedges=drawedges, **kwargs) - + pad = {'bottom': 0.05, 'right': 0.02} + pad = pad.get(location, 0.02) + cax, kwargs = mpl.colorbar.make_axes(self.mgaxis, + location=location, + pad=pad, + shrink=0.9) + cbar = self.mgplot.colorbar(mco, + cax=cax, + ax=self.mgaxis, + ticks=self.ticks, + format=self.formats, + pad=.06, + extend=self.extend, + drawedges=drawedges, + **kwargs) cbar.ax.tick_params(labelsize=float(self.coordsopts[1])) for lin in cbar.ax.yaxis.get_ticklines(): lin.set_visible(False) @@ -225,22 +240,27 @@ class PlotMap(MapCross, MapDrawOptions): else: ticks = mco.levels - fac = span/float(len(ticks) - 1) + fac = span / float(len(ticks) - 1) for i in arrows: for j in range(0, len(ticks) - 1): - if i >= ticks[j] and i <= ticks[j+1]: - if i == ticks[j+1]: - idx = j+1 + if i >= ticks[j] and i <= ticks[j + 1]: + if i == ticks[j + 1]: + idx = j + 1 add = 0 else: idx = j - add = (float(i - ticks[j])/float(ticks[j+1] - - ticks[j]))*fac - pos = fac*idx + lower - plt.arrow(0.88, pos + add, 0.10, 0, - length_includes_head=True, head_length=0.10, - head_width=0.025, fill=True, color='k') - # plt.colorbar(drawedges=drawedges, cax=cax, ticks=self.bounds) + add = (float(i - ticks[j]) / + float(ticks[j + 1] - ticks[j])) * fac + pos = fac * idx + lower + plt.arrow(0.88, + pos + add, + 0.10, + 0, + length_includes_head=True, + head_length=0.10, + head_width=0.025, + fill=True, + color='k') def init_map(self, grid): """ Initialize a map. Initialization should be performed only once @@ -248,25 +268,16 @@ class PlotMap(MapCross, MapDrawOptions): glon, glat = grid # print("***",self.lon, self.lat,"***") if not self.lat: # or len(self.lat) not in (2,3): - self.lat = "%s-%s" % (str(round(glat.min(), 1)).replace('-', 'm'), - str(round(glat.max(), 1)).replace('-', 'm')) + self.lat = "%s-%s" % (str(round(glat.min(), 1)).replace( + '-', 'm'), str(round(glat.max(), 1)).replace('-', 'm')) if not self.lon: # or len(self.lon) not in (2,3): - self.lon = "%s-%s" % (str(round(glon.min(), 1)).replace('-', 'm'), - str(round(glon.max(), 1)).replace('-', 'm')) + self.lon = "%s-%s" % (str(round(glon.min(), 1)).replace( + '-', 'm'), str(round(glon.max(), 1)).replace('-', 'm')) self._build_orig_projection() self._build_projection() - # print(self.lon, self.lat) -# # Fix the printout of tick values to avoid .0 decimals in integers -# strs = [] -# if self.bounds: -# for b in self.bounds: -# if (b == int(b)): -# strs.append("%d" % b) -# else: -# strs.append(str(b)) -# + # # Set the colormap self.set_color_map() if not self.resolution: @@ -277,8 +288,13 @@ class PlotMap(MapCross, MapDrawOptions): self.first_image = True - def gen_image_map(self, fig_name, grid, data, img_title, - cur_scatter_data=None, **kwargs): + def gen_image_map(self, + fig_name, + grid, + data, + img_title, + cur_scatter_data=None, + **kwargs): """ Generate image map """ # overwrite option @@ -291,59 +307,19 @@ class PlotMap(MapCross, MapDrawOptions): map_data = data.map_data # FIXME scatter_data = data.scatter_data or cur_scatter_data - # if self.subplot is None: - # self.mgplot.clear() - -# params = { -# 'font.size': 14, -# 'text.fontsize': 28, -# 'axes.titlesize': 11, -# 'savefig.dpi': self.dpi -# } -# -# mpl.rcParams.update(params) # Draw filled contour - self.mgaxis.set_aspect(self.xsize/self.ysize) - + self.mgaxis.set_aspect(self.xsize / self.ysize) + self._set_extent() if self.projection.lower().endswith('polarstereo'): - self.mgaxis.set_extent([-180, 179.9999999999999, - self.lat[0], self.lat[-1]], - self._orig_crs) # Compute a circle in axes coordinates, which we can use as a # boundary for the map. We can pan/zoom as much as we like - the # boundary will be permanently circular. - theta = np.linspace(0, 2*np.pi, 100) + theta = np.linspace(0, 2 * np.pi, 100) center, radius = [0.5, 0.5], 0.5 verts = np.vstack([np.sin(theta), np.cos(theta)]).T circle = Path(verts * radius + center) self.mgaxis.set_boundary(circle, transform=self.mgaxis.transAxes) - else: - self.mgaxis.set_extent([self.lon[0], self.lon[-1], - self.lat[0], self.lat[-1]], - self._orig_crs) - -# if self.subplot: -# self.map = Basemap( -# ax=self.mgaxis, -# projection=self.projection, resolution=self.resolution, -# llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], -# urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], -# lon_0=lon_0, lat_0=lat_0, -# fix_aspect=self.keep_aspect, -# area_thresh=self.area_thresh, -# boundinglat=self.boundinglat, -# ) -# else: -# self.map = Basemap( -# projection=self.projection, resolution=self.resolution, -# llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], -# urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], -# lon_0=lon_0, lat_0=lat_0, -# fix_aspect=self.keep_aspect, -# area_thresh=self.area_thresh, -# boundinglat=self.boundinglat, -# ) glon, glat = grid LOG.debug("0. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) @@ -354,74 +330,36 @@ class PlotMap(MapCross, MapDrawOptions): LOG.debug("1. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) # if curvilinear grid do interpolation, return already gridded coords - if not is_grid_regular(glon, glat): + if is_grid_regular(glon, glat): + if len(glon.shape)==2: + glon = glon[0] + glat = glat[:, 0] + else: map_data[0], glon, glat = do_interpolation(map_data[0], glon, glat) - LOG.info("2. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) + try: + map_data[0], glon = add_cyclic_point(map_data[0], coord=glon) + except ValueError: + LOG.debug('Can not add cyclic point.') + pass -# if not self.nomap: -# x, y = self.map(*(glon, glat)) -# else: + LOG.info("2. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) xloc, yloc = glon, glat LOG.info("3. GLON: %s, GLAT: %s", str(xloc.shape), str(yloc.shape)) -# print "map_data", map_data[0].shape - - # self.print_time("meshgrid") - # LOG.info("X: %s, Y: %s" % (str(x.shape), str(y.shape))) - -# # nomap option -# if self.nomap and os.path.exists("%s-%s/%s.png" % (run_date, var_name, -# fig_name)) and not self.overwrite: -# print fig_name, " already exists." -# plt.clf() -# return fig_name if self.nomap: self.mgplot.frameon = False -# if self.alpha is not None and self.bounds is not None: -# if self.extend in ('min', 'both'): -# map_data[0] = np.ma.masked_where(map_data[0] < -# self.bounds[0], map_data[0]) -# else: -# map_data[0] = np.ma.masked_where((map_data[0] >= -# self.bounds[0]) & -# (map_data[0] < -# self.bounds[1]), -# map_data[0]) - mco = None LOG.info(type(map_data[0])) map_data[0] = np.ma.filled(map_data[0], np.nan) LOG.info(type(map_data[0])) -# if not self.nocontourf and not self.nomap and self.smooth: -# LOG.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, -# map_data[0])) -# mco = self.map.contourf(x, y, -# map_data[0], -# cmap=self.cmap, -# norm=self.norm, -# levels=self.bounds, -# extend=self.extend, -# horizontalalignment='center', -# alpha=self.alpha, -# antialiased=True) -# -# elif not self.nocontourf and not self.nomap and not self.smooth: -# LOG.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, -# map_data[0])) -# mco = self.map.pcolormesh(x, y, -# map_data[0], -# cmap=self.cmap, -# norm=self.norm, -# alpha=self.alpha, -# antialiased=True) - if not self.nocontourf and self.smooth: - mco = self.mgaxis.contourf(xloc, yloc, + mco = self.mgaxis.contourf(xloc, + yloc, map_data[0], cmap=self.cmap, norm=self.norm, @@ -430,9 +368,12 @@ class PlotMap(MapCross, MapDrawOptions): alpha=self.alpha, transform=self._orig_crs, antialiased=True) + if self.bad: + self.mgaxis.set_facecolor(self.bad) elif not self.nocontourf and not self.smooth: - mco = self.mgaxis.pcolormesh(xloc, yloc, + mco = self.mgaxis.pcolormesh(xloc, + yloc, map_data[0], cmap=self.cmap, norm=self.norm, @@ -447,20 +388,18 @@ class PlotMap(MapCross, MapDrawOptions): LOG.info("Processing shape file: %s with line width: %s", shapef, line_w) adm1_shapes = list(shpreader.Reader(shapef).geometries()) - self.mgaxis.add_geometries(adm1_shapes, self._crs, + self.mgaxis.add_geometries(adm1_shapes, + self._crs, linewidth=line_w, edgecolor=self.countropts[1]) - # self.map.readshapefile(shapef, "%s" % - # os.path.basename(shapef), linewidth=line_w, - # color=self.countropts[1]) - line_w = max(self.shapef_width_step, line_w - - self.shapef_width_step) + line_w = max(self.shapef_width_step, + line_w - self.shapef_width_step) # FIXME Modify to use scatter inside DATA # if DATA.hasScatterData()... etc... if scatter_data is not None: - LOG.info("Plotting scatter data: %s of keys %s", - str(scatter_data), str(scatter_data.keys())) + LOG.info("Plotting scatter data: %s of keys %s", str(scatter_data), + str(scatter_data.keys())) if mco and self.bounds: self.mgaxis.scatter(scatter_data['lon'].tolist(), scatter_data['lat'].tolist(), @@ -513,14 +452,14 @@ class PlotMap(MapCross, MapDrawOptions): if data.wind_data: winds = data.wind_data x_vec, y_vec, u_vec, v_vec = delete_masked_points( - xloc.ravel(), - yloc.ravel(), - winds['u'].ravel(), - winds['v'].ravel() - ) + xloc.ravel(), yloc.ravel(), winds['u'].ravel(), + winds['v'].ravel()) if 'barbs' in winds: - self.mgaxis.barbs(x_vec, y_vec, u_vec, v_vec, + self.mgaxis.barbs(x_vec, + y_vec, + u_vec, + v_vec, units=self.wind_units, headlength=self.wind_head_length, headwidth=self.wind_head_width, @@ -530,7 +469,10 @@ class PlotMap(MapCross, MapDrawOptions): transform=self._orig_crs, color='k') else: - quiv = self.mgaxis.quiver(x_vec, y_vec, u_vec, v_vec, + quiv = self.mgaxis.quiver(x_vec, + y_vec, + u_vec, + v_vec, units=self.wind_units, headlength=self.wind_head_length, headwidth=self.wind_head_width, @@ -546,7 +488,8 @@ class PlotMap(MapCross, MapDrawOptions): self.wind_label_scale, label='%s m/s' % self.wind_label_scale, coordinates='axes', - labelpos='S', labelsep=0.05) + labelpos='S', + labelsep=0.05) if data.contour_data: interval = self.contours_int @@ -555,13 +498,11 @@ class PlotMap(MapCross, MapDrawOptions): cupp_bound = 99999 cdata = data.contour_data try: - # cmin = min(filter (lambda a: a > clow_bound, cdata.ravel())) cmin = cdata.where(cdata > clow_bound).min() adjcmin = int(cmin - (cmin % interval) - interval * 2) except ValueError: cmin = 0 try: - # cmax = max(filter (lambda a: a < cupp_bound, cdata.ravel())) cmax = cdata.where(cdata < cupp_bound).max() adjcmax = int(cmax - (cmax % interval) + interval * 2) except ValueError: @@ -570,7 +511,8 @@ class PlotMap(MapCross, MapDrawOptions): for exc in exclude: lvls = [exc for exc in lvls if exc != 0] if (len(lvls) > 0) and map_data is not None: - mco = plt.contourf(xloc, yloc, + mco = plt.contourf(xloc, + yloc, map_data[0], cmap=self.cmap, norm=self.norm, @@ -581,7 +523,8 @@ class PlotMap(MapCross, MapDrawOptions): if map_data is not None and (map_data[0] == cdata).all(): LOG.debug(":::::::::::::::::::::: SAME !!! ::::::::::::::::") - mcs = plt.contour(xloc, yloc, + mcs = plt.contour(xloc, + yloc, cdata, levels=self.bounds, colors=self.contours_color, @@ -590,7 +533,8 @@ class PlotMap(MapCross, MapDrawOptions): else: LOG.debug(":::::::::::::::::::: DIFFERENT !!! :::::::::::::") LOG.debug("MIN: %s - MAX: %s", cmin, cmax) - mcs = plt.contour(xloc, yloc, + mcs = plt.contour(xloc, + yloc, cdata, levels=lvls, colors=self.contours_color, @@ -598,20 +542,17 @@ class PlotMap(MapCross, MapDrawOptions): alpha=self.alpha) if self.contours_label: - self.mgplot.clabel(mcs, - inline=1, - fontsize=self.contours_label_fontsize, - # backgroundcolor='r', - fmt=self.contours_label_format) - - # coords normalization -# lat_offset = abs(self.lat[0]) % self.lat[2] -# lon_offset = abs(self.lon[0]) % self.lon[2] + self.mgplot.clabel( + mcs, + inline=1, + fontsize=self.contours_label_fontsize, + # backgroundcolor='r', + fmt=self.contours_label_format) if not self.nomap and not self.kml and not self.kmz: - if self.continents: - self.mgaxis.add_feature(cfeature.LAND, color=self.continents, + self.mgaxis.add_feature(cfeature.LAND, + color=self.continents, zorder=10) self.mgaxis.coastlines(resolution=self.resolution, @@ -619,66 +560,46 @@ class PlotMap(MapCross, MapDrawOptions): color=str(self.coastsopts[1]), zorder=15) - self.mgaxis.add_feature(cfeature.BORDERS - .with_scale(self.resolution), + self.mgaxis.add_feature(cfeature.BORDERS.with_scale( + self.resolution), linewidth=float(self.countropts[0]), edgecolor=str(self.countropts[1]), zorder=15) - draw_labels = bool(self.projection == 'PlateCarree') - grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, - crs=self._crs, - draw_labels=draw_labels, - linestyle='--', - linewidth=float(self.coordsopts[0]), - color=str(self.coordsopts[2]), - zorder=20) + if self.draw_labels is None: + self.draw_labels = bool(self.projection == 'PlateCarree') + - if draw_labels: + grl = self.mgaxis.gridlines( + xlocs=self.lon, + ylocs=self.lat, + draw_labels=self.draw_labels, + linestyle='--', + linewidth=float(self.coordsopts[0]), + color=str(self.coordsopts[2]), + zorder=20, + auto_inline=True, + ) + + if self.draw_labels and bool(self.projection == 'PlateCarree'): grl.xlabels_top = False grl.ylabels_right = False + if self.draw_labels: grl.xlabel_style = {'size': float(self.coordsopts[1])} grl.ylabel_style = grl.xlabel_style # Change axes for colorbar if self.colorbar and not self.kml and not self.kmz and \ (not self.nocontourf or scatter_data is not None): - self.set_color_bar(mco) + self.set_color_bar(mco, self.colorbar_location) if self.background: self.mgaxis.add_image(self.stamen_terrain, self.zoom_level) -# if self.background == 'shadedrelief': -# #m.bluemarble() -# if self.background == 'shadedrelief': -# self.map.shadedrelief() -# if self.background == 'bluemarble': -# self.map.bluemarble() -# if self.background == 'etopo': -# self.map.etopo() -# if self.background == 'GIS': -# h0 = float(self.lat[-1] - self.lat[0]) -# w0 = float(self.lon[-1] - self.lon[0]) -# ff = h0/w0 # form factor -# height = 1024*ff # height -# size = "%d,%d" % (1024, height) -# basemap_url = -# "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/export?\ -# bbox=%s,%s,%s,%s&\ -# bboxSR=4326&\ -# imageSR=4326&\ -# size=%s&\ -# dpi=%d&\ -# format=png24&\ -# f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, -# self.dpi) self.map.imshow(imread(urlopen(basemap_url)), origin='upper') # logo if self.logo: img = Image.open(self.logo[0]) -# height = im.size[1] -# width = im.size[0] - # We need a float array between 0-1, rather than # a uint8 array between 0-255 nim = np.array(img).astype(np.float) / 255 @@ -687,38 +608,43 @@ class PlotMap(MapCross, MapDrawOptions): # use the "zorder" kwarg to make the image overlay # the plot, rather than hide behind it... (e.g. zorder=10) self.mgplot.figimage(nim, self.logo[1], self.logo[2], zorder=10) - + plt.setp(self.mgaxis.spines.values(), color='black') # SAVEFIG fullname = "{}.{}".format(fig_name, self.filefmt) LOG.info("printing %s", fullname) if self.kml or self.kmz: - # self.mgplot.axes(frameon=0) if self.save: - self.mgplot.savefig(fullname, bbox_inches='tight', - frameon=0, pad_inches=0, dpi=self.dpi, + self.mgplot.savefig(fullname, + bbox_inches='tight', + frameon=0, + pad_inches=0, + dpi=self.dpi, transparent=True) else: self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0) if self.save: - self.mgplot.savefig(fullname, bbox_inches='tight', - pad_inches=.2, dpi=self.dpi) + self.mgplot.savefig(fullname, + frame_on = True, + bbox_inches='tight', + pad_inches=.2, + dpi=self.dpi) return fig_name + def _set_extent(self): + if not self.extent: + return + if self.extent == 'auto': + if self.projection.lower().endswith('polarstereo'): + extent = [-180, 179.9999999999999, self.lat[0], self.lat[-1]] + else: + extent = [self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]] + self.mgaxis.set_extent(extent, ccrs.PlateCarree()) + def compare_maps(self, map_names, outdir, date, steps, tpl=''): """ Generate maps comparison """ - os.chdir(outdir) - - # print "MAPNAMES", map_names - # for map_name in map_names: -# newMapNames = map_names[0] -# items = {} #[i[0] for i in map_names] -# for i in range(0, len(map_names)-1): -# items, newMapNames, newMapNameTpl = -# self.twoMapsCompare([newMapNames, map_names[i+1]], items) - mnames = np.array(map_names) k_s = mnames.T @@ -735,25 +661,6 @@ class PlotMap(MapCross, MapDrawOptions): _, _ = subprocess.getstatusoutput(comm) idx += 1 - # montage -tile 2x -geometry 800x600+1+1 - - # rename from 00-06-... to 00-01 ... -# st, out = commands.getstatusoutput("ls %s*" % newMapNameTpl) -# if st != 0: -# print "Error: %s" % str(out) -# else: -# names = out.split('\n') -# #print names -# for num in range(0, len(names)): -# snum = "%02d" % num -# #print "mv %s %s.gif" % (names[num], -# newMapNameTpl.replace("??", snum)) st2, out2 = -# commands.getstatusoutput("mv -f %s %s.gif" % (names[num], -# newMapNameTpl.replace("??", snum))) -# if st2 != 0: -# print "Error: %s" % str(out2) - -# newMapNameTpl2 = newMapNameTpl.replace("??", "loop") nimg0 = "??" nimg1 = "loop" if tpl: @@ -782,12 +689,19 @@ class PlotMap(MapCross, MapDrawOptions): self.wind_scale = int(self.windopts[3]) LOG.info("SHP %s", str(self.shapefiles)) if self.shapefiles: - self.shapefiles = [f.replace(".shp", "") for f in - glob(self.shapefiles)] + self.shapefiles = [ + f.replace(".shp", "") for f in glob(self.shapefiles) + ] LOG.info("SHP %s", str(self.shapefiles)) - def aplot(self, xloc, yloc, map_data=None, wind_data=None, - contour_data=None, scatter_data=None, **kwargs): + def aplot(self, + xloc, + yloc, + map_data=None, + wind_data=None, + contour_data=None, + scatter_data=None, + **kwargs): """ Plot one or more maps directly from numerical arrays """ # store options to be restored at the end @@ -809,8 +723,7 @@ class PlotMap(MapCross, MapDrawOptions): map_data=[map_data], # temporarily wind_data=wind_data, contour_data=contour_data, - scatter_data=scatter_data - ) + scatter_data=scatter_data) grid = (xloc, yloc) self.init_map(grid) @@ -821,15 +734,17 @@ class PlotMap(MapCross, MapDrawOptions): fig_name = "%s/%s" % (self.outdir, f_name) if self.subplot is None: plt.clf() - self.mgaxis = plt.axes(projection=self._crs) + self.mgaxis = plt.axes(projection=self._crs, frame_on=True) self.mgplot = plt.gcf() else: self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[2], - projection=self._crs) + projection=self._crs, + frame_on=True) self.mgplot = plt.gcf() - + self.mgaxis.patch.set_edgecolor('black') + self.mgaxis.patch.set_linewidth('1') self.gen_image_map( fig_name, grid, @@ -838,21 +753,6 @@ class PlotMap(MapCross, MapDrawOptions): # scatter_data=scatter_data ) -# # Create Max at required intervals -# if self.maxdata and n_time in self.maxdata: -# if (self.maxtitle != None): -# fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time/24)) -# p_title = self.set_title(self.maxtitle, s_date, curr_date, -# s_time) -# self.gen_image_map( -# fig_name, -# grid, -# nc_handler.get_current_max_data(), -# p_title -# ) -# else: -# print "Missing MAXTITLE, thus not generating MAX images!" - # restore options without local function parameters vars(self).update(localvars) @@ -862,12 +762,10 @@ class PlotMap(MapCross, MapDrawOptions): if 'title' not in kwargs: kwargs['title'] = self._get_default_title(cube) - self.aplot( - cube.coord('longitude').points, - cube.coord('latitude').points, - map_data=cube.data, - **kwargs - ) + self.aplot(cube.coord('longitude').points, + cube.coord('latitude').points, + map_data=cube.data, + **kwargs) def plot(self, **kwargs): """ Plot one or more maps from netCDF file(s) """ @@ -913,8 +811,10 @@ class PlotMap(MapCross, MapDrawOptions): self.transf, self.subsetting, self.dimension, - winds={'src': self.wind, - 'opts': self.windopts}, + winds={ + 'src': self.wind, + 'opts': self.windopts + }, contours={'var': self.contours}, varconds=self.varconds, ) @@ -939,20 +839,16 @@ class PlotMap(MapCross, MapDrawOptions): # if not NOMAP: self.init_map(grid) - s_date = datetime.strptime("%s %s %s %s" % (run_tmp[-4:], - run_tmp[-7:-4], - run_tmp[-9:-7], - run_tmp[0:2]), - "%Y %b %d %H") + s_date = datetime.strptime( + "%s %s %s %s" % + (run_tmp[-4:], run_tmp[-7:-4], run_tmp[-9:-7], run_tmp[0:2]), + "%Y %b %d %H") ss_date = s_date.strftime("%Y%m%d") steps = [] start = self.timesteps[0] - # LOG.info("Start @ %s - s_date: %s - run_tmp: %s" % (START, s_date, - # run_tmp)) - if start is None: start = 0 @@ -969,48 +865,40 @@ class PlotMap(MapCross, MapDrawOptions): self.mgplot = plt.gcf() valid_tmp = dims[n_time] - curr_date = datetime.strptime("%s %s %s %s" % (valid_tmp[-4:], - valid_tmp[-7:-4], - valid_tmp[-9:-7], - valid_tmp[0:2]), - "%Y %b %d %H") + curr_date = datetime.strptime( + "%s %s %s %s" % (valid_tmp[-4:], valid_tmp[-7:-4], + valid_tmp[-9:-7], valid_tmp[0:2]), + "%Y %b %d %H") s_time = "%02d" % n_time steps.append(s_time) - p_time = (curr_date - s_date).total_seconds()/3600 - # s_time24 = "%02d" % (p_time%24) - # s_time3d = "%03d" % p_time # Currently unused, only for - # Valentina who has more than 99 img in a set + p_time = (curr_date - s_date).total_seconds() / 3600 if self.img_template: f_name = self.img_template % {'date': ss_date, 'step': s_time} fig_name = "%s/%s" % (self.outdir, f_name) - loop_name = self.img_template % {'date': ss_date, - 'step': 'loop'} + loop_name = self.img_template % { + 'date': ss_date, + 'step': 'loop' + } else: - f_name = '.'.join(os.path.basename(self.srcfiles[0]) - .split('.')[:-1]) + f_name = '.'.join( + os.path.basename(self.srcfiles[0]).split('.')[:-1]) fig_name = "%s/%s_%s" % (self.outdir, f_name, s_time) loop_name = "%s_loop" % (f_name) - # valid = "%02dUTC %s" % (p_time % 24, curr_date.strftime("%d %b - # %Y")) - - # stime = "%02d" % (int(run_tmp.split('Z')[0])+p_time) stime = "%02d" % (p_time) p_title = set_title(self.title, s_date, curr_date, s_time, stime) cur_scatter_data = None if scatter_data is not None and (curr_date in scatter_data): cur_scatter_data = scatter_data[curr_date] - fname = self.gen_image_map( - fig_name, - grid, - nc_handler.get_data_for_tstep(n_time), - p_title, - cur_scatter_data=cur_scatter_data - ) + fname = self.gen_image_map(fig_name, + grid, + nc_handler.get_data_for_tstep(n_time), + p_title, + cur_scatter_data=cur_scatter_data) fig_names.append(fname) plt_names.append((self.mgplot, self.mgaxis)) @@ -1018,15 +906,12 @@ class PlotMap(MapCross, MapDrawOptions): # Create Max at required intervals if self.maxdata and n_time in self.maxdata: if self.maxtitle is not None: - fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time/24)) + fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time / 24)) p_title = set_title(self.maxtitle, s_date, curr_date, s_time, stime) - self.gen_image_map( - fig_name, - grid, - nc_handler.get_current_max_data(), - p_title - ) + self.gen_image_map(fig_name, grid, + nc_handler.get_current_max_data(), + p_title) else: LOG.info("Missing MAXTITLE, not generating MAX images!") @@ -1038,10 +923,9 @@ class PlotMap(MapCross, MapDrawOptions): mpl.rcParams['axes.linewidth'] = 0.1 fig = plt.figure(figsize=(.1, 12)) cax = fig.add_axes([0.05, 0.80, 0.9, 0.15]) - # , axisbg=(1, 1, 1, 0)) print("...", self.bounds, "...") cbar = mpl.colorbar.ColorbarBase( - cax, # =self.mgaxis, + cax, cmap=self.cmap, norm=self.norm, values=self.bounds, @@ -1050,11 +934,6 @@ class PlotMap(MapCross, MapDrawOptions): drawedges=False) if self.bounds: cbar.set_ticklabels(self.bounds) -# else: -# try: -# cbar.set_ticklabels(mco.levels) -# except: -# pass cbar.ax.tick_params(labelsize=6) plt.setp(plt.getp(cax, 'yticklabels'), color='w') plt.setp(plt.getp(cax, 'yticklabels'), fontsize=6) @@ -1064,12 +943,11 @@ class PlotMap(MapCross, MapDrawOptions): tit = str(p_title) idx1 = tit.find('(') idx2 = tit.find(')') - var_unit = tit[idx1:idx2+1] + var_unit = tit[idx1:idx2 + 1] if var_unit.find("%s") >= 0: var_unit = '' plt.xlabel( p_title, - # "%s\n%s" % (self.srcvars[0], var_unit), horizontalalignment='left', color='w', fontsize=6, @@ -1079,55 +957,36 @@ class PlotMap(MapCross, MapDrawOptions): os.makedirs("%s-%s" % (run_date, self.srcvars[0])) if self.save: - fig.savefig( - "%s/%s-%s/%s-colorbar.png" % (self.outdir, run_date, - self.srcvars[0], run_date), - bbox_inches='tight', - pad_inches=0, dpi=self.dpi, - transparent=True - ) + fig.savefig("%s/%s-%s/%s-colorbar.png" % + (self.outdir, run_date, self.srcvars[0], run_date), + bbox_inches='tight', + pad_inches=0, + dpi=self.dpi, + transparent=True) # generate KMZ/KML - Offline/Online - online priority if self.kmz or self.kml: online = self.kml LOG.info("Generating KMZ ...") - gen_kml(fig_names, self.srcvars[0], self.lon, self.lat, dims, - self.outdir, online=online) + gen_kml(fig_names, + self.srcvars[0], + self.lon, + self.lat, + dims, + self.outdir, + online=online) # generate animation. if self.anim: - gen_anim( - "%s.%s" % (loop_name.replace('loop', '*'), self.filefmt), - "%s.gif" % loop_name, - self.outdir, - self.anim_delay - ) -# fulloutdir = os.path.join(self.outdir, -# os.path.dirname(self.img_template)) -# loop_name = os.path.basename(loop_name) -# self.gen_anim( -# fulloutdir, -# "%s.%s" % (loop_name.replace('loop','*'), -# self.filefmt), -# fulloutdir, -# "%s.gif" % loop_name -# ) - + gen_anim("%s.%s" % (loop_name.replace('loop', '*'), self.filefmt), + "%s.gif" % loop_name, self.outdir, self.anim_delay) map_names.append(fig_names) # restore options without local function parameters vars(self).update(localvars) - # print "Returning ", plt_names - # return plt_names - -# num = len(map_names) -# if num > 1: -# mg.compare_maps(map_names, OUTDIR, ss_date, steps, JOINT_TEMPLATE ) - def reset_conf(self): """ Back to the initial conditions. """ - self.__init__() def load_conf(self, section=None, fpath=None, reset=False): @@ -1173,8 +1032,7 @@ class PlotMap(MapCross, MapDrawOptions): self.reset_conf() defaults = copy.deepcopy(vars(self)) # calculate diff - diff = {k: curropts[k] for k in curropts if curropts[k] != - defaults[k]} + diff = {k: curropts[k] for k in curropts if curropts[k] != defaults[k]} # reload diff vars(self).update(diff) # save diff diff --git a/mapgenerator/plotting/tools.py b/mapgenerator/plotting/tools.py index ce3708c9b4a11cf41ae3596eaa7929e5ad18687f..a5a51ee2855556706694a68197b3d8ad2a2b5af2 100644 --- a/mapgenerator/plotting/tools.py +++ b/mapgenerator/plotting/tools.py @@ -73,6 +73,7 @@ def set_resolution(lon, lat): zoom = 1 elif lats > 45 and lons > 75: resolution = '50m' + zoom = 2 elif lats <= 45 and lons <= 75: resolution = '10m' zoom = 4 diff --git a/mapgenerator/test/unit/test_argument_parser.py b/mapgenerator/test/unit/test_argument_parser.py index 79d74ed2555d621bc820d708dcb4b396a35b1d16..3984ce8f3ed48a8dc042e5fe2cf1fbdc778363fa 100644 --- a/mapgenerator/test/unit/test_argument_parser.py +++ b/mapgenerator/test/unit/test_argument_parser.py @@ -1,21 +1,4 @@ -#!/usr/bin/env python -# Copyright 2016 Earth Sciences Department, BSC-CNS - -# This file is part of MapGenerator. - -# MapGenerator is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# MapGenerator is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with MapGenerator. If not, see . import unittest from mapgenerator.plotting.config import ArgumentParser diff --git a/mapgenerator/test/unit/test_environment.py b/mapgenerator/test/unit/test_environment.py index 5ab7bde7e96f8f8bd3a4ddc85ed82c4e3bc61674..a78e96b0b2d1d8e353e9742a7544a258ee272dba 100644 --- a/mapgenerator/test/unit/test_environment.py +++ b/mapgenerator/test/unit/test_environment.py @@ -1,22 +1,3 @@ -#!/usr/bin/env python - -# Copyright 2016 Earth Sciences Department, BSC-CNS - -# This file is part of R2D2. - -# R2D2 is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# R2D2 is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with R2D2. If not, see . - from unittest import TestCase diff --git a/mapgenerator/test/unit/test_http.py b/mapgenerator/test/unit/test_http.py index c95f476b081b1775f45f9cf46f03c93f2bdd891e..3c9dd5e0df61e6378ec303de43fb4b90649c4493 100644 --- a/mapgenerator/test/unit/test_http.py +++ b/mapgenerator/test/unit/test_http.py @@ -1,22 +1,3 @@ -#!/usr/bin/env python - -# Copyright 2016 Earth Sciences Department, BSC-CNS - -# This file is part of R2D2. - -# R2D2 is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# R2D2 is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with R2D2. If not, see . - import unittest from r2d2.argument_parser import ArgumentParser from r2d2.http.dataset import DatasetHTTP diff --git a/setup.py b/setup.py index b3175a35e991cd3af97db0233add8b173f9e4ac7..818fcf3640bbe6fb603870852686807de86be2da 100644 --- a/setup.py +++ b/setup.py @@ -1,24 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright 2018 Barcelona Supercomputing Center - Centro Nacional de -# Supercomputación (BSC-CNS) - -# This file is part of MapGenerator. - -# MapGenerator is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# MapGenerator is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with MapGenerator. If not, see . - from setuptools import setup from mapgenerator import __version__ @@ -41,7 +20,7 @@ REQUIREMENTS = { setup( # Application name: name="mapgenerator", - license='GNU GPL v3', + license='Apache 2.0', # Version number (initial): version=__version__, diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391