Shifting grid with matplotlib - python

I'm plotting data which is formatted between 0 and 360 degrees. I'm trying to plot this on cyl or merc projection, but it is only showing data from 0 onwards (I want to plot the data with the GMT in the center, so need the data on a lon grid of -180 to 180). If I shift the grid (lon = lon -180) then all data shows, but the data is in the wrong place by -180 degrees.
Issue:
Works fine in ortho projection though. Relevant code below.
lat = np.linspace(90,-90,721)
lon = np.linspace(0,360,1440)
m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,llcrnrlon=0,urcrnrlon=360,resolution='c',)
X, Y = np.meshgrid(lon, lat)
X, Y = m(X, Y)
cs = m.contourf(X,Y,Plot,scale, cmap=cmap)

Please Try:
import numpy as np
from mpl_toolkits.basemap import shiftgrid
from mpl_toolkits.basemap import Basemap
lat = np.linspace(-90, 90, 721)
lon = np.linspace(0, 360, 1440)
Plot, lon = shiftgrid(180., Plot, lon, start=False) # shiftgrid
m = Basemap(projection='cyl', llcrnrlat=-90, urcrnrlat=90, llcrnrlon=-180, urcrnrlon=180, resolution='c',)
X, Y = np.meshgrid(lon, lat)
X, Y = m(X, Y)
cs = m.contourf(X, Y, Plot, scale, cmap=cmap)
shiftgrid: shifts global lat/lon grids east or west.

I have a solution (albeit an ugly one). By reordering the data.
temp = np.zeros((721,1440))
temp[:,0:720] = Plot[:,720:1440]
temp[:,720:1440] = Plot[:,0:720]
Plot[:]=temp[:]
Or by using np.roll (if you know how many gridpoints to shift)

Related

Reproducing extent of cartopy plots with orthographic projection

I am generating set of different maps and I would like them to all have the same axis limits. However, I am having a hard time figuring out how to set the extent.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
lon = -90
lat = 90
proj = ccrs.Orthographic
projection = proj(lon, lat)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = projection)
transform = ccrs.Geodetic()
lat0 = 50
lon0 = 50
for x in range(-5,6):
plt.plot(lon0 + x, lat0, 'bo', transform = transform)
plt.plot(lon0, lat0 + x, 'bo', transform = transform)
I want to be able to extract the extent of the "map" and then apply it to other maps that will autoscale differently. However, even when I try to extract the extent and reapply it to the same figure, it fails.
a = ax.get_extent(crs = proj())
#a = (2769836.95350539, 3487499.8040009444, 4467034.2547478145, 5254224.255689873)
ax.set_extent(a, crs = proj())
Kind of obvious once I realized it, but the lon and lat of projection need to be specified in the extent description.
a = ax.get_extent(crs = proj(lon, lat))
ax.set_extent(a, crs = proj(lon, lat))

Aligning data (contourf) on Basemap

I've started working with Basemap, which seems potentially very useful.
If I plot some global data on a latitude/longitude grid as filled contours, it works great: Iff I leave the lat_0 and lon_0 as zero. Once I change the center location, the map moves but the data doesn't. I would be grateful for advice.
I've created a simple version of the code I'm using, with some simple sample data that illustrates the problem. The values should be (are) large at the equator but small at the poles. If you run the code with lat_0 and lon_0 = 0, it works fine. But if you change the center location to a different coordinate, the same pattern/data is presented even though the map has moved.
from mpl_toolkits.basemap import Basemap, cm
import matplotlib.pyplot as plt
import numpy as np
# create data
lat = np.linspace(-90,90,num=180)
lon = np.linspace(-180,180,num=361)
h2o_north = np.linspace(1,65,num=90)
h2o_south = np.flipud(h2o_north)
h2o = np.append(h2o_north,h2o_south)
data = np.transpose(np.tile(h2o,(len(lon),1)))
# create figure and axes instances
fig = plt.figure(figsize=(10,10))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
# create map
m = Basemap(projection='ortho',lon_0=-50,lat_0=50,resolution='l')
# draw coastlines and country boundaries
m.drawcoastlines()
m.drawcountries()
# draw parallels
parallels = np.arange(-90.,90,10.)
m.drawparallels(parallels)
# draw meridians
meridians = np.arange(180.,360.,10.)
m.drawmeridians(meridians)
ny = data.shape[0]
nx = data.shape[1]
lons, lats = m.makegrid(nx, ny) # get lat/lons of ny by nx evenly space grid
x, y = m(lons, lats) # compute map projection coordinates
# draw filled contours.
clevs = np.linspace(0,70,num=281)
cs = m.contourf(x,y,data,clevs,cmap=plt.cm.jet)
# colorbar
cbar = m.colorbar(cs,location='bottom',pad="5%",ticks=np.linspace(0,70,15))
cbar.set_label('Scale of the data')
plt.title('Some global data', fontsize=14)
Use np.meshgrid() to create the meshgrid of lon-lat, then, convert it to projection coordinates, and the data are ready to generate contours and plot.
Here is the working code:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
# data for z (2D array)
h2o_north = np.linspace(1, 65, num=90)
h2o_south = np.flipud(h2o_north)
h2o = np.append(h2o_north, h2o_south)
data = np.transpose(np.tile(h2o, (len(h2o_north), 1)))
# create figure and axes instances
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot()
# create basemap instance
m = Basemap(projection='ortho', lon_0=-50, lat_0=50, resolution='c', ax=ax)
# create meshgrid covering the whole globe with ...
# conforming dimensions of the `data`
lat = np.linspace(-90, 90, data.shape[0])
lon = np.linspace(-180, 180, data.shape[1])
xs, ys = np.meshgrid(lon, lat) # basic mesh in lon, lat (degrees)
x, y = m(xs, ys) # convert (lon,lat) to map (x,y)
# draw filled contours
clevs = np.linspace(0, np.max(data), 60)
cs = m.contourf(x, y, data, clevs, cmap=plt.cm.jet)
m.drawcoastlines()
m.drawcountries()
m.drawmeridians(range(-180, 180, 30))
m.drawparallels(range(-90, 90, 30))
# draw colorbar
cbar = m.colorbar(cs, location='bottom', pad="5%", ticks=np.linspace(0, np.max(data), 5))
cbar.set_label('Scale of the data')
plt.show()
The resulting plot:

