defining a partial geostationary projection with matplotlib Basemap - python

i'm having trouble creating a custom geostationary projection with Basemap. what i'd need is not a full globe projection, but the upper third of a full disc (full width, one third from the top down ). i found an example here:
http://matplotlib.org/basemap/users/geos.html
explaining how to make a projection for the upper right quadrant of the disc using the llcrnrx, llcrnry, urcrnrx, and urcrnry keywords. following the example, i thought i would get my result like this:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
lon_0 = 9.5
fig = plt.figure()
m1 = Basemap(projection='geos',lon_0=lon_0,resolution=None)
m = Basemap(projection='geos',lon_0=lon_0,resolution='l',
# lower left
llcrnrx=0., llcrnry=0.,
# calculate the upper right coordinates, full width
# two 3rds of the height
urcrnrx=m1.urcrnrx, urcrnry=m1.urcrnry-m1.urcrnry/3.)
m.drawcoastlines()
plt.show()
which gives me a weird plot. from the example i though that for the new projection the lower left corner is always (0,0), then you just calculate the width and height coordinates (upper right). this obviously is not the case. with just bare experimentation i have this now:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
lon_0 = 9.5
fig = plt.figure()
m1 = Basemap(projection='geos',lon_0=lon_0,resolution=None)
m = Basemap(projection='geos',lon_0=lon_0,resolution='l',
llcrnrx=-m1.urcrnrx/2., llcrnry=m1.urcrnry/4.,
urcrnrx=m1.urcrnrx/2., urcrnry=m1.urcrnry/2.)
m.drawcoastlines()
plt.show()
which gives me a rough estimate on what i'm after, but i have no idea why this works and what the corner points really represent.
i hope you guys get the drift...

Related

pyplot drawing on an angle

I'm trying to draw a couple of vectors with pyplot.
My approach is to define first the plot size (or axis values), as this will change depending on the job run.
pretty much what i need to do is to draw a straight, upwards-facing vector.
So for the test i am using the same values for x-axis, and two different ones for Y (as that would give me an vertical vector)
problem I face is - whatever i am doing, i can't seem to make the vector straight. It always gets rotated.
Does anyone know what reason there might be for it ?
my current code
import matplotlib.pyplot as plt
import math
start = (162305,299400,162890,299400) #define coordinates for vector
#define plot axis values
top = 163440
bottom = 161910
left = 299595
right = 300510
fig, ax = plt.subplots()
ax.quiver(start[0],start[1],start[2],start[3])
plt.xlim(left,right)
plt.ylim(bottom,top)
plt.autoscale()
plt.show()
and the resulting plot
Hope this would help!
import matplotlib.pyplot as plt
import math
start = (162305,299400,162890,299400) #define coordinates for vector
#define plot axis values
top = 163440
bottom = 161910
left = 299595
right = 300510
fig, ax = plt.subplots()
ax.quiver(start[0],start[1],1,0)
#ax.quiver(x_pos, y_pos, x_dir, y_dir, color)
#give the x_pos, y_pos value
#x_dir=1, y_dir=1, meaning 45 degree angle
#x_dir=1, y_dir=0, meaning 0 degree angle
#x_dir=0, y_dir=1, meaning 90 degree angle
# you put x_dir=start[2] and y_dir=start[3], making an arbitary angle of atan
#(y_dir/x_dir) which makes the angled arrow
plt.xlim(left,right)
plt.ylim(bottom,top)
plt.autoscale()
plt.show()

How to plot an ortographic projection of the celestial sphere with equatorial coordinates in python, for a given latitude?

I am trying to obtain an ortographic projection of the celestial sphere, with equatorial coordinates, as seen from a certain latitude, as in the following picture:
(Grid obtained from Skychart/Cartes du ciel)
This image is a print of Skychart/Cartes du ciel, showing the equatorial grid for an observer at 23°S latitude. I want to be able to reproduce the exact same image in Python (apart from the dark blue background). My first attempt was to use CartoPy, setting the central latitude as -23, as follows:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
ax = plt.axes(projection=ccrs.Orthographic(central_latitude=-23))
ax.gridlines()
plt.show()
but the resulting picture looks like this:
From the position of the south pole, I believe setting the central latitude to the observer's latitude in CartoPy does not solve my problem. Is there another way, either with CartoPy or another package (maybe AstroPy? - I have never used it) to obtain the same plot as Skychart (Image 1) in python?
First of all, your first image is Azimuthal Equidistant Projection. So that, it is quite different from your second plot (Orthographic projection). To get the plot (first image) like that using Cartopy requires some steps that are interesting to follow. Here is the code with comments that produces the output plot that I consider a good result.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.path as mpath
import numpy as np
r_limit = 20037508 #from: ax.get_ylim() of full extent
# this makes circle for clipping the plot
pts = [] #unit circle vertices
cds = [] #path codes
numps = 32
for ix,ea in enumerate(np.linspace(0, 2*np.pi, numps)):
#print(ea)
xi = np.cos(ea)
yi = np.sin(ea)
pts.append([xi,yi])
if (ix==0):
# start
cds.append(1)
elif (ix==numps-1):
# close
cds.append(79)
else:
cds.append(4)
# make them np.array for easy uses
vertices = np.array(pts)
codes = np.array(cds)
# manipulate them to create a required clip_path
scale = r_limit*0.5
big_ccl = mpath.Path(vertices*scale, codes)
clippat = plt.Polygon(big_ccl.vertices[:, :], visible=True, fill=False, ec='red')
# create axis to plot `AzimuthalEquidistant` projection
# this uses specific `central_latitude`
ax = plt.axes(projection=ccrs.AzimuthalEquidistant(central_latitude=-23))
# add the clip_path
ax.add_patch(clippat)
# draw graticule (of meridian and parallel lines)
# applying clip_path to get only required extents plotted
ax.gridlines(draw_labels=False, crs=ccrs.PlateCarree(),
xlocs=range(-180,180,30), ylocs=range(-80,90,20), clip_path=clippat)
# specify radial extents, use them to set limits of plot
r_extent = r_limit/(2-0.05) # special to the question
ax.set_xlim(-r_extent, r_extent)
ax.set_ylim(-r_extent, r_extent)
ax.set_frame_on(False) #hide the rectangle frame
plt.show()

change location of polar bar on map-python

I have map with locations in that area. I want to add 1/6 circle starting from each point in a specific direction so I decided to use polar plot with only one bar.
My locations is of the type(lat,lon) but the code accept location with only one parameter
I saw the same code all over the internet but still after reading the documantation its not working.
import numpy as np
import matplotlib.pyplot as plt
#I removed this line
plt.axes([0,0,1,1])
N = 20
theta = np.arange(0.0, 2*np.pi, 2*np.pi/N)
radii = 10*np.random.rand(N)
width = np.pi/4*np.random.rand(N)
bars = plt.bar(theta, radii, width=width, bottom=0.0)
for r,bar in zip(radii, bars):
bar.set_facecolor( cm.jet(r/10.))
bar.set_alpha(0.5)
plt.show()
right now, when runing it, I don't see a thing eventhough ax belong to my graph

Matplotlib plot has slanted lines

I'm trying to plot projections of coordinates onto a line, but for some reason, Matplotlib is plotting the projections in a slightly slanted manner. Ideally, I would like the (blue) projections to be perpendicular to the (green) line. Here's an image of how it looks with sample data:
As you can see, the angles between the blue lines and the green line are slightly obtuse instead of right. I tried playing around with the rotation parameter to the annotate function, but this did not help. The code for this plot is below, although the data might look a bit different since the random generator is not seeded:
import numpy as np
import matplotlib.pyplot as plt
prefs = {'color':'purple','edgecolors':'black'}
X = np.dot(np.random.rand(2,2), np.random.rand(2,50)).T
pts = np.linspace(-1,1)
v1_m = 0.8076549717643662
plt.scatter(X[:,0],X[:,1],**prefs)
plt.plot(pts, [v1_m*x for x in pts], color='lightgreen')
for x,y in X:
# slope of connecting line
# y = mx+b
m = -np.reciprocal(v1_m)
b = y-m*x
# find intersecting point
zx = b/(v1_m-m)
zy = v1_m*zx
# draw line
plt.annotate('',(zx,zy),(x,y),arrowprops=dict(linewidth=2,arrowstyle='-',color='lightblue'))
plt.show()
The problem lies in the unequal axes which makes it look like they are not at a right angle. Use plt.axis('equal') to have equal axis spans on x- and y-axis and a square figure with equal height and width. plt.axis('scaled') works the same way. As pointed out by #CedricZoppolo, you should set the equal aspect ratios before plt.show(). As per docs, setting the aspect ratio to "equal" means
same scaling from data to plot units for x and y
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,8))
# Your code here
plt.axis('equal')
plt.show()
Choosing a square figure is not necessary as it works also with rectangular figures as
fig = plt.figure(figsize=(8,6))
# Your code here
plt.axis('equal')
plt.show()
The blue lines not being perpendicular is due to axis not being equal.
You just need to add below line before plt.show()
plt.gca().set_aspect('equal')
Below you can see the resulted graph:

Matplotlib: Focus on specific lon/lat using spstere projection

I am trying to focus my map on a specific area of Antarctica using 'spstere' projection from the matplotlib package in Python. I am able to plot the whole of Antarctica but this time I want to 'zoom' in and have a closer look at a specific area of the continent.
Similar examples using other projections (Pyplot contour plot - clabel spacing; http://matplotlib.org/basemap/api/basemap_api.html; https://matplotlib.org/basemap/users/examples.html) are available online but I have not been able to apply those to the 'spstere' projection over Antarctica.
I basically want to focus my map on the region of the Antarctic Peninsula, which spans roughly from
llcrnrlon=-100,urcrnrlon=-30,llcrnrlat=-90,urcrnrlat=-55.0
I have tried to use this code with the 'spstere' proj but python only takes into account boundinglat and lon_0. I've tried to change the values for boundinglat and lon_0 but it does not work either.
Any idea how I could go about? I have also tried using other projections such as 'cyl' but instead of getting a nice square like the 'spstere' proj, I get a horizontal rectangle.
m = Basemap(projection='cyl',lon_0=0,lat_0=0,\
llcrnrlon=-180,urcrnrlon=180,llcrnrlat=-90,urcrnrlat=-55.0,resolution='c')
Any help would be highly appreciated!
Using the Polar Stereographic Projection 'spstere', you can get the antarctic region by using e.g. boundinglat=-60:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
m = Basemap(projection='spstere',boundinglat=-60,lon_0=180,resolution='c')
m.drawcoastlines()
plt.show()
Note that 'spstere' is always centered at the south pole.
In order to have a map, which is not centered at the south pole, you need to use the "stere" projection. Setting the corners for the "stere" projection is not straigt forward.
One may therefore use a plot in 'spstere' projection and find some points which would enclose the region of interest. In this case e.g.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
m = Basemap(projection='spstere',boundinglat=-50,
lon_0=180+(-100+-30)/2.,resolution='c')
m.drawmeridians(np.arange(0,360,30),labels=[1,1,1,0])
m.drawparallels(np.arange(-90,90,5))
m.drawcoastlines()
xll, yll = m(-150,-70) # <-- find those points by looking at meridians and parallels
xur, yur = m(-30,-55)
m.scatter([xll,xur], [yll, yur], c="crimson")
plt.show()
Using those points, (-150,-70, -30,-55), as the corners of the map, you can then plot a map using 'stere' projection.
m = Basemap(projection='stere',resolution='c',
lat_0=-90, lon_0=(-100+-30)/2., lat_ts=(-90.+-55.)/2.,
llcrnrlon=-150,urcrnrlon=-30,llcrnrlat=-70,urcrnrlat=-55)
If this heuristic method is not wanted, you may automate this procedure by creating a dummy map in 'spstere' projection, calculate the coordinates from the rectangle in question (llcrnrlon=-100,urcrnrlon=-30,llcrnrlat=-90,urcrnrlat=-55.0) and create a new basemap in stere projection with them. The function below is taken from the ActiveState site (author PG).
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
def polar_stere(lon_w, lon_e, lat_s, lat_n, **kwargs):
'''Returns a Basemap object (NPS/SPS) focused in a region.
lon_w, lon_e, lat_s, lat_n -- Graphic limits in geographical coordinates.
W and S directions are negative.
**kwargs -- Aditional arguments for Basemap object.
'''
lon_0 = lon_w + (lon_e - lon_w) / 2.
ref = lat_s if abs(lat_s) > abs(lat_n) else lat_n
lat_0 = np.copysign(90., ref)
proj = 'npstere' if lat_0 > 0 else 'spstere'
prj = Basemap(projection=proj, lon_0=lon_0, lat_0=lat_0,
boundinglat=0, resolution='c')
lons = [lon_w, lon_e, lon_w, lon_e, lon_0, lon_0]
lats = [lat_s, lat_s, lat_n, lat_n, lat_s, lat_n]
x, y = prj(lons, lats)
ll_lon, ll_lat = prj(min(x), min(y), inverse=True)
ur_lon, ur_lat = prj(max(x), max(y), inverse=True)
return Basemap(projection='stere', lat_0=lat_0, lon_0=lon_0,
llcrnrlon=ll_lon, llcrnrlat=ll_lat,
urcrnrlon=ur_lon, urcrnrlat=ur_lat, **kwargs)
llcrnrlon=-100
urcrnrlon=-30
llcrnrlat=-90
urcrnrlat=-55.0
m = polar_stere(llcrnrlon, urcrnrlon, llcrnrlat, urcrnrlat)
m.drawmeridians(np.arange(0,360,30),labels=[1,1,1,0])
m.drawparallels(np.arange(-90,90,30),labels=[1,1,1,1])
m.drawcoastlines()
plt.show()

Categories