def Get_grid_Mercator_Parent(grdfile, name='MERCATOR_PARENT'): nc = Dataset(grdfile) lon = nc.variables['lon'][22:93] lat = nc.variables['lat'][18:86] var = nc.variables['votemper'][0, 0, 18:86, 22:93] #; depth = nc.variables['depth'][:] #lon = nc.variables['lon'][:]; lat = nc.variables['lat'][:]; var = nc.variables['votemper'][0,0,:,:]#; depth = nc.variables['depth'][:] print np.shape(lon), np.shape(lat), np.shape(var) nc.close() lon, lat = np.meshgrid(lon, lat) lon_t = lon[:, :] lat_t = lat[:, :] lon_vert = 0.5 * (lon[:, 1:] + lon[:, :-1]) lon_vert = 0.5 * (lon_vert[1:, :] + lon_vert[:-1, :]) lat_vert = 0.5 * (lat[1:, :] + lat[:-1, :]) lat_vert = 0.5 * (lat_vert[:, 1:] + lat_vert[:, :-1]) a = np.ones((len(var), len(var.T))) a[var.mask == True] = 0 mask_t = a for i in range(len(var)): for j in range(len(var.T)): if point_inside_polygon(lat[i, j], lon[i, j], poly) == False: #if point_inside_polygon(lat[i,j],lon[i,j],poly_parent)==False: mask_t[i, j] = 0 z_t = 1 h = 1 geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:, :-1], lat_vert[:, :-1], lon_vert[:, 1:], lat_vert[:, 1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) angle = (90 - angle) * np.pi / 180. return Grid_VALID(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def draw_scale_bar(self, lat_c, lon_c, distance, ax=None, font_size=12, yoffset=None, color='k'): """draw a simple map scale from x1,y to x2,y in map projection coordinates, label it with actual distance ref_link: http://matplotlib.1069221.n5.nabble.com/basemap-scalebar-td14133.html Parameters: lat_c/lon_c : float, longitude and latitude of scale bar center, in degree distance : float, distance of scale bar, in m yoffset : float, optional, scale bar length at two ends, in degree Example: m.drawscale(33.06, 131.18, 2000) """ gc = pyproj.Geod(a=self.rmajor, b=self.rminor) if distance > 1000.0: distance = np.rint(distance/1000.0)*1000.0 lon_c2, lat_c2, az21 = gc.fwd(lon_c, lat_c, 90, distance) length = np.abs(lon_c - lon_c2) lon0 = lon_c - length/2.0 lon1 = lon_c + length/2.0 if not yoffset: yoffset = 0.1*length self.plot([lon0, lon1], [lat_c, lat_c], color=color) self.plot([lon0, lon0], [lat_c, lat_c+yoffset], color=color) self.plot([lon1, lon1], [lat_c, lat_c+yoffset], color=color) if not ax: ax = plt.gca() if distance < 1000.0: ax.text(lon0+0.5*length, lat_c+yoffset*3, '%d m' % (distance), verticalalignment='top', horizontalalignment='center', fontsize=font_size, color=color) else: ax.text(lon0+0.5*length, lat_c+yoffset*3, '%d km' % (distance/1000.0), verticalalignment='top', horizontalalignment='center', fontsize=font_size, color=color)
def distance(evnt_click, evnt_release): g = pyproj.Geod(ellps='WGS84') _, _, dist = g.inv(evnt_click.xdata, evnt_click.ydata, evnt_release.xdata, evnt_release.ydata) print("(%f, %f) to (%f, %f): %f km" % (evnt_click.xdata, evnt_click.ydata, evnt_release.xdata, evnt_release.ydata, dist / 1000.0))
def _calculate_grid_angle(self): geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(self.lon_t[:,:-1], self.lat_t[:,:-1], \ self.lon_t[:,1:], self.lat_t[:,1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) self.angle = (90 - angle) * np.pi / 180.
def latlon_points(p1, p2, numpoints=100, connection='linear'): """ Compute intermediate points between two given points. Arguments: p1, p2 -- points given as lat/lon pairs, i.e. p1, p2 = [lat, lon] numpoints -- number of intermediate points to be computed aloing the path connection -- method to compute the intermediate points. Can be 'linear' or 'greatcircle' Returns two arrays lats, lons with intermediate latitude and longitudes. """ LAT = 0 LON = 1 TIME = 2 lats, lons, times = None, None, None if connection == 'linear': lats = np.linspace(p1[LAT], p2[LAT], numpoints) lons = np.linspace(p1[LON], p2[LON], numpoints) elif connection == 'greatcircle': if numpoints > 2: gc = pyproj.Geod(ellps="WGS84") pts = gc.npts(p1[LON], p1[LAT], p2[LON], p2[LAT], numpoints - 2) lats = np.asarray([p1[LAT]] + [_x[1] for _x in pts] + [p2[LAT]]) lons = np.asarray([p1[LON]] + [_x[0] for _x in pts] + [p2[LON]]) else: lats = np.asarray([p1[LAT], p2[LAT]]) lons = np.asarray([p1[LON], p2[LON]]) p1_time, p2_time = nc.date2num([p1[TIME], p2[TIME]], "seconds since 2000-01-01") times = np.linspace(p1_time, p2_time, numpoints) return lats, lons, nc.num2date(times, "seconds since 2000-01-01")
def get_nc_Grid_HYCOM(grdfile, name='PUSSY'): nc = Dataset(grdfile) lon = nc.variables['longitude'][:] lat = nc.variables['latitude'][:] #depth = nc.variables['depth'][:] print "2" var = nc.variables['t2m'][0, :, :] nc.close() lon, lat = np.meshgrid(lon, lat) lon_t = lon[:, :] lat_t = lat[:, :] lon_vert = 0.5 * (lon[:, 1:] + lon[:, :-1]) lon_vert = 0.5 * (lon_vert[1:, :] + lon_vert[:-1, :]) lat_vert = 0.5 * (lat[1:, :] + lat[:-1, :]) lat_vert = 0.5 * (lat_vert[:, 1:] + lat_vert[:, :-1]) mask_t = np.ones((len(var[:, 0]), len(var[0, :]))) z_t = 1 h = 1 geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:, :-1], lat_vert[:, :-1], lon_vert[:, 1:], lat_vert[:, 1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) angle = (90 - angle) * np.pi / 180. return Grid_HYCOM(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def addWayPoint(self): """ Handler for button <btAddWayPointToFlightTrack>. Adds a new waypoint behind the currently selected waypoint. """ tableView = self.tableWayPoints index = tableView.currentIndex() lon, lat = 0, 0 if not index.isValid(): row = 0 flightlevel = 0 else: row = index.row() + 1 flightlevel = self.waypoints_model.waypoint_data(row - 1).flightlevel if row < len(self.waypoints_model.all_waypoint_data()): wp_prev = self.waypoints_model.waypoint_data(row - 1) wp_next = self.waypoints_model.waypoint_data(row) gc = pyproj.Geod(ellps="WGS84") # a=40e6, b=40e6) lon, lat = gc.npts(wp_prev.lon, wp_prev.lat, wp_next.lon, wp_next.lat, 3)[1] self.waypoints_model.insertRows( row, waypoints=[ft.Waypoint(lat=lat, lon=lon, flightlevel=flightlevel)]) index = self.waypoints_model.index(row, 0) tableView = self.tableWayPoints tableView.setFocus() tableView.setCurrentIndex(index) # tableView.edit(index) tableView.resizeRowsToContents()
def ellipse(self, x0, y0, a, b, n, ax=None, **kwargs): """Extension to Basemap class from `basemap` to draw ellipses. Parameters ---------- x0 : :class: `float` Centroid of the ellipse in the X axis. y0 : :class: `float` Centroid of the ellipse in the Y axis. a : :class: `float` Semi-major axis of the ellipse. b : :class: `float` Semi-minor axis of the ellipse. n : :class: `int` Number of points to draw the ellipse. Returns ------- :class: `Basemap` It returns one Basemap ellipse at a time. """ ax = kwargs.pop('ax', None) or self._check_ax() g = pyproj.Geod(a=self.rmajor, b=self.rminor) azf, azb, dist = g.inv([x0, x0], [y0, y0], [x0 + a, x0], [y0, y0 + b]) tsid = dist[0] * dist[1] # a * b seg = [self(x0 + a, y0)] AZ = np.linspace(azf[0], 360. + azf[0], n) for i, az in enumerate(AZ): # Skips segments along equator (Geod can't handle equatorial arcs). if np.allclose(0., y0) and (np.allclose(90., az) or np.allclose(270., az)): continue # In polar coordinates, with the origin at the center of the # ellipse and with the angular coordinate ``az`` measured from the # major axis, the ellipse's equation is [1]: # # a * b # r(az) = ------------------------------------------ # ((b * cos(az))**2 + (a * sin(az))**2)**0.5 # # Azymuth angle in radial coordinates and corrected for reference # angle. azr = 2. * np.pi / 360. * (az + 90.) A = dist[0] * np.sin(azr) B = dist[1] * np.cos(azr) r = tsid / (B**2. + A**2.)**0.5 lon, lat, azb = g.fwd(x0, y0, az, r) x, y = self(lon, lat) # Add segment if it is in the map projection region. if x < 1e20 and y < 1e20: seg.append((x, y)) poly = Polygon(seg, **kwargs) ax.add_patch(poly) # Set axes limits to fit map region. self.set_axes_limits(ax=ax) return poly
def Get_grid_ODYSSEA_Parent(grdfile, name='ODYSSEA_PARENT'): nc = Dataset(grdfile) lon = nc.variables['lon'][:] lat = nc.variables['lat'][:] var = nc.variables['analysed_sst'][0, :, :] nc.close() lon, lat = np.meshgrid(lon, lat) lon_t = lon[:, :] lat_t = lat[:, :] lon_vert = 0.5 * (lon[:, 1:] + lon[:, :-1]) lon_vert = 0.5 * (lon_vert[1:, :] + lon_vert[:-1, :]) lat_vert = 0.5 * (lat[1:, :] + lat[:-1, :]) lat_vert = 0.5 * (lat_vert[:, 1:] + lat_vert[:, :-1]) a = np.ones((len(var), len(var.T))) a[var.mask == True] = 0 mask_t = a for i in range(len(var)): for j in range(len(var.T)): if point_inside_polygon(lat[i, j], lon[i, j], poly) == False: #if point_inside_polygon(lat[i,j],lon[i,j],poly_parent)==False: mask_t[i, j] = 0 z_t = 1 h = 1 geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:, :-1], lat_vert[:, :-1], lon_vert[:, 1:], lat_vert[:, 1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) angle = (90 - angle) * np.pi / 180. return Grid_VALID(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def gcpoints_path(self, lons, lats, del_s=100., map_coords=True): """ Same as gcpoints2, but for an entire path, i.e. multiple line segments. lons and lats are lists of waypoint coordinates. """ # use great circle formula for a perfect sphere. gc = pyproj.Geod(a=self.rmajor, b=self.rminor) assert len(lons) == len(lats) assert len(lons) > 1 gclons = [lons[0]] gclats = [lats[0]] for i in range(len(lons) - 1): az12, az21, dist = gc.inv(lons[i], lats[i], lons[i + 1], lats[i + 1]) npoints = int((dist + 0.5 * 1000. * del_s) / (1000. * del_s)) # BUG -- weird path in cyl projection on waypoint move # On some system configurations, the path is wrongly plotted when one # of the waypoints is moved by the user and the current projection is cylindric. # The weird thing is that when comparing cyl projection and stereo projection # (which works), the exact same arguments are passed to gc.npts(). Also, the # gc is initialised with the exact same a and b. Nevertheless, for the cyl # projection, gc.npts() returns lons that connect lon1 and lat2, not lon1 and # lon2 ... I cannot figure out why, maybe this is an issue in certain versions # of pyproj?? (mr, 16Oct2012) lonlats = [] if npoints > 0: lonlats = gc.npts(lons[i], lats[i], lons[i + 1], lats[i + 1], npoints) # The cylindrical projection of matplotlib is not periodic, that means that # -170 longitude and 190 longitude are not identical. The gc projection however # assumes identity and maps all longitudes to -180 to 180. This is no issue for # most other projections. # The clean solution would be to have a periodic display, where the locations # and path are plotted periodically every 360 degree. As long as this is not # supported by matplotlib.basemap, we "hack" this to map the path to the # longitude range defined by the locations. This breaks potentially down in case # that the locations are too far apart (>180 degree), but this is not the typical # use case and will thus hopefully not pose a problem. if self.projection == "cyl" and npoints > 0: lonlats = np.asarray(lonlats) milon = min(lons[i], lons[i + 1]) malon = max(lons[i], lons[i + 1]) sel = lonlats[:, 0] < milon lonlats[sel, 0] += 360 sel = lonlats[:, 0] > malon lonlats[sel, 0] -= 360 for lon, lat in lonlats: gclons.append(lon) gclats.append(lat) gclons.append(lons[i + 1]) gclats.append(lats[i + 1]) if map_coords: x, y = self(gclons, gclats) else: x, y = (gclons, gclats) return x, y
def get_distance(coord0, coord1): """ Computes the distance between two points on the Earth surface Args: coord0: coordinate(lat/lon) of first point coord1: coordinate(lat/lon) of second point Returns: length of distance in km """ pr = pyproj.Geod(ellps='WGS84') return (pr.inv(coord0[1], coord0[0], coord1[1], coord1[0])[-1] / 1000.)
def getGridHYCOM(grdfile, varname3D='water_temp', name='GLBv0.08_expt_93.0'): """ grd = get_nc_Grid_HYCOM(grdfile) Load grid object for HYCOM_GLBv0.08_expt93.0 """ with Dataset(grdfile, 'r') as nc: lon = nc.variables['lon'][:] lat = nc.variables['lat'][:] lon, lat = np.meshgrid(lon, lat) depth = nc.variables['depth'][:] var = nc.variables[varname3D][0, :, 1:-1, 1:-1] lon_t = lon[1:-1, 1:-1] lat_t = lat[1:-1, 1:-1] lon_vert = 0.5 * (lon[:, 1:] + lon[:, :-1]) lon_vert = 0.5 * (lon_vert[1:, :] + lon_vert[:-1, :]) lat_vert = 0.5 * (lat[1:, :] + lat[:-1, :]) lat_vert = 0.5 * (lat_vert[:, 1:] + lat_vert[:, :-1]) mask_t = np.array(~var[:].mask, dtype='int') z_t = np.tile(depth, (mask_t.shape[2], mask_t.shape[1], 1)).T depth_bnds = np.zeros(len(depth) + 1) for i in range(1, len(depth)): depth_bnds[i] = 0.5 * (depth[i - 1] + depth[i]) depth_bnds[-1] = 5750 bottom = pyroms.utility.get_bottom(var[::-1, :, :], mask_t[0], spval=var.fill_value) nlev = len(depth) bottom = (nlev - 1) - bottom h = np.zeros(mask_t[0, :].shape) for i in range(mask_t[0, :].shape[1]): for j in range(mask_t[0, :].shape[0]): if mask_t[0, j, i] == 1: h[j, i] = depth_bnds[int(bottom[j, i]) + 1] geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:, :-1], lat_vert[:, :-1], lon_vert[:, 1:], lat_vert[:, 1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) angle = (90 - angle) * np.pi / 180. return Grid_HYCOM(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def get_nc_Grid_HYCOM(grdfile, name='GLBa0.08_NEP'): """ grd = get_nc_Grid_HYCOM(grdfile) Load grid object for HYCOM_GLBa0.08_NEP """ nc = netCDF4.Dataset(grdfile) lon = nc.variables['lon'][:] lat = nc.variables['lat'][:] depth = nc.variables['z'][:] var = nc.variables['temp'][0, :, 1:-1, 1:-1] nc.close() lon_t = lon[1:-1, 1:-1] lat_t = lat[1:-1, 1:-1] lon_vert = 0.5 * (lon[:, 1:] + lon[:, :-1]) lon_vert = 0.5 * (lon_vert[1:, :] + lon_vert[:-1, :]) lat_vert = 0.5 * (lat[1:, :] + lat[:-1, :]) lat_vert = 0.5 * (lat_vert[:, 1:] + lat_vert[:, :-1]) mask_t = np.array(~var[:].mask, dtype='int') z_t = np.tile(depth, (mask_t.shape[2], mask_t.shape[1], 1)).T depth_bnds = np.zeros(len(depth) + 1) for i in range(1, len(depth)): depth_bnds[i] = 0.5 * (depth[i - 1] + depth[i]) depth_bnds[-1] = 5750 bottom = pycnal.utility.get_bottom(var[::-1, :, :], mask_t[0], spval=var.fill_value) nlev = len(depth) bottom = (nlev - 1) - bottom h = np.zeros(mask_t[0, :].shape) for i in range(mask_t[0, :].shape[1]): for j in range(mask_t[0, :].shape[0]): if mask_t[0, j, i] == 1: h[j, i] = depth_bnds[bottom[j, i] + 1] geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:, :-1], lat_vert[:, :-1], lon_vert[:, 1:], lat_vert[:, 1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) angle = (90 - angle) * np.pi / 180. return Grid_HYCOM(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def Get_any_standard_grid(grdfile): nc = Dataset(grdfile, 'r') variables = list(nc.variables.keys()) if 'lon' in variables and 'lat' in variables: lon = nc.variables['lon'][:] lat = nc.variables['lat'][:] elif 'longitude' in variables and 'latitude' in variables: lon = nc.variables['longitude'][:] lat = nc.variables['latitude'][:] else: raise NameError('No conditional latitude and longitude found') for i in variables: if nc.variables[i][:].ndim == 3: var = nc.variables[i][0] break elif nc.variables[i][:].ndim == 4: var = nc.variables[i][0, 0] break else: pass nc.close() try: lon, lat = np.meshgrid(lon, lat) lon_t, lat_t = lon, lat lon_vert = 0.5 * (lon[:, 1:] + lon[:, :-1]) lon_vert = 0.5 * (lon_vert[1:, :] + lon_vert[:-1, :]) lat_vert = 0.5 * (lat[1:, :] + lat[:-1, :]) lat_vert = 0.5 * (lat_vert[:, 1:] + lat_vert[:, :-1]) a = np.ones((len(var), len(var.T))) a[var.mask == True] = 0 mask_t = a z_t = 1 h = 1 geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:, :-1], lat_vert[:, :-1], lon_vert[:, 1:], lat_vert[:, 1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) angle = (90 - angle) * np.pi / 180. name = grdfile.split('/')[-1].split('_')[0].split('.')[0] return Class_grid(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name) except: raise NameError('No variables are found')
def _calculate_metrics(self): # calculate metrics based on x and y grid super(CGrid_geo, self)._calculate_metrics() # optionally calculate dx and dy based on great circle distances # for more accurate cell sizes. if self.use_gcdist: geod = pyproj.Geod(ellps=self.ellipse) az_forward, az_back, dx = geod.inv(self.lon[:,1:], self.lat[:,1:], \ self.lon[:,:-1], self.lat[:,:-1]) self.dx = 0.5 * (dx[1:, :] + dx[:-1, :]) self.pm = 1.0 / self.dx az_forward, az_back, dy = geod.inv(self.lon[1:,:], self.lat[1:,:], \ self.lon[:-1,:], self.lat[:-1,:]) self.dy = 0.5 * (dy[:, 1:] + dy[:, :-1]) self.pn = 1.0 / self.dy
def Get_grid_Mercator_Nest(grdfile, name='MERCATOR_NEST'): nc = Dataset(grdfile) #lon = nc.variables['lon'][52:78]; lat = nc.variables['lat'][39:65]; var = nc.variables['votemper'][0,0,39:65,52:78]#; depth = nc.variables['depth'][:] lon = nc.variables['lon'][22:93] lat = nc.variables['lat'][18:86] var = nc.variables['votemper'][0, 0, 18:86, 22:93] print np.shape(lon), np.shape(lat), np.shape(var) nc.close() lon, lat = np.meshgrid(lon, lat) lon_t = lon[:, :] lat_t = lat[:, :] lon_vert = 0.5 * (lon[:, 1:] + lon[:, :-1]) lon_vert = 0.5 * (lon_vert[1:, :] + lon_vert[:-1, :]) lat_vert = 0.5 * (lat[1:, :] + lat[:-1, :]) lat_vert = 0.5 * (lat_vert[:, 1:] + lat_vert[:, :-1]) #mask_t = np.array(~var[:].mask, dtype='int') a = np.ones((len(var), len(var.T))) a[var.mask == True] = 0 #print a mask_t = a #mask correction for i in range(len(var)): for j in range(len(var.T)): if point_inside_polygon(lat[i, j], lon[i, j], poly) == False: mask_t[i, j] = 0 #z_t = np.tile(depth,(mask_t.shape[2],mask_t.shape[1],1)).T #depth_bnds = np.zeros(len(depth)+1) #for i in range(1,len(depth)): # depth_bnds[i] = 0.5 * (depth[i-1] + depth[i]) #depth_bnds[-1] = 1000 #bottom = pyroms.utility.get_bottom(var[::-1,:,:], mask_t[0], spval=var.fill_value) #nlev = len(depth) #bottom = (nlev-1) - bottom #h = np.zeros(mask_t[0,:].shape) #for i in range(mask_t[0,:].shape[1]): # for j in range(mask_t[0,:].shape[0]): # if mask_t[0,j,i] == 1: # h[j,i] = depth_bnds[bottom[j,i]+1] z_t = 1 h = 1 geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:, :-1], lat_vert[:, :-1], lon_vert[:, 1:], lat_vert[:, 1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) angle = (90 - angle) * np.pi / 180. return Grid_VALID(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def get_nc_Grid_HYCOM(grdfile, name='PUSSY'): nc = Dataset(grdfile) lon = nc.variables['lon'][:] lat = nc.variables['lat'][:] depth = nc.variables['depth'][:] var = nc.variables['votemper'][0,:,:,:] nc.close() lon,lat=np.meshgrid(lon,lat) lon_t = lon[:,:] lat_t = lat[:,:] lon_vert = 0.5 * (lon[:,1:] + lon[:,:-1]) lon_vert = 0.5 * (lon_vert[1:,:] + lon_vert[:-1,:]) lat_vert = 0.5 * (lat[1:,:] + lat[:-1,:]) lat_vert = 0.5 * (lat_vert[:,1:] + lat_vert[:,:-1]) mask_t = np.array(~var[:].mask, dtype='int') z_t = np.tile(depth,(mask_t.shape[2],mask_t.shape[1],1)).T print z_t depth_bnds = np.zeros(len(depth)+1) for i in range(1,len(depth)): depth_bnds[i] = 0.5 * (depth[i-1] + depth[i]) depth_bnds[-1] = 1000 bottom = pyroms.utility.get_bottom(var[::-1,:,:], mask_t[0], spval=var.fill_value) nlev = len(depth) bottom = (nlev-1) - bottom h = np.zeros(mask_t[0,:].shape) for i in range(mask_t[0,:].shape[1]): for j in range(mask_t[0,:].shape[0]): if mask_t[0,j,i] == 1: h[j,i] = depth_bnds[bottom[j,i]+1] geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:,:-1], lat_vert[:,:-1], lon_vert[:,1:], lat_vert[:,1:]) angle = 0.5 * (az_forward[1:,:] + az_forward[:-1,:]) angle = (90 - angle) * np.pi/180. return Grid_HYCOM(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def _calculate_grid_angle(self): geod = pyproj.Geod(ellps='WGS84') # This is how it used to be, but it fails for some reason. # az_forward, az_back, dx = geod.inv(self.lon_t_vert[:,:-1], self.lat_t_vert[:,:-1], \ # self.lon_t_vert[:,1:], self.lat_t_vert[:,1:]) # angle = 0.5 * (az_forward[1:,:] + az_forward[:-1,:]) # Seems to work... sizey, sizex = self.lon_t_vert.shape angle = np.zeros(self.h.shape) for i in range(sizex - 1): az_forward, az_back, dx = geod.inv(self.lon_t_vert[:,i], self.lat_t_vert[:,i], \ self.lon_t_vert[:,i+1], self.lat_t_vert[:,i+1]) angle[:, i] = 0.5 * (az_forward[1:] + az_forward[:-1]) # part of original code self.angle = (90 - angle) * np.pi / 180.
def _calculate_angle_rho(self): if isinstance(self.lon, np.ma.MaskedArray) or \ isinstance(self.lat, np.ma.MaskedArray): self.angle_rho = np.ma.zeros(self.lon.shape, dtype='d') else: self.angle_rho = np.zeros(self.lon.shape, dtype='d') # calculate metrics based on x and y grid super(CGrid_geo, self)._calculate_angle_rho() # optionally calculate dx and dy based on great circle distances # for more accurate cell sizes. if self.use_gcdist: geod = pyproj.Geod(ellps=self.ellipse) az_forward, az_back, dx = geod.inv(self.lon[:,:-1], self.lat[:,:-1], \ self.lon[:,1:], self.lat[:,1:]) angle = 0.5 * (az_forward[1:, :] + az_forward[:-1, :]) self.angle_rho = (90 - angle) * np.pi / 180.
def gcpoints2(self, lon1, lat1, lon2, lat2, del_s=100., map_coords=True): """ The same as basemap.gcpoints(), but takes a distance interval del_s to space the points instead of a number of points. """ # use great circle formula for a perfect sphere. gc = pyproj.Geod(a=self.rmajor, b=self.rminor) az12, az21, dist = gc.inv(lon1, lat1, lon2, lat2) npoints = int((dist + 0.5 * 1000. * del_s) / (1000. * del_s)) lonlats = gc.npts(lon1, lat1, lon2, lat2, npoints) lons = [lon1] lats = [lat1] for lon, lat in lonlats: lons.append(lon) lats.append(lat) lons.append(lon2) lats.append(lat2) if map_coords: x, y = self(lons, lats) else: x, y = (lons, lats) return x, y
def get_nc_Grid_Nest(grdfile, name='NESTED'): nc = Dataset(grdfile) lon = nc.variables['lon_rho'][:] lat = nc.variables['lat_rho'][:] #depth = nc.variables['depth'][:] h = nc.variables['h'][:] print "3" nc.close() #lon,lat=np.meshgrid(lon,lat) lon_t = lon[:,:] lat_t = lat[:,:] lon_vert = 0.5 * (lon[:,1:] + lon[:,:-1]) lon_vert = 0.5 * (lon_vert[1:,:] + lon_vert[:-1,:]) lat_vert = 0.5 * (lat[1:,:] + lat[:-1,:]) lat_vert = 0.5 * (lat_vert[:,1:] + lat_vert[:,:-1]) rr = Dataset('Nested_grid.nc', 'a', format='NETCDF4') mask_t = rr.variables['mask_rho'][:] z_t = 1 #h=1 geod = pyproj.Geod(ellps='WGS84') az_forward, az_back, dx = geod.inv(lon_vert[:,:-1], lat_vert[:,:-1], lon_vert[:,1:], lat_vert[:,1:]) angle=rr.variables['angle'][:] rr.close() #angle = 0.5 * (az_forward[1:,:] + az_forward[:-1,:]) #angle = (90 - angle) * np.pi/180. return Grid_Nest(lon_t, lat_t, lon_vert, lat_vert, mask_t, z_t, h, angle, name)
def get_cell_area(lon, lat): if np.size(lon.shape) == 1: lon, lat = np.meshgrid(lon, lat) #compute position of the vertices of the cell lonv = np.zeros((lon.shape[0] + 1, lon.shape[1] + 1)) latv = np.zeros((lat.shape[0] + 1, lat.shape[1] + 1)) lonv[1:-1, 1:-1] = 0.25 * (lon[1:, 1:] + lon[:-1, :-1] + lon[1:, :-1] + lon[:-1, 1:]) lonv[0, :] = lonv[1, :] lonv[-1, :] = lonv[-2, :] lonv[:, 0] = lonv[:, 1] - (lonv[:, 2] - lonv[:, 1]) lonv[:, -1] = lonv[:, -2] + (lonv[:, -2] - lonv[:, -3]) latv[1:-1, 1:-1] = 0.25 * (lat[1:, 1:] + lat[:-1, :-1] + lat[1:, :-1] + lat[:-1, 1:]) latv[:, 0] = latv[:, 1] latv[:, -1] = latv[:, -2] latv[0, :] = latv[1, :] - (latv[2, :] - latv[1, :]) latv[-1, :] = latv[-2, :] + (latv[-2, :] - latv[-3, :]) #get dx and dy based on great circle distances ellipse = 'WGS84' geod = pyproj.Geod(ellps=ellipse) az_forward, az_back, dx = geod.inv(lonv[:,1:], latv[:,1:], \ lonv[:,:-1], latv[:,:-1]) dx = 0.5 * (dx[1:, :] + dx[:-1, :]) az_forward, az_back, dy = geod.inv(lonv[1:,:], latv[1:,:], \ lonv[:-1,:], latv[:-1,:]) dy = 0.5 * (dy[:, 1:] + dy[:, :-1]) area = dx * dy return dx, dy, area
def drawscale(self, length, yoffset=None): """draw a simple map scale from x1,y to x2,y in map projection coordinates, label it with actual distance in miles""" # Define dimensions x1, y1, x2, y, extra = 0.03 * map.xmax, 0.03 * map.xmax, 0.03 * map.xmax + length, 0.05 * map.xmax, 0.01 * map.xmax # Call rectangle function for bounding box drawRect(x1 - extra, y1 - extra, length + 2 * extra + 1300, y1 + 2 * extra, 'k', 1) # Get info for scale bar yoffset = 0.01 * map.ymax lon1, lat1 = self(x1, y, inverse=True) lon2, lat2 = self(x2, y, inverse=True) # Convert to map projection units gc = pyproj.Geod(a=self.rmajor, b=self.rminor) # This gets the distance on a level plane (inv), to get distance on the plane, use sqrt() az12, az21, dist = gc.inv(lon1, lat1, lon2, lat2) # These are the pieces of the scale bar map.plot([x1, x2], [y, y], linewidth=1, color='k') # Vertical ticks map.plot([x1, x1], [y - yoffset, y + yoffset], linewidth=1, color='k') map.plot([x1 + length / 6.0, x1 + length / 6.0], [y - yoffset, y + yoffset], linewidth=1, color='k') map.plot([x1 + length / 3.0, x1 + length / 3.0], [y - yoffset, y + yoffset], linewidth=1, color='k') map.plot([x1 + 2 * length / 3.0, x1 + 2 * +length / 3.0], [y - yoffset, y + yoffset], linewidth=1, color='k') map.plot([x2, x2], [y - yoffset, y + yoffset], linewidth=1, color='k') # Label after converting to miles # 0 text(x1, y - yoffset - 0.5 * extra, '%d' % (dist * 0 * 0.621371, ), verticalalignment='top', horizontalalignment='center', fontsize=9, color='k', fontweight='normal') # 0.5 text(x1 + length / 6.0, y - yoffset - 0.5 * extra, '%g' % (round(dist / 6 / 1000. * 0.621371, 1), ), verticalalignment='top', horizontalalignment='center', fontsize=9, color='k', fontweight='normal') # 1 text(x1 + length / 3.0, y - yoffset - 0.5 * extra, '%d' % (dist / 3 / 1000. * 0.621371, ), verticalalignment='top', horizontalalignment='center', fontsize=9, color='k', fontweight='normal') # 2 text(x1 + length * 2 / 3.0, y - yoffset - 0.5 * extra, '%d' % (dist * 2 / 3 / 1000. * 0.621371, ), verticalalignment='top', horizontalalignment='center', fontsize=9, color='k', fontweight='normal') # 3 text(x1 - 200 + length, y - yoffset - 0.5 * extra, '%d miles' % (dist / 1000. * 0.621371, ), verticalalignment='top', horizontalalignment='left', fontsize=9, color='k', fontweight='normal')
def __init__(self, identifier=None, CRS=None, BBOX_UNITS=None, OPERATION_NAME=None, appearance=None, **kwargs): """ New constructor automatically adds coastlines, continents, and a graticule to the map. Keyword arguments are the same as for mpl_toolkits.basemap. Additional arguments: CRS -- string describing the coordinate reference system of the map. BBOX_UNITS -- string describing the units of the map coordinates. OPERATION_NAME -- string with operation name """ # Coordinate reference system identifier and coordinate system units. self.crs = CRS if CRS is not None else self.crs if hasattr(self, "crs") else None if BBOX_UNITS is not None: self.bbox_units = BBOX_UNITS else: self.bbox_units = getattr(self, "bbox_units", None) self.operation_name = OPERATION_NAME if OPERATION_NAME is not None else self.operation_name \ if hasattr(self, "operation_name") else None # Dictionary containing map appearance settings. if appearance is not None: param_appearance = appearance else: param_appearance = getattr(self, "appearance", {}) default_appearance = {"draw_graticule": True, "draw_coastlines": True, "fill_waterbodies": True, "fill_continents": True, "colour_water": ((153 / 255.), (255 / 255.), (255 / 255.), (255 / 255.)), "colour_land": ((204 / 255.), (153 / 255.), (102 / 255.), (255 / 255.))} default_appearance.update(param_appearance) self.appearance = default_appearance # Identifier of this map canvas (used to query data structures that # are observed by different views). if identifier is not None: self.identifier = identifier else: self.identifier = getattr(self, "identifier", None) # Call the Basemap constructor. If a cylindrical projection was used # before, Basemap stores an EPSG code that will not be changed if # the Basemap constructor is called a second time. Hence, we have to # delete the attribute (mr, 08Feb2013). if hasattr(self, "epsg"): del self.epsg super().__init__(**kwargs) self.gc = pyproj.Geod(a=self.rmajor, b=self.rminor) self.kwargs = kwargs # Set up the map appearance. if self.appearance["draw_coastlines"]: self.map_coastlines = None if len(self.coastsegs) > 0 and len(self.coastsegs[0]) > 0: self.map_coastlines = self.drawcoastlines(zorder=3) self.map_countries = self.drawcountries(zorder=3) else: self.map_coastlines = None self.map_countries = None if self.appearance["fill_waterbodies"]: self.map_boundary = self.drawmapboundary(fill_color=self.appearance["colour_water"]) else: self.map_boundary = None # zorder = 0 is necessary to paint over the filled continents with # scatter() for drawing the flight tracks and trajectories. # Curiously, plot() works fine without this setting, but scatter() # doesn't. if self.appearance["fill_continents"]: self.map_continents = self.fillcontinents( color=self.appearance["colour_land"], lake_color=self.appearance["colour_water"], zorder=1) else: self.map_continents = None self.image = None # Print project name and CRS identifier into figure. crs_text = "" if self.operation_name is not None: crs_text += self.operation_name if self.crs is not None: if len(crs_text) > 0: crs_text += "\n" crs_text += self.crs if hasattr(self, "crs_text"): # update existing textbox self.crs_text.set_text(crs_text) else: self.crs_text = self.ax.figure.text(0, 0, crs_text) if self.appearance["draw_graticule"]: pass # self._draw_auto_graticule() ; It's already called in mpl_qtwidget.py in MplTopviewCanvas init_map(). else: self.map_parallels = None self.map_meridians = None # self.warpimage() # disable fillcontinents when loading bluemarble self.ax.set_autoscale_on(False) if not hasattr(self, "airports") or not self.airports: self.airports = None self.airtext = None if not hasattr(self, "airspaces") or not self.airspaces: self.airspaces = None self.airspacetext = None
def geodesic(self, lon1, lat1, lon2, lat2, del_s=.01, clip=True, **kwargs): """ Plot a geodesic curve from (lon1, lat1) to (lon2, lat2), with points separated by arc length del_s. Return a list of Line2D instances for the curves comprising the geodesic. If the geodesic does not cross the map limb, there will be only a single curve; if it crosses the limb, there will be two curves. """ # TODO: Perhaps return a single Line2D instance when there is only a # single segment, and a list of segments only when there are two segs? # TODO: Check the units of del_s. # This is based on Basemap.drawgreatcircle (which draws an *arc* of a # great circle), but addresses a limitation of that method, supporting # geodesics that cross the map boundary by breaking them into two # segments, one in the eastern hemisphere and the other in the western. gc = pyproj.Geod(a=self.rmajor, b=self.rminor) az12, az21, dist = gc.inv(lon1, lat1, lon2, lat2) npoints = int((dist + 0.5**del_s) / del_s) # Calculate lon & lat for points on the arc. lonlats = gc.npts(lon1, lat1, lon2, lat2, npoints) lons = [lon1] lats = [lat1] for lon, lat in lonlats: lons.append(lon) lats.append(lat) lons.append(lon2) lats.append(lat2) # Break the arc into segments as needed, when there is a longitudinal # hemisphere crossing. segs = [] seg_lons, seg_lats = [lon1], [lat1] cur_hem = self.east_hem(lon1) for lon, lat in zip(lons[1:], lats[1:]): if self.east_hem(lon) == cur_hem: seg_lons.append(lon) seg_lats.append(lat) else: # We should interpolate a new pt at the boundary, but in # the mean time just rely on the step size being small. segs.append((seg_lons, seg_lats)) seg_lons, seg_lats = [lon], [lat] cur_hem = not cur_hem segs.append((seg_lons, seg_lats)) # Plot each segment; return a list of the mpl lines. lines = [] for lons, lats in segs: x, y = self(lons, lats) if clip and self._limb: line = plot(x, y, clip_path=self._limb, **kwargs)[0] else: line = plot(x, y, **kwargs)[0] lines.append(line) # If there are multiple segments and no color args, reconcile the # colors, which mpl will have autoset to different values. # *** Does this screw up mpl's color set sequence for later lines? if 'c' not in kwargs or 'color' in kwargs: if len(lines) > 1: c1 = lines[0].get_color() for line in lines[1:]: line.set_color(c1) return lines
""" import logging import netCDF4 as nc import numpy as np from scipy.interpolate import interp1d from scipy.ndimage import map_coordinates try: import mpl_toolkits.basemap.pyproj as pyproj except ImportError: import pyproj from mslib.utils.config import config_loader __PR = pyproj.Geod(ellps='WGS84') def get_distance(lat0, lon0, lat1, lon1): """ Computes the distance between two points on the Earth surface Args: lat0: lat of first point lon0: lon of first point lat1: lat of second point lon1: lon of second point Returns: length of distance in km """ return __PR.inv(lon0, lat0, lon1, lat1)[-1] / 1000.
def tissot(self, lon_0, lat_0, radius_deg, npts, ax=None, **kwargs): """ Draw a polygon centered at ``lon_0,lat_0``. The polygon approximates a circle on the surface of the earth with radius ``radius_deg`` degrees latitude along longitude ``lon_0``, made up of ``npts`` vertices. The polygon represents a Tissot's indicatrix (http://en.wikipedia.org/wiki/Tissot's_Indicatrix), which when drawn on a map shows the distortion inherent in the map projection. Tissots can be used to display azimuthally symmetric directional uncertainties ("error circles"). Extra keyword ``ax`` can be used to override the default axis instance. Other \**kwargs passed on to matplotlib.patches.Polygon. returns a list of matplotlib.patches.Polygon objects, with two polygons when the tissot crosses the limb, and just one polygon otherwise. """ # TODO: Just return the polygon (not a list) when there is only one # polygon? Or stick with the list for consistency? # This is based on Basemap.tissot, but addresses a limitation of that # method by handling tissots that cross the limb of the map by finding # separate polygons in the eastern and western hemispheres comprising # the tissot. ax = kwargs.pop('ax', None) or self._check_ax() g = pyproj.Geod(a=self.rmajor, b=self.rminor) az12, az21, dist = g.inv(lon_0, lat_0, lon_0, lat_0 + radius_deg) start_hem = self.east_hem(lon_0) segs1 = [self(lon_0, lat_0 + radius_deg)] over, segs2 = [], [] delaz = 360. / npts az = az12 last_lon = lon_0 # Note adjacent and opposite edge longitudes, in case the tissot # runs over the edge. if start_hem: # eastern case adj_lon = self.east_lon opp_lon = self.west_lon else: adj_lon = self.west_lon opp_lon = self.east_lon for n in range(npts): az = az + delaz # skip segments along equator (Geod can't handle equatorial arcs) if np.allclose(0., lat_0) and (np.allclose(90., az) or np.allclose(270., az)): continue else: lon, lat, az21 = g.fwd(lon_0, lat_0, az, dist) # If in the starting hemisphere, add to 1st polygon seg list. if self.east_hem(lon) == start_hem: x, y = self(lon, lat) # Add segment if it is in the map projection region. if x < 1.e20 and y < 1.e20: segs1.append((x, y)) last_lon = lon # Otherwise, we cross hemispheres. else: # Trace the edge of each hemisphere. x, y = self(adj_lon, lat) if x < 1.e20 and y < 1.e20: segs1.append((x, y)) # We presume if adj projection is okay, opposite is. segs2.append(self(opp_lon, lat)) # Also store the overlap in the opposite hemisphere. x, y = self(lon, lat) if x < 1.e20 and y < 1.e20: over.append((x, y)) last_lon = lon poly1 = Polygon(segs1, **kwargs) ax.add_patch(poly1) if segs2: over.reverse() segs2.extend(over) poly2 = Polygon(segs2, **kwargs) ax.add_patch(poly2) return [poly1, poly2] else: return [poly1]
#!/usr/bin/env python import calendar import pickle import numpy as np from mpl_toolkits.basemap import pyproj import matplotlib.pyplot as plt from datetime import datetime, timedelta from opendrift.models.oceandrift3D import OceanDrift3D from opendrift.readers import reader_ROMS_native from opendrift.readers import reader_netCDF_CF_generic geod = pyproj.Geod(ellps='WGS84') recalc = True alldata = {'CODE': {'norshelf': {'stokes': None, 'nostokes': None}, 'globcur': {'stokes': None, 'nostokes': None}}, 'iSphere': {'norshelf': {'stokes': None, 'nostokes': None}, 'globcur': {'stokes': None, 'nostokes': None}} } # Read drifter data f = '/disk1/data/drifter/all_trajectories_wind_unfiltered.dat' data = pickle.load(open(f)) #print data.keys() #print data[12].keys() if recalc is True: for with_stokes in [True, False]: if with_stokes is True: hasstokes = 'stokes'
glon2 *= 180. / np.pi glat2 *= 180. / np.pi baz *= 180. / np.pi return (glon2, glat2, baz) def equi(m, centerlon, centerlat, radius, *args, **kwargs): glon1 = centerlon glat1 = centerlat X = [] Y = [] for azimuth in range(0, 360): glon2, glat2, baz = shoot(glon1, glat1, azimuth, radius) X.append(glon2) Y.append(glat2) X.append(X[0]) Y.append(Y[0]) #~ m.plot(X,Y,**kwargs) #Should work, but doesn't... X, Y = m(X, Y) plt.plot(X, Y, **kwargs) m.plot(X, Y, **kwargs) _GEOD = pyproj.Geod(ellps='WGS84') def CalcDist(lon1, lat1, lon2, lat2): return _GEOD.inv(lon1, lat1, lon2, lat2)[2] / 1000.
def mapModisRanking(mapSize, mapTitle, mapFile, boundaryFile, notePosition, legendPosition, outName, outRes=200, backgroundLabel='', citiesFile=None, citiesField=None, citiesLabelSize=None, citiesMarkerSize=None): ''' Maps the ranking raster and export to file mapSize (tuple of num): Size of the ap in inches mapTitle (str): Title of the map mapFile (str): Full address of the raster with the ranking information boundaryFile (str): Full address of the shapefile with the boundaries information notePosition (tuple of num): Two values between 0 and 1 giving the position of the top left corner of the note on the chart. The value are relative to the chart, so (0,0) is the bottom left corner and (1,1) is the top right corner. legendPosition (tuple of num): Two values giving the position of the legend. Same as notePosition outName (str): Full address of the output name for the chart (.png) outRes (int): Dpi resolution for the output chart backgroundLabel (str): Label for the white background pixels citiesFile (str): Full address of the shapefile with the information on the cities. citiesField (str): Name of the field in the shapefile with name of the cities to use as label citiesLabelSize (int): Size of the labels ''' #Create new figure window fig = plt.figure(figsize=mapSize) # a new figure window ax = fig.add_subplot(1, 1, 1) # specify (nrows, ncols, axnum) #Remove frame of subplot ax.axis('off') #Add title ax.set_title(mapTitle, fontsize=24, weight='bold', y=1.02) # Read the data and metadata datafile = gdal.Open(mapFile) bnd1 = datafile.GetRasterBand(1).ReadAsArray() #Get no data value nodata = datafile.GetRasterBand(1).GetNoDataValue() #Change data type and remove no data value from raster bnd1 = bnd1.astype(float) bnd1[bnd1 == nodata] = np.nan #Get raster size, projection, and resolution nx = datafile.RasterXSize # Raster xsize ny = datafile.RasterYSize # Raster ysize gt = datafile.GetGeoTransform() proj = datafile.GetProjection() xres = gt[1] yres = gt[5] # get the edge coordinates and add half the resolution # to go to center coordinates xmin = gt[0] + xres * 0.5 xmax = gt[0] + (xres * nx) - xres * 0.5 ymin = gt[3] + (yres * ny) + yres * 0.5 ymax = gt[3] - yres * 0.5 # create a grid of lat/lon coordinates in the original projection (lon_source, lat_source) = np.mgrid[xmin:xmax + xres:xres, ymax + yres:ymin:yres] #Create the basemap mapR = Basemap(projection='cyl',llcrnrlat=ymin,urcrnrlat=ymax,\ llcrnrlon=xmin,urcrnrlon=xmax , resolution='i', ax=ax) #Prepare the color map #Colors and values for scale colorsScale = [(255, 254, 141), (239, 48, 166), (197, 38, 182), (113, 28, 198), (19, 0, 236), (28, 67, 198), (5, 251, 255)] stopsScale = [-3, 10, 30, 50, 70, 90, 110] #Normalize the colors to 0-1 norm = mpl.colors.Normalize(vmin=0., vmax=255.) colorsScale = [tuple(norm(v) for v in T) for T in colorsScale] #Normalize the values for the scale norm = mpl.colors.Normalize(vmin=-3., vmax=110.) stopsScale = [norm(v) for v in stopsScale] #Combine into color scale for mapping purpleBlue = zip(stopsScale, colorsScale) #Create the segmented color map purpleBlueLinear = col.LinearSegmentedColormap.from_list('purpleBlue', purpleBlue, N=256, gamma=1.0) #Create labels for colormap purpleBlueLabels = [ 'Below min', 'Min of the 10 ref. years', '', 'Median of the 10 ref. years', '', 'Max of the 10 ref. Years', 'Above max' ] # project in the original Basemap and plot with pcolormesh mapR.pcolormesh(lon_source, lat_source, bnd1.T, cmap=purpleBlueLinear) #Add boundary aoi_info = mapR.readshapefile(boundaryFile, 'aoi', color='black', linewidth=1.3) if citiesFile: #Add major cities cities_info = mapR.readshapefile(citiesFile, 'cities') #Add the city names as labels cityFont = { 'fontname': 'Arial', 'size': str(citiesLabelSize), 'color': 'black', 'weight': 'bold' } for info, city in zip(mapR.cities_info, mapR.cities): mapR.plot(city[0], city[1], marker='o', color='black', markersize=citiesMarkerSize, markeredgewidth=2) #'o' for circle, '.' for point plt.text(city[0] + 0.01, city[1] + 0.005, unicode(info[citiesField], 'utf-8'), **cityFont) #Add scale bar scaleParam = { 'startLon': xmin + (xmax - xmin) * 0.62, 'startLat': ymin + 0.01, 'lengthKm': 100, 'yoffset': 0.02 } #Get initial lon lat in map units lon1, lat1 = mapR(scaleParam['startLon'], scaleParam['startLat'], inverse=True) #Get final lon lat from distance gc = pyproj.Geod(a=mapR.rmajor, b=mapR.rminor) lon2, lat2, az = gc.fwd(lon1, lat1, 90, scaleParam['lengthKm'] * 1000) #Get back the final lon lat in map units x2, y2 = mapR(lon2, lat2, inverse=False) #Plot the lines for the scale barHeight = abs(scaleParam['startLon'] - x2) / 100. mapR.plot([scaleParam['startLon'], x2], [scaleParam['startLat'], scaleParam['startLat']], color='k') mapR.plot([scaleParam['startLon'], scaleParam['startLon']], [ scaleParam['startLat'] - barHeight, scaleParam['startLat'] + barHeight ], color='k') mapR.plot([x2, x2], [ scaleParam['startLat'] - barHeight, scaleParam['startLat'] + barHeight ], color='k') scaleFont = { 'fontname': 'Arial', 'size': '18', 'color': 'black', 'weight': 'bold', 'horizontalalignment': 'center' } plt.text(scaleParam['startLon'], scaleParam['startLat'] + barHeight + 0.01, '0', **scaleFont) plt.text(x2, scaleParam['startLat'] + barHeight + 0.01, '%s km' % (scaleParam['lengthKm']), **scaleFont) #Add note commentFont = {'fontname': 'Arial', 'size': '18', 'color': 'black'} comment = 'Notes:'\ '\n--Most pixels contain other land uses \nbesides coffee.'\ '\n--The index shows health of vegetation in \neach pixel compared to reference years, \n'\ 'not coffee production directly. '\ '\n--Vegetative health is affected mostly by \n'\ 'natural factors (rain, etc.) but can also be \naffected by human intervention (pruning, \netc.).' plt.text(notePosition[0], notePosition[1], comment, horizontalalignment='left', verticalalignment='center', transform=ax.transAxes, **commentFont) #Prepare color legend if backgroundLabel: cmap = mpl.colors.ListedColormap([(1, 1, 1)] + colorsScale) #bounds = range(len(colorsScale)+1) else: cmap = mpl.colors.ListedColormap(colorsScale) bounds = range(cmap.N) norm = mpl.colors.BoundaryNorm(bounds, cmap.N) #Add axis for the color legend legendFont = { 'fontsize': 18, 'fontweight': 'bold', 'verticalalignment': 'center' } axColors = fig.add_axes([ legendPosition[0], legendPosition[1], 0.05, 0.15 ]) #left, bottom, width, height] in fractions of figure width and height cb = mpl.colorbar.ColorbarBase( axColors, cmap=cmap, norm=norm, boundaries=bounds, ticks=[y + 0.5 for y in range(len(colorsScale) + 1)], spacing='uniform', orientation='vertical') cb.ax.set_title('Legend', fontsize=20, weight='bold', x=0.7, y=1.05) axColors.tick_params(axis=u'both', which=u'both', length=0) if backgroundLabel: legendLabels = [backgroundLabel] + purpleBlueLabels else: legendLabels = purpleBlueLabels axColors.set_yticklabels(legendLabels, fontdict=legendFont) #Fit layout for smaller image plt.tight_layout() #Save plot plt.savefig(outName, dpi=outRes)