Plotting equal area map around a given Lat-Long using Cartopy - python

I want to have a square map around a user input latitude and longitude.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.feature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
x=float(input('Enter latitude:'))
y=float(input('Enter longitude:'))
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([y-10,y+10,x-10,x+10],ccrs.PlateCarree())
ax.coastlines()
ax.gridlines(draw_labels=True)
plt.show()
It's a good plot
This works if I don't go towards the poles-
The sphericity of the earth is not taken count here and it stops at 90 degree -
If I change the projection to orthographic, the pole problem may be resolved.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.feature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
x=float(input('Enter latitude:'))
y=float(input('Enter longitude:'))
ax = plt.axes(projection=ccrs.Orthographic(y,x))
ax.set_extent([y-10,y+10,x-10,x+10],ccrs.PlateCarree())
ax.coastlines()
ax.gridlines(draw_labels=True)
plt.show()
I think the sphericity is taken into account, and I may get a good square plot, when I am near the equator. But, as I approach the poles, the square will start shrinking, and eventually I will not get a good area, as I have to take latitudes and longitudes from that square area, and calculate some value with them.
For instance, here are two plots below
plot at lat=80 long = 60
This picture clarifies the question. I need the diametrically opposite side also, so the latitude should again start to decrease from 90 towards 0, but then the longitudes will change again. In this way, I am going nowhere and stuck.
Plot at lat = 22 and long = 78
The 1st one becomes rectangular, and the 2nd one a square. How can I make them equal area with any given lat-long?
Even if I change the projection for the set_extent to LCC or any other, there is no way I can get to set the extent in terms of the center.

The solution is rather simple, I was completely misinterpreting how the Orthographic projection works. Projecting and setting the extent both in Orthographic projection will work fine. Here is the solution -
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
x=float(input('Enter latitude:'))
y=float(input('Enter longitude:'))
ax = plt.axes(projection=ccrs.Orthographic(y,x))
ax.set_extent((-1000000,1000000,-1000000,1000000),ccrs.Orthographic(y,x))
ax.coastlines('50m')
ax.gridlines(draw_labels=True)
plt.show()
plot at lat = 90 and long = 180
plot at lat=22 and long = 88
Both the maps clearly covers same area around the given latitude, longitude values.
Thanks!!!

Related

Polar pcolormesh shifts center when used set_ylim in matplotlib

Although I am providing a excerpt of the code I am using, but this piece contains the problem I am facing. I am trying to plot density of the particles over the disc and hence polar plot seems natural to use. So I have used following piece of code to read a density file which contains the density with rows and column representing radius and angular direction.
#! /usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
from os.path import exists
from os import sys
import matplotlib as mpl
from matplotlib import rc
NUMBINS=100
rmax=20.0
dR2=rmax*rmax/NUMBINS
density = np.random.random((NUMBINS, NUMBINS))
r = np.sqrt(np.arange(0,rmax*rmax,dR2) )[:NUMBINS]
theta = np.linspace(0,2*np.pi,NUMBINS)
mpl.rcParams['legend.fontsize'] = 10
mpl.rcParams['pcolor.shading'] ='nearest'
fig = plt.figure(figsize=(5, 5))
ax1 = plt.subplot(111,projection="polar")
rad, th = np.meshgrid(r,theta)
ax1.set_yticks(np.arange(0,rmax,3))
ax1.pcolormesh(th,rad,density,cmap='Blues')
#ax1.set_ylim([rad[0,0], rad[0,NUMBINS-1]])
plt.tight_layout()
plt.show()
which gives me the following plot :
As you can see that the radius starts from 0 to rmax, removing the commented line
ax1.set_ylim([rad[0,0], rad[0,NUMBINS-1]])
shall not have any effect on the plot but it shifts the center of the plot :
I don't understand why setting ymin=0 creates this white space in the center?
Turns out that it is a problem with version of matplotlib. I tried a different version and the plot works as expected. Apologies for not trying it earlier.

Matplotlib Mollweide/Hammer projection: region of interest only

I was wondering if there's a way to only show the region of interest from a plot based on Mollweide/Hammer projection in basemap (matplotlib).
I am trying to set the plot-bounds roughly to the Pacific plate, as in the link below. However, the set_xlim and set_ylim functions do not seem to have any effect. Thanks in advance for any guidance.
http://geology.gsapubs.org/content/29/8/695/F1.large.jpg
From the documentation, both Hammer and Mollweide projections don't allow this as they print out entire world maps. Here's some code using Polyconic projection, but it is bounded by straight lines. The trick here is to define the corner longitude and latitudes on creation.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
my_map = Basemap(projection='poly', lat_0=0, lon_0=-160,
resolution = 'h', area_thresh = 0.1,
llcrnrlon=140, llcrnrlat=-60,
urcrnrlon=-100, urcrnrlat=60)
plt.figure(figsize=(16,12))
my_map.drawcoastlines()
my_map.drawcountries()
my_map.fillcontinents(color='coral', lake_color='aqua')
my_map.drawmapboundary(fill_color='aqua')
my_map.drawmeridians(np.arange(0, 360, 20))
my_map.drawparallels(np.arange(-90, 90, 10))
plt.show()
Result:
Note that this effectively shows less area than the one in the picture you provided.