Python Basemap does not show plotted points

I just started using Python basemap, but I can not make point to appear in my map!... Here is the function I'm trying to build:
def map_plot(df):
df = df.apply(pd.to_numeric, errors='coerce').dropna()
m = Basemap(projection='mill',
llcrnrlat=25,
llcrnrlon=-130,
urcrnrlat=50,
urcrnrlon=-60,
resolution='l') #proyeccion de Miller
m.drawcoastlines()
m.drawcountries(linewidth=2)
m.drawstates(color='b')
m.fillcontinents(color = '#888888')
x_map, y_map = m(df['Latitude'].values, df['Longitud'].values)
x = []
y = []
for x_map, y_map in zip(x_map, y_map):
if y_map > 0: continue
x.append(x_map)
y.append(y_map)
m.plot(x, y, 'g^', markersize=5)
plt.show()
So, the map shows, but not a single point is plotted.
Here is how my data looks before calculating the projection coordinates:
,Latitude,Longitud
0,35.93,-77.79
1,35.93,-77.79
2,38.78,-80.22
3,37.65,-82.25
4,41.12,-104.82
5,41.85,-80.83
6,39.7,-84.21
7,39.9,-80.94
8,39.1,-84.54
9,39.93,-83.82
10,40.05,-82.39
What am I doing wrong?
Thank you!!!
You need grid coordinates (x, y) to plot points on the map. Here is the code that implements the required coordinate transformation and does the plotting.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
m = Basemap(projection='mill',
llcrnrlat=20,
llcrnrlon=-130,
urcrnrlat=50,
urcrnrlon=-60,
resolution='l') # Miller proj, USA
m.drawcoastlines()
m.drawcountries(linewidth=2)
m.drawstates(color='b')
m.fillcontinents(color = '#888888')
# sample data to plot with
lons = [-100, -75] # degrees
lats = [25, 40]
# plotting points
for lon, lat in zip(lons, lats):
x, y = m.projtran(lon, lat) # coord transformation
m.plot(x, y, 'r^', markersize=15) # needs grid coords to plot
plt.show()
Your line
if y_map > 0: continue
is causing you problems. Every value of y is >0, so the continue is being applied, which skips to the next iteration of the for loop. Consequently, your lines
x.append(x_map)
y.append(y_map)
are never used

Pcolormesh on basemap

I have a long list of latitudes and longitudes, and the ultimate goal is to plot the number of occurrences at each latitude/longitude pair on a map of North America. As an example, I have a DataFrame, called sort_df, that looks like:
longitude -95 -94 -93 -92
latitude
43 0 4 8 3
44 7 3 0 0
using sort_df.values and pmeshcolor, I can plot the data. The code is as follows:
m.Basemap(llcrnrlon = -170, llcrnrlat = 10, urcrnrlon = -50, urcrnrlat = 80)
m.drawcoastlines()
m.drawstates()
ny = sort_df.shape[0]
nx = sort_df.shape[1]
lons, lats = m.makegrid(nx,ny)
x,y = m(lons,lats)
my_cmap = plt.get_cmap('rainbow')
my_cmap.set_under('white')
data = np.array(sort_df.values)
cs = m.pcolormesh(x,y,data,cmap = my_cmap, vmin = 1)
m.colorbar(cs,extend = 'min')
plt.show()
The above code produces a plot that looks like:
The problem: There should only be colors on the lat/lon grid defined by sort_df.
Question: How do I make the grid appear in the correct spots on the map?
You made a missprint while convert lat-lon. Your arrays lats and lons are empty.
Another problem of your code is that data have to have shape of [nx-1, ny-1] to plot with pcolormesh (it draw between points):
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pandas import DataFrame
m = Basemap(llcrnrlon = -170, llcrnrlat = 10, urcrnrlon = -50, urcrnrlat = 80)
m.drawcoastlines()
m.drawstates()
lon = np.array([-95, -94, -93, -92, -91])
lat = np.array([43, 42, 41])
data = np.array([[0,4,8,3],[7,3,0,0]])
# you have to write just like here to convert coordinates
x,y = m(lon,lat)
my_cmap = plt.get_cmap('rainbow')
my_cmap.set_under('white')
cs = m.pcolormesh(x,y,data,cmap = my_cmap)
m.colorbar(cs, extend = 'min')
plt.show()

Drawing ellipses on matplotlib basemap projections

I am trying to draw ellipses on a basemap projection. To draw a circle like polygon there is the tissot function used to draw Tissot's indicatrix' as illustrates the following example.
from mpl_toolkits.basemap import Basemap
x0, y0 = 35, -50
R = 5
m = Basemap(width=8000000,height=7000000, resolution='l',projection='aea',
lat_1=-40.,lat_2=-60,lon_0=35,lat_0=-50)
m.drawcoastlines()
m.tissot(x0, y0, R, 100, facecolor='g', alpha=0.5)
However, I am interested in plotting an ellipsis in the form (x-x0)**2/a**2 + (y-y0)**2/2 = 1. On the other hand, to draw an ellipsis on a regular Cartesian grid I can use the following sample code:
import pylab
from matplotlib.patches import Ellipse
fig = pylab.figure()
ax = pylab.subplot(1, 1, 1, aspect='equal')
x0, y0 = 35, -50
w, h = 10, 5
e = Ellipse(xy=(x0, y0), width=w, height=h, linewidth=2.0, color='g')
ax.add_artist(e)
e.set_clip_box(ax.bbox)
e.set_alpha(0.7)
pylab.xlim([20, 50])
pylab.ylim([-65, -35])
Is there a way to plot an ellipsis on a basemap projection with the an effect similar to tissot?
After hours analyzing the source code of basemap's tissot function, learning some properties of ellipses and lot's of debugging, I came with a solution to my problem. I've extended the basemap class with a new function called ellipse as follows,
from __future__ import division
import pylab
import numpy
from matplotlib.patches import Polygon
from mpl_toolkits.basemap import pyproj
from mpl_toolkits.basemap import Basemap
class Basemap(Basemap):
def ellipse(self, x0, y0, a, b, n, ax=None, **kwargs):
"""
Draws a polygon centered at ``x0, y0``. The polygon approximates an
ellipse on the surface of the Earth with semi-major-axis ``a`` and
semi-minor axis ``b`` degrees longitude and latitude, made up of
``n`` vertices.
For a description of the properties of ellipsis, please refer to [1].
The polygon is based upon code written do plot Tissot's indicatrix
found on the matplotlib mailing list at [2].
Extra keyword ``ax`` can be used to override the default axis instance.
Other \**kwargs passed on to matplotlib.patches.Polygon
RETURNS
poly : a maptplotlib.patches.Polygon object.
REFERENCES
[1] : http://en.wikipedia.org/wiki/Ellipse
"""
ax = kwargs.pop('ax', None) or self._check_ax()
g = pyproj.Geod(a=self.rmajor, b=self.rminor)
# Gets forward and back azimuths, plus distances between initial
# points (x0, y0)
azf, azb, dist = g.inv([x0, x0], [y0, y0], [x0+a, x0], [y0, y0+b])
tsid = dist[0] * dist[1] # a * b
# Initializes list of segments, calculates \del azimuth, and goes on
# for every vertex
seg = [self(x0+a, y0)]
AZ = numpy.linspace(azf[0], 360. + azf[0], n)
for i, az in enumerate(AZ):
# Skips segments along equator (Geod can't handle equatorial arcs).
if numpy.allclose(0., y0) and (numpy.allclose(90., az) or
numpy.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. * numpy.pi / 360. * (az + 90.)
A = dist[0] * numpy.sin(azr)
B = dist[1] * numpy.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
This new function can be used promptly like in this example:
pylab.close('all')
pylab.ion()
m = Basemap(width=12000000, height=8000000, resolution='l', projection='stere',
lat_ts=50, lat_0=50, lon_0=-107.)
m.drawcoastlines()
m.fillcontinents(color='coral',lake_color='aqua')
# draw parallels and meridians.
m.drawparallels(numpy.arange(-80.,81.,20.))
m.drawmeridians(numpy.arange(-180.,181.,20.))
m.drawmapboundary(fill_color='aqua')
# draw ellipses
ax = pylab.gca()
for y in numpy.linspace(m.ymax/20, 19*m.ymax/20, 9):
for x in numpy.linspace(m.xmax/20, 19*m.xmax/20, 12):
lon, lat = m(x, y, inverse=True)
poly = m.ellipse(lon, lat, 3, 1.5, 100, facecolor='green', zorder=10,
alpha=0.5)
pylab.title("Ellipses on stereographic projection")
Which has the following outcome:

Categories

Resources