I am trying to create a stereographic plot using Basemap offset from the north pole, but the west-east directions are apparently reversed. Is this an error in my implementation, or a bug?
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
m = Basemap(projection='stere',
lat_0=90, lon_0=270, lat_ts=(90.+35.)/2.,
llcrnrlon=150,urcrnrlon=-60,llcrnrlat=50,urcrnrlat=50)
m.drawmeridians(np.arange(0,360,30),labels=[1,1,1,0])
m.drawparallels(np.arange(-90,90,5))
m.drawcoastlines()
m.shadedrelief()
plt.show()
Here is the result:
result from script
How might I reproduce the following map (which is offset-centred, and rotated?)
Restricted map
Using an azimuthal type of map projection always requires a set of proper parameters to get a good result. In this case, Stereographic projection centered at the north pole, its proper parameters are not what you usually use when implement with PlateCaree projection which is often used. Here is a working code that you may try.
# Stereographic projection coverage
# should be specified less than half of a hemisphere
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
m = Basemap(projection='stere', resolution='c',
lat_0=90, lon_0=270, lat_ts=(90.+35.)/2., width=15000000, height=10000000)
# (width, height) is the plot extents in meters
m.drawmeridians(np.arange(0, 360, 30), labels=[1,1,1,0])
m.drawparallels(np.arange(0, 90, 10), labels=[0,0,0,1])
m.drawcoastlines()
m.shadedrelief()
plt.show()
The resulting plot (map 1):
To get other part of the world into the plotting area is achieved by recentering the map.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
# projection center point
lon0 = 180
lat0 = 60
m = Basemap(projection='stere', resolution='c',
lat_0=lat0, lon_0=lon0, lat_ts=lat0, width=15000000, height=10000000)
m.drawmeridians(np.arange(0, 360, 30), labels=[1,0,0,1]) # left, right, top, bottom
m.drawparallels(np.arange(0, 90, 10), labels=[0,1,1,0])
m.drawcoastlines()
m.shadedrelief()
plt.show()
The output plot (map 2):
By specifying proper values of llcrnrlon, urcrnrlon, llcrnrlat, urcrnrlat, in Basemap() one can get the map extents as required. Here is another example of plot as requested by the OP.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(8,8))
m = Basemap(projection='stere', resolution='c',
lat_0=90, lon_0=-90, lat_ts=(90.+35.)/2.,
llcrnrlon=-142, urcrnrlon=78, llcrnrlat=19, urcrnrlat=45)
m.drawmeridians(np.arange(0, 360, 30), labels=[1,0,1,0]) # left, right, top, bottom
m.drawparallels(np.arange(0, 90, 10), labels=[0,1,0,1])
m.drawcoastlines()
m.shadedrelief()
plt.show()
The resulting plot (map 3):
Related
I am trying to retrieve the yaxis and xaxis tick positions from a cartopy geoaxes.
As far as I understand, a common matplotlib's Axes has the internal method: 'axes.get_xticks' and 'axes.get_yticks'.
Nevertheless, a cartopy's gridline from a geoaxes does not. How could I retrieve them?
Also, when I try to retrieve the ticks from a geoaxes using the common format (i.e.: "axes.get_yticks"), I end up with strange coordinates.
Here is an example.
import pandas as pd
pd.set_option('display.width', 50000)
pd.set_option('display.max_rows', 50000)
pd.set_option('display.max_columns', 5000)
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from matplotlib.offsetbox import AnchoredText
def main(projection = ccrs.Mercator(), drawlicense=True):
fig = plt.figure(figsize=(9,7))
ax = plt.axes(projection=projection)
# Put a background image on for nice sea rendering.
ax.stock_img()
# Create a feature for States/Admin 1 regions at 1:50m from Natural Earth
states_provinces = cfeature.NaturalEarthFeature(
category='cultural',
name='admin_1_states_provinces_lines',
scale='50m',
facecolor='none')
SOURCE = 'Natural Earth'
LICENSE = 'public domain'
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(states_provinces, edgecolor='gray')
# Add a text annotation for the license information to the
# the bottom right corner.
if drawlicense:
text = AnchoredText(r'$\mathcircled{{c}}$ {}; license: {}'
''.format(SOURCE, LICENSE),
loc='right',
bbox_transform=ax.transAxes,
bbox_to_anchor=(1.01, -0.02),
prop={'size': 8},
frameon=False)
ax.add_artist(text)
plt.show()
return ax
ax = main()
Gridliner = ax.gridlines(draw_labels=True)
In this above case, if I try to retrieve the yticks from the geoaxes "ax", I end up with an array of strange values as:
In: ax.get_yticks()
Out:
array([-20000000., -15000000., -10000000., -5000000., 0.,
5000000., 10000000., 15000000., 20000000.])
Notice that the values are not in degrees, though the figure and also the selected cartopy's projection states degree coordinates.
Therefore, what am I doing wrong? How can I get the respective degree coordinates of the map?
Sincerely,
The cartopy axes does not actually show normal matplotlib ticks. Instead you can use ax.gridlines to obtain a set of linecollections that show the grid. The returned cartopy.mpl.gridliner.Gridliner can be used to query the positions of the lines.
Note that projections are not necessarily separable in x and y, hence the gridlines could potentially be curves.
In the following we take the first points of those lines.
# create grid
gridliner = ax.gridlines(draw_labels=True)
# we need to draw the figure, such that the gridlines are populated
fig.canvas.draw()
ysegs = gridliner.yline_artists[0].get_segments()
yticks = [yseg[0,1] for yseg in ysegs]
xsegs = gridliner.xline_artists[0].get_segments()
xticks = [xseg[0,0] for xseg in xsegs]
print(xticks)
print(yticks)
This prints two lists with the first gridline point's coordinates:
[-180.0, -120.0, -60.0, 0.0, 60.0, 120.0]
[-80.0, -60.0, -40.0, -20.0, 0.0, 20.0, 40.0, 60.0, 80.0, 100.0]
I am trying to plot a netCDF4 file containing ocean currents from a NASA database for a project, but I keep getting errors such as "x and y coordinates are not compatible with the shape of the vector components".
I have tried changing the streamplot to a contourf (when I did it said that it needed to be a 2d array) which I tried to change but I could not get it to work.
import os
import matplotlib.pyplot as plt
from netCDF4 import Dataset as netcdf_dataset
import numpy as np
import cartopy.crs as ccrs
fname = "oscar_vel2019.nc.gz.nc"
data=netcdf_dataset(fname)
v = data.variables['v'][0, :, :, :]
vf = data.variables['vm'][0, :, :, :]
u = data.variables['u'][0, :, :, :]
uf = data.variables['um'][0, :, :, :]
lats = data.variables['latitude'][:]
lons = data.variables['longitude'][:]
ax = plt.axes(projection=ccrs.PlateCarree())
mymap=plt.streamplot(lons, lats, u, v, 60, transform=ccrs.PlateCarree())
ax.coastlines()
plt.show()
I would like it to work such that the ocean currents are visible on the plot and to show the movement of particles in the currents through an animation. I really don't have much knowledge with this which is why I am asking. Here is the link from which I got the file: https://podaac-opendap.jpl.nasa.gov/opendap/hyrax/allData/oscar/preview/L4/oscar_third_deg/oscar_vel2019.nc.gz.html
OK, I downloaded the data. The problem is that u and v are 4-dimensional, so you need to squeeze out the "depth" dimension. Cartopy also doesn't accept longitudes greater than 180, and you probably won't get away with stream plotting the whole thing. Also, density=60 will take forever...
This is ugly, but gives you the idea.
import xarray as xr
import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
with xr.open_dataset('/Users/jklymak/downloads/oscar_vel2019.nc.gz.nc') as ds:
print(ds)
ax = plt.axes(projection=ccrs.PlateCarree())
dec = 10
lon = ds.longitude.values[::dec]
lon[lon>180] = lon[lon>180] - 360
mymap=plt.streamplot(lon, ds.latitude.values[::dec], ds.u.values[0, 0, ::dec, ::dec], ds.v.values[0, 0, ::dec, ::dec], 6, transform=ccrs.PlateCarree())
ax.coastlines()
plt.show()
Question:
How to set up scaled axes with hvplot? [https://hvplot.pyviz.org/]
Code example:
I have the following code giving me the figure hereafter but the lat and long axes are not equal. How to have a 1:1 ratio between the two axes?
import os, sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import hvplot.pandas
pos = pd.read_csv(os.path.join('my_gps_positions.csv'))
pos.hvplot.scatter(*'lat lng'.split())
Here's the GPS file content:
And the resulting graph with unequal axes in my notebook:
You could adjust the width and the height parameter of the plot:
df.hvplot.scatter(x='lat', y='lon', width=500, height=500)
Or do you mean the range of the axes? They can be set by parameter xlim and ylim, for example:
df.hvplot.scatter(x='lat', y='lon', xlim=(6, 8), ylim=(45, 47))
Since you're plotting latitudes and longitudes you should definitely take a look at geoviews: http://geoviews.org/
longitude and latitude units should not be the same distance, except right on the equator. So normally you would plot these with hvplot as:
df.hvplot.points(x='lon', y='lat', geo=True, tiles='OSM')
but if you really want to force them to be the same, you can use aspect:
df.hvplot.scatter(x='lon', y='lat', aspect='equal')
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3,3,1000)
t = np.ones(1000)
f = x**2
plt.scatter(x,f) #plot1
plt.scatter(t,f) #plot2
plt.show()
I'd like to draw pile datas up.
when you look at upper exmaple, you can see two plot.plot1 is y=x^2 and plot2 is a line of perpendicular with x-axis. datas of plot2 are showing just a line all of datas. it means, i guess there is a section of more dense near the minimum point of plot1 (0,0).
but when you look at plot2 , it just show a line because of no density.
how can i fix this code?
Try axvline if you want a line that is perpendicular to the x-axis:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3,3,1000)
f = x**2
plt.scatter(x,f) #plot1
plt.axvline(1) #plot2
plt.show()
I am trying to plot a scatter plot on a background using basemap. But it's overwriting the background. How do I retain the background?
I am using this code
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='c')
m.bluemarble()
x, y = m(list(longitude), list(latitude))
plt.scatter(x,y,1,marker='o',color='Red')
plt.show()
But as soon as I run the scatter plot, its overwriting background image. How can I overlay the scatter plot on the image.
This is how to plot a series of points on top of a raster map. Note that the bluemarble image is huge, so a full scale (1.0 or default) plot of it should be avoided. The code is based on yours.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
# make up some data for scatter plot
lats = np.random.randint(-75, 75, size=20)
lons = np.random.randint(-179, 179, size=20)
fig = plt.gcf()
fig.set_size_inches(8, 6.5)
m = Basemap(projection='merc', \
llcrnrlat=-80, urcrnrlat=80, \
llcrnrlon=-180, urcrnrlon=180, \
lat_ts=20, \
resolution='c')
m.bluemarble(scale=0.2) # full scale will be overkill
m.drawcoastlines(color='white', linewidth=0.2) # add coastlines
x, y = m(lons, lats) # transform coordinates
plt.scatter(x, y, 10, marker='o', color='Red')
plt.show()
The resulting plot:
I realize it's an old question but in case anyone comes here with the same problem as I did.
The trick is to give a higher zorder for the scatter plot than for the .bluemarble().
m.scatter(x, y, 10, marker='o', color='Red', zorder=3)
More info here: https://matplotlib.org/3.1.0/gallery/misc/zorder_demo.html
I'm not entirely sure what you mean by "overwriting the background". When you use plt.scatter(), it will plot the points over the map, so it will display the points over the background.
Just based off the code provided, I think you're issue here is m(list(longitude), list(latitude)).
If you have multiple points in a list, you want to loop over them.
lats = [32, 38, 35]
lons = [-98, -79, -94]
x, y = m(lons, lats)
for i in range(len(lats)):
plt.scatter(x, y, marker = 'o')
If it's only one single point,
lat, lon = 32, -92
x, y = m(lon, lat)
plt.scatter(x, y, marker = 'o')
The styling of the points can be found in the matplotlib documentation.