Plotting at boundaries using matplotlib-basemap

I have difficulties in plotting e.g. polygons across the boundaries of a map generated using matplotlib-basemap. In the example below, the map boundary is specified by the dateline. I try to plot a triangle across the dateline by specifying the coordinates of vertices of a triangle. This works fine when all coordinates are within the map, but if they go across the map boundary, basemap performs strange extrapolation, as it seems not to know how to draw the rectangles in the right way.
Right way would mean in my sense that the triangle is drawn until the map boundary and would then continue at the other side of the map.
Below is a minimum code example and a figure illustrating the general problem.
Any ideas how to solve this problem in a general way are highly welcome.
from mpl_toolkits.basemap import Basemap
import matplotlib.pylab as plt
import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches
import matplotlib as mpl
from matplotlib.collections import PatchCollection
![plt.close('all')
Path = mpath.Path
fig=plt.figure(); ax=fig.add_subplot(121); ax1=fig.add_subplot(122)
def do_plot(ax,lons,lats,title):
patches=\[\]
m = Basemap(projection='robin', resolution='c',lon_0=0.,ax=ax) #todo: how to make it properly work for other projections ???
m.drawmapboundary(fill_color='grey')
m.drawcoastlines()
#--- generate first sample with no problem
x,y=m(lons,lats)
verts = np.asarray(\[x,y\]).T
codes = \[Path.MOVETO,Path.LINETO,Path.LINETO\]
patches.append(mpatches.PathPatch(mpath.Path(verts, codes,closed=True)))
#--- generate collection
cmap = plt.cm.get_cmap('jet', 50); norm = mpl.colors.Normalize(vmin=None, vmax=None) #colorbar mapping
collection = PatchCollection(patches, cmap=cmap,norm=norm, alpha=1.,match_original=False) #construct library of all objects
colors = np.asarray(np.random.random(len(patches)))
collection.set_array(np.array(colors)) #assign data values here
#--- do actual plotting
im=m.ax.add_collection(collection)
ax.set_title(title)
do_plot(ax,\[-10.,0.,20.\],\[30.,50.,20.\],'This works')
do_plot(ax1,\[170,180,-175\],\[30.,50.,20.\],'... and here is the boundary problem')
plt.show()][1]
You cannot get around this problem with Basemap in a simple way. In your line x,y=m(lons,lats) you have transformed the points to map coordinates, and drawing the polygon just draws between those projected points.
You might try using Cartopy, which can do this. This example may help.

Orthographic projection Python

I use orthographic projection to plot maps.
I use this programm:
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
import os, sys
from sys import argv
import pylab
from mpl_toolkits.basemap import Basemap, shiftgrid
from matplotlib import mpl
from matplotlib import rcParams
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import matplotlib.patches as patches
import matplotlib.path as path
import matplotlib.dates as dt
from numpy import linalg
import netCDF4
import time
import datetime as d
import sys
import math
from mpl_toolkits.axes_grid1 import make_axes_locatable
from pylab import *
nc = netCDF4.Dataset ('tt.nc')
latvar = nc.variables['lat']
lat = latvar[:]
lon = nc.variables['lon'][:]
lat_0=30;lon_0=-25
m1 = Basemap(projection='ortho',lon_0=-25,lat_0=30,resolution='l')
m = Basemap(projection='ortho',lon_0=lon_0,lat_0=lat_0,resolution='l',\
llcrnrx=0.,llcrnry=0.,urcrnrx=m1.urcrnrx/2.,urcrnry=m1.urcrnry/2.)
X, Y = m(lon, lat)
O_x_1=nc.variables['O3']
h=9
lev=0
minOzone=0
maxOzone=40
plotOzone = m.pcolor(X,Y,O_x_1[h,lev,:,:],vmin=minOzone,vmax=maxOzone)
ax=colorbar(plotOzone, shrink=0.8,norm=(0,40))
m.drawcoastlines()
m.drawparallels(np.arange(-90.,120.,30.))
m.drawmeridians(np.arange(0.,420.,60.))
plt.show()
What do I have to do to center my map
on Europe ?
I have already played with lat_0 and lon_0 but that doesn't
give what I want...
I can't add figures to show what I obtained and
what I would like...
Thank you!
The lat_0 and lon_0 are for setting the origin of the projection, not the extent of the map. Usually the place with the least distortions so you dont want the origin to deviate too much from the center of your area of interest. Basemap will automatically center the map around the origin if you dont specify an extent.
Centering the map (different from its origin) can be done if you know what extent (or boundingbox) you want to use. If you know the corner coordinates in your 'ortho' projection you could use the keywords from your example (llcrnrx etc.). I have had no luck with the 'llcrnrlon' keywords in Basemap 1.0.6, they seem to suggest that you can input the coordinates of your extent in geographic (lat/lon).
An alternative is to grab the axes and manually set an x- and y limit. A benefit is that you can do it after declaring the Basemap object which you can then use for coordinate transformation. An example:
from mpl_toolkits.basemap import Basemap
fig = plt.figure(figsize=(5,5))
m = Basemap(projection='ortho',lon_0=5,lat_0=35,resolution='l')
m.drawcoastlines()
m.drawparallels(np.arange(-90.,120.,15.))
m.drawmeridians(np.arange(0.,420.,30.))
# your extent in lat/lon (dec degrees)
ulx = -10
uly = 65
lrx = 65
lry = 35
# transform coordinates to map projection
xmin, ymin = m(ulx, lry)
xmax, ymax = m(lrx, uly)
# set the axes limits
ax = plt.gca()
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
Make sure the projection in the map declaration suites your needs, i have just picked an origin which falls within Europe.

Stereographic Sun Diagram matplotlib polar plot python

I am trying to create a simple stereographic sun path diagram similar to these:
http://wiki.naturalfrequency.com/wiki/Sun-Path_Diagram
I am able to rotate a polar plot and set the scale to 90. How do I go about reversing the y-axis?
Currently the axis goes from 0>90, how do I reverse the axis to 90>0 to represent the azimuth?
I have tried:
ax.invert_yaxis()
ax.yaxis_inverted()
Further, how would I go about creating a stereographic projection as opposed to a equidistant?
My code:
import matplotlib.pylab as plt
testFig = plt.figure(1, figsize=(8,8))
rect = [0.1,0.1,0.8,0.8]
testAx = testFig.add_axes(rect,polar=True)
testAx.invert_yaxis()
testAx.set_theta_zero_location('N')
testAx.set_theta_direction(-1)
Azi = [90,180,270]
Alt= [0,42,0]
testAx.plot(Azi,Alt)
plt.show()
Currently my code doesn't seem to even plot the lines correctly, do I need need to convert the angle or degrees into something else?
Any help is greatly appreciated.
I finally had time to play around with matplotlib. After much searching, the correct way as Joe Kington points out is to subclass the Axes. I found a much quicker way utilising the excellent basemap module.
Below is some code I have adapted for stackoverflow. The sun altitude and azimuth were calculated with Pysolar with a set of timeseries stamps created in pandas.
import matplotlib.pylab as plt
from mpl_toolkits.basemap import Basemap
import numpy as np
winterAzi = datafomPySolarAzi
winterAlt = datafromPySolarAlt
# create instance of basemap, note we want a south polar projection to 90 = E
myMap = Basemap(projection='spstere',boundinglat=0,lon_0=180,resolution='l',round=True,suppress_ticks=True)
# set the grid up
gridX,gridY = 10.0,15.0
parallelGrid = np.arange(-90.0,90.0,gridX)
meridianGrid = np.arange(-180.0,180.0,gridY)
# draw parallel and meridian grid, not labels are off. We have to manually create these.
myMap.drawparallels(parallelGrid,labels=[False,False,False,False])
myMap.drawmeridians(meridianGrid,labels=[False,False,False,False],labelstyle='+/-',fmt='%i')
# we have to send our values through basemap to convert coordinates, note -winterAlt
winterX,winterY = myMap(winterAzi,-winterAlt)
# plot azimuth labels, with a North label.
ax = plt.gca()
ax.text(0.5,1.025,'N',transform=ax.transAxes,horizontalalignment='center',verticalalignment='bottom',size=25)
for para in np.arange(gridY,360,gridY):
x= (1.1*0.5*np.sin(np.deg2rad(para)))+0.5
y= (1.1*0.5*np.cos(np.deg2rad(para)))+0.5
ax.text(x,y,u'%i\N{DEGREE SIGN}'%para,transform=ax.transAxes,horizontalalignment='center',verticalalignment='center')
# plot the winter values
myMap.plot(winterX,winterY ,'bo')
Note that currently I am only plotting points, you will have to make sure that line points have a point at alt 0 at sunrise/sunset.

Categories

Resources