Plotting gridded data with cartopy using NorthPolarStereo - python

I'm moving from Basemap to Cartopy and want to plot data for the Arctic Ocean that covers the pole.
I've decided to use the NorthPolarStereo() projection and am happy to use either pcolormesh or contourf. Unfortunately my field of data doesn't show up when I execute the following code:
import cartopy.crs as ccrso
from netCDF4 import Dataset
def import_envisat_field(year,
month):
data_dir = f'/media/robbie/Seagate Portable Drive/Envisat_thickness/{year}/'
file = f'ESACCI-SEAICE-L3C-SITHICK-RA2_ENVISAT-NH25KMEASE2-{year}{month}-fv2.0.nc'
data = Dataset(data_dir+file)
return(data)
# Import data
data = import_envisat_field("2003","02")
# Make plot
fig = plt.figure(figsize=[10, 5])
ax = plt.axes(projection=ccrs.NorthPolarStereo())
ax.add_feature(cartopy.feature.OCEAN, zorder=0)
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='black')
extent = 2500000
ax.set_extent((-extent,
extent,
-extent,
extent),
crs=ccrs.NorthPolarStereo())
ax.gridlines()
lon = np.array(data['lon'])
lat = np.array(data['lat'])
field = np.array(data['sea_ice_thickness'])[0]
print(lon.shape,lat.shape,field.shape)
# This print command gives (432, 432) (432, 432) (432, 432)
plt.pcolormesh(lon, lat, field,zorder=2,
transform=ccrs.NorthPolarStereo())
plt.show()
The data plots in a straightforward way using Basemap, but executing the above code just gives me a nice picture of the Arctic ocean but without my data on it.
I've also tried replacing plt.pcolormesh with ax.pcolormesh but that didn't work either.
Cartopy output:
Basemap output with the same data:

If your data coordinates are latitude and longitude you need to use the PlateCarree transform:
plt.pcolormesh(lon, lat, field,zorder=2, transform=ccrs.PlateCarree())
The transform describes the data coordinates and is independent from the projection you'd like to plot on. See this guide in the Cartopy documentation for more details https://scitools.org.uk/cartopy/docs/latest/tutorials/understanding_transform.html

Related

Geopandas plot shapefile on xarray with same legend

I'm trying to create some maps of precipitation data (xarray) with a shapefile of the region of interest on top. However, when Python plots the figures, I get two seperate figures:
When I open the data in QGIS they do appear on top of each other, so the coordinate systems do check out. Then I have an additional bonus question: I have to create multiple precipitation maps, on for a visual analysis it would be ideal if I could have the same legend (thus the same min/max for the colorbar) for each map. Anyone an idea how to proceed further?
My code so far:
def chirps_to_map(input1, input2, title):
projection = input1 + input2
plt.figure(figsize=(9, 9))
projection['pr'].plot()
watershed.plot()
plt.title(title)
plt.show()
plt.close()
projection.to_netcdf(str(path)+str(title)+".nc")
return projection
This is a case where it's simpler to use the Matplotlib object-oriented API.
A nice general workflow might be
fig, ax = plt.subplot()
gdf.plot(ax=ax) # Plot the vector data on the subplot
raster.plot(ax=ax) # Plot the raster data on the same subplot
Example
First, we get some sample raster+vector data
import xarray as xr
import geopandas as gpd
import matplotlib.pyplot as plt
da = xr.tutorial.load_dataset('ROMS_example').zeta.isel(ocean_time=0)
gdf = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
usa = gdf.loc[gdf['name'].eq('United States of America')]
Next, we plot both of the data on the same AxesSubplot
fig, ax = plt.subplots(figsize=(15, 10))
da.plot.pcolormesh(x='lon_rho', y='lat_rho', ax=ax)
usa.plot(ax=ax, edgecolor='red', color='none')
# Focus on the raster extent
ax.set_xlim(-95, -87)
ax.set_ylim(26, 32)
Bonus: hvPlot way
hvPlot provides a nice unified API for interactive plotting with pandas, xarray, and many other libraries, and might be of interest to people stumbling upon this answer.
Plotting both vector and raster data is rather easy, simply use the * operator.
import hvplot.pandas
import hvplot.xarray
usa.hvplot(geo=True) * da.hvplot.quadmesh(x='lon_rho', y='lat_rho', geo=True)

3D elevation on geographical map with python

I am trying to display elevation/topography in 3D on a geographical map
I am currently displaying elevation with a colormap using the scatter function of matplolib over a geographical map created with the basemap package. I would like to visualize it in 3D with a shady effect or something similar.
Bellow is a simple example using data created randomly. The only constrain is to keep the 'ortho' look shown bellow. Any python package could be used.
Input data could either be a 1D arrays or 2D arrays.
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import numpy as np
size = 1000
# data to plot
data = np.arange(size)*0.5/size
# coordinates
lat = np.random.uniform(low=65, high=90, size=(size,))
lon = np.random.uniform(low=-180, high=180, size=(size,))
f1, ax = plt.subplots(1, 1,figsize=(9,8))
m = Basemap(projection='ortho',lat_0=70,lon_0=0,resolution='l',ax=ax)
m.drawcoastlines(linewidth=0.25, zorder=0)
m.drawparallels(np.arange(90,-90,-5), labels=[1,1,1,1],linewidth = 0.25, zorder=1)
m.drawmeridians(np.arange(-180.,180.,30.),labels=[1,1,1,1],latmax=85, linewidth = 0.25, zorder=1)
m.fillcontinents(color='dimgray',lake_color='grey', zorder=1)
x,y = m(lon,lat)
cmap='viridis'
m.scatter(x,y,c=data,s=10,cmap=cmap,vmin=0,vmax=0.5,zorder=3,alpha=1)
plt.show()
Thanks a lot,

Plotting coordinate data on top of a map in Python matplotlib

So, what I am having trouble with is how I am supposed to plot the data I have on top of a global map. I have an array of data, and two arrays of coordinates in latitude and longitude, where each datapoint was taken, but I am not sure of how to plot it on top of a global map. Creating the map itself is not too difficult, I just use:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
fig = plt.figure(figsize=(10, 8))
m = Basemap(projection='cyl', resolution='c',
llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, )
m.shadedrelief(scale=0.5)
m.drawcoastlines(color='black')
But the next step is where I am having problems. I have tried doing both a colormesh plot and scatter plot, but they haven't worked so far. How should I go about it so that the data is plotted in the correct coordinate locations for the global map?
Thanks a lot for any help!
Maybe a bit late, but I have this piece of code I used to plot multiple linear plot over a map in Basemap that worked for me.
map = Basemap(projection='cyl', resolution='c',
llcrnrlat=mins[1], urcrnrlat=maxs[1],
llcrnrlon=mins[0], urcrnrlon=50, )
plt.figure(figsize=(15, 15))
for i in range(1259):
filepath = filename[i]
data = pd.read_csv(filepath, index_col=0)
map.plot(data.x,data.y,'k-', alpha=0.1) ### Calling the plot in a loop!!
map.drawcoastlines(linewidth=1)
map.drawcountries(linewidth=0.5, linestyle='solid', color='k' )
plt.show()
The loop calls data from different folders, and I just use the map.plot command to plot. By doing it like that, you can plot all data in the same map.

Display geographical points using geopandas

I want to display points on the map using a shape file as a map and a csv with coordinates. The code works but I don't understand how to show the figure map.
My questions are: how to display the points? What is "WnvPresent"? How can i just display the map and the points, not as a split between negative and positive but as a hole?
Website from where i downloaded the shp file: https://ec.europa.eu/eurostat/web/gisco/geodata/reference-data/administrative-units-statistical-units/countries
Website from where the idea comes from: https://towardsdatascience.com/geopandas-101-plot-any-data-with-a-latitude-and-longitude-on-a-map-98e01944b972
import pandas as pd
import matplotlib.pyplot as plt
import descartes
import geopandas as gpd
from shapely.geometry import Point, Polygon
%matplotlib inline
#read map data in form of .shp
street_map = gpd.read_file(r"C:\Users\stetc\Desktop\images/portofolio\ref-countries-2016-01m.shp")
#create the map
fig,ax = plt.subplots(figsize=(15,15))
street_map.plot(ax = ax)
#read given data
df = pd.read.file(r"C:\Users\stetc\Documents\full_dataset.csv")
#the next step is to get the data in the right format. The way we do this is by turning our regular Pandas DataFrame into a geo-DataFrame, which will require us to specify as parameters the original DataFrame, our coordinate reference system (CRS), and the geometry of our new DataFrame. In order to format our geometry appropriately, we will need to convert the longitude and latitude into Points (we imported Point from shapely above), so first let’s read in the training data-set and specify the EPSG:4326 CRS like so
crs = {"init":"epsg:4326"}
#create points using longitude and lat from the data set
geometry = [Point(xy) for xy in zip (df["Longitude"], df["Latitude"])]
#Create a GeoDataFrame
geo_df =gpd.GeoDataFrame (df, #specify out data
crs=crs, # specify the coordinates reference system
geometry = geometry #specify the geometry list created
)
fig,ax = plt.subplots(figsize = (15,15))
street_map.plot (ax = ax, alpha = 0.4 , color="grey" )
geo_df[geo_df["WnvPresent"]==0].plot(ax=ax,markersize=20, color = "blue", marker="o",label="Neg")
geo_df[geo_df["WnvPresent"]==1].plot(ax=ax,markersize=20, color = "red", marker="o",label="Pos")
plt.legend(prop={"size":15})
WnvPresent is just a column used in the example to plot two different colours (I would do it differently, but that is for another discussion), you can ignore that if your goal is to plot points only.
Try the code below. I have also added zorder to ensure that points are on top of the street_map.
fig, ax = plt.subplots(figsize=(15,15))
street_map.plot(ax=ax, alpha=0.4, color="grey", zorder=1)
geo_df.plot(ax=ax, markersize=20, color="blue", marker="o", zorder=2)
In the first step you create the figure, then you add street_map to ax and then geo_df to the same ax. The last line answers your question "how to display the points?". Keep in mind that both layers has to be in the same CRS (assuming epsg 4326 from your code), otherwise layers won't overlap.
A bit more on plotting is in geopandas docs - https://geopandas.readthedocs.io/en/latest/mapping.html and on CRS here https://geopandas.readthedocs.io/en/latest/projections.html.

Projection Problems when Displaying an Image on a Map with Cartopy

I have some satellite image data I would like to display using Cartopy. I have successfully followed the image example detailed here. Resulting in this code:
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig = plt.figure(figsize=(12, 12))
img_extent = (-77, -59, 9, 26)
ax = plt.axes(projection=ccrs.PlateCarree())
# image data coming from server, code not shown
ax.imshow(img, origin='upper', extent=img_extent)
ax.set_xmargin(0.05)
ax.set_ymargin(0.10)
# mark a known place to help us geo-locate ourselves
ax.plot(-117.1625, 32.715, 'bo', markersize=7)
ax.text(-117, 33, 'San Diego')
ax.coastlines()
ax.gridlines()
plt.show()
This code generates the following image
My problem is that the satellite image data is not in the PlateCarree projection, but the Mercator projection.
But when I get the axis object with
ax = plt.axes(projection=ccrs.Mercator())
I lose the coastlines.
I saw the issue reported here. But
ax.set_global()
results in this image:
The data is not present, and San Diego is in the wrong location. Also the lat/lon extents have changed. What am I doing wrong?
Post Discussion Update
The main problem is that I had not properly specified the image extents in the target projection with the transform_points method. I also had to be specific about the coordinate reference system in the imshow method as Phil suggests. Here is the correct code:
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
proj = ccrs.Mercator()
fig = plt.figure(figsize=(12, 12))
extents = proj.transform_points(ccrs.Geodetic(),
np.array([-77, -59]),
np.array([9, 26]))
img_extents = (extents[0][0], extents[1][0], extents[0][6], extents[1][7] )
ax = plt.axes(projection=proj)
# image data coming from server, code not shown
ax.imshow(img, origin='upper', extent=img_extents,transform=proj)
ax.set_xmargin(0.05)
ax.set_ymargin(0.10)
# mark a known place to help us geo-locate ourselves
ax.plot(-117.1625, 32.715, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.text(-117, 33, 'San Diego', transform=ccrs.Geodetic())
ax.coastlines()
ax.gridlines()
plt.show()
Resulting in this correctly geoprojected satellite image:
Ideally, try to always be specific about the coordinate reference system your data are in when plotting with cartopy (via the transform keyword). This will mean you can just switch projections in your script and the data will automatically be put in the correct place.
So in your case, the plt.imshow should have a transform=ccrs.Mercator() keyword argument (you may need a a more specific parameterised Mercator instance). If your extents are in Geodetic (lats and lons) you will have to transform the bounding box into the mercator coordinates, but other than that, everything else should work as expected.
NOTE: I'm going to go and update the example to include the transform argument ;-) (PR: https://github.com/SciTools/cartopy/pull/343)
HTH

Categories

Resources