How to add a point-feature shapefile to map using cartopy - cartopy

I have two shapefiles. One is a point feature shapefile, named "point.shp", the other is a polygon shapefile named "polygon.shp". Both I want to add to a map using cartopy.
I managed to add the "polygon.shp", but failed with the "point.shp".
Here's my code:
import matplotlib.pyplot as plt
from cartopy import crs
from cartopy.io.shapereader import Reader
from cartopy.feature import ShapelyFeature
ax = plt.axes(projection=crs.PlateCarree())
# add the polygon file, worked
ax.add_geometries(Reader("polygon.shp").geometries(), crs.PlateCarree(), facecolor='w')
# or(also worked):
ax.add_feature(ShapelyFeature(Reader("polygon.shp").geometries(), crs.PlateCarree(), facecolor='r'))
# but these two ways both failed with the "point.shp"
ax.add_geometries(Reader("point.shp").geometries(), crs.PlateCarree())
# or, this doesn't work neither:
ax.add_feature(ShapelyFeature(Reader("polygon.shp").geometries(), crs.PlateCarree(), facecolor='r'))
Does any one know how to do this, or why, without retrieving all the points' x, y coords and then plotting them?
And with coordinates(x, y values), ax.plot() works, but ax.scatter() fails, why?
Thanks

add_geometries currently turns a geometry into a polygon and then colours it appropriately, which of course means that when you pass points the add_geometries, the polygons are not visible. Potentially cartopy could do a better job of this in the future, but in the meantime, it sounds like you just want to use something like scatter to visualize your data.
You can achieve this by getting the x and y coordinate values out of the geometry and passing these straight on to scatter with the appropriate transform:
import cartopy.crs as ccrs
import cartopy.io
import matplotlib.pyplot as plt
fname = cartopy.io.shapereader.natural_earth(resolution='10m',
category='cultural',
name='populated_places_simple')
plt.figure(figsize=(12, 6))
ax = plt.axes(projection=ccrs.Robinson())
ax.set_title('Populated places of the world.')
ax.coastlines()
points = list(cartopy.io.shapereader.Reader(fname).geometries())
ax.scatter([point.x for point in points],
[point.y for point in points],
transform=ccrs.Geodetic())
plt.show()
HTH

Related

Is there a way to draw shapes on a python pandas plot

I am creating shot plots for NHL games and I have succeeded in making the plot, but I would like to draw the lines that you see on a hockey rink on it. I basically just want to draw two circles and two lines on the plot like this.
Let me know if this is possible/how I could do it
Pandas plot is in fact matplotlib plot, you can assign it to variable and modify it according to your needs ( add horizontal and vertical lines or shapes, text, etc)
# plot your data, but instead diplaying it assing Figure and Axis to variables
fig, ax = df.plot()
ax.vlines(x, ymin, ymax, colors='k', linestyles='solid') # adjust to your needs
plt.show()
working code sample
import pandas as pd
import matplotlib.pyplot as plt
import seaborn
from matplotlib.patches import Circle
from matplotlib.collections import PatchCollection
df = seaborn.load_dataset('tips')
ax = df.plot.scatter(x='total_bill', y='tip')
ax.vlines(x=40, ymin=0, ymax=20, colors='red')
patches = [Circle((50,10), radius=3)]
collection = PatchCollection(patches, alpha=0.4)
ax.add_collection(collection)
plt.show()

how to remove all indicators from pyplot.polar

i need to make a polar plot with just the main data content visible.
for now i have managed to get the following image by using these simple codes.
but there is still one outline circle left around it. how can i remove it
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
data = np.random.randint(1800,2200,(24*60))
data = list(data)
data.append(data[0])
print(data)
theta = np.arange(0,360+360/(24*60),360/(24*60))*np.pi/180
plt.polar(theta, data)
plt.xticks([])
plt.yticks([])
plt.savefig("p.png")
plt.show()
This should do the trick:
plt.box(on=None)
Solution inspired from the Q: Removing frame while keeping axes in pyplot subplots

How to control the axis units in a map made with astropy and matplotlib?

When using astropy and matplotlib to create a map, the units in the right ascension axis are deg/min/sec, instead of h/m/s. I do not find an easy way in astropy to select the units h/m/s.
For example, if I try to reproduce the map of the Horsehead nebula as in the documentation of astropy.wcs, I get a R.A. axis in deg/min/sec.
The code is simply:
from matplotlib import pyplot as plt
from astropy.io import fits
from astropy.wcs import WCS
from astropy.utils.data import get_pkg_data_filename
filename = get_pkg_data_filename('tutorials/FITS-images/HorseHead.fits')
hdu = fits.open(filename)[0]
wcs = WCS(hdu.header)
fig = plt.figure()
fig.add_subplot(111, projection=wcs)
plt.imshow(hdu.data, origin='lower', cmap=plt.cm.viridis)
plt.xlabel('RA')
plt.ylabel('Dec')
plt.show()
It is supposed to produce this:
correct units
but I get that:
wrong units
You can use:
ax = fig.gca()
ra = ax.coords[0]
ra.set_format_unit('hour')
e.g. as specified here: http://docs.astropy.org/en/stable/visualization/wcsaxes/controlling_axes.html
However, when I ran the same example, it defaulted to hours, so I'm not sure what configuration you have set that defaulted to degrees instead.

Cartopy AzimuthalEquidistant projection: zooming into a region and coastlines

I am trying to plot some data on an AzimuthalEquidistant projection using cartopy. However, it gives me a couple of problems. First the coastlines no longer show for this type of projection. Not sure if this is my code or a Cartopy problem. I also notice that if I use a ccrs.PlateCarree() transform in the pcolormesh command the coastlines do show but then, presumably, my data is on the wrong type of prejection?
Second I would prefer if the axis boarder was circular after plotting the data, is it possible to use set_extent or some similar function to do this?
The code below should reproduce the problems, the circle shows how I would like the boarder to look.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
clat = 55.0
clon = -8.0
lons = np.arange(clon-15,clon+16,0.5)
lats = np.arange(clat-15,clat+16,0.5)
d = np.random.rand(lons.shape[0],lats.shape[0])
trans = ccrs.AzimuthalEquidistant(central_latitude=clat, central_longitude=clon)
ax = plt.axes(projection=trans)
ax.coastlines(resolution='10m')
CB=ax.pcolormesh(lons-0.25, lats-0.25, d.T,
cmap=plt.cm.viridis, alpha=0.5,
transform=trans)#ccrs.PlateCarree())
p1 = mpatches.Circle((clon,clat), radius=15, color='k', lw=5, fill=False,
transform=trans)
ax.add_patch(p1)
If the data you are plotting is in latitude/longitude coordinates then the correct value for the transform keyword is indeed ccrs.PlateCarree(). This is common gotcha for new users. The transform argument tells cartopy what coordinates your data are in, and is completely independent of the projection you want to plot onto.
To make the plot circular you'll need to set the boundary yourself. The Cartopy documentation have a couple of examples of this: http://scitools.org.uk/cartopy/docs/latest/examples/always_circular_stereo.html and http://scitools.org.uk/cartopy/docs/latest/examples/star_shaped_boundary.html.

Using ax.set_rasterization_zorder() puts rasterized objects outside of the axis extent

The following figure (exported as a pdf) contains overly detailed vector images that load slowly in a PDF viewer:
I'm trying to rasterize the outlines of the polygons displayed. However, when I use ax.set_rasterization_zorder(0), the following occurs:
I believe it is a backend issue but cannot figure out how to fix the problem.
import numpy as np
import matplotlib as mpl
from matplotlib.collections import PatchCollection
fig, ax = plt.subplots()
ax.imshow(dem, zorder=-2, rasterized=True)
parts = mpl.patches.Polygon(vertices)
polygon = PatchCollection(parts, zorder=-1)
ax.add_collection(polygon)
ax.set_rasterization_zorder(0)
I was not setting rasterized=True when creating the PatchCollection objects that were drawing the polygons. When using rasterized=True without ax.set_rasterization_zorder(), each polygon would be rasterized individually, creating a large file; however, it was at least in the right location.
By rasterizing the polygon initially at the PatchCollection level and combining it with ax.set_rasterization_zorder() prior to calling plt.savefig(), all of the polygon objects were combined with the underlying DEM into one raster, creating a file manageable by Adobe PDF viewers.
import numpy as np
import matplotlib as mpl
from matplotlib.collections import PatchCollection
fig, ax = plt.subplots()
ax.imshow(dem, zorder=-2, rasterized=True)
parts = mpl.patches.Polygon(vertices)
polygon = PatchCollection(parts, zorder=-1, rasterized=True)
ax.add_collection(polygon)
ax.set_rasterization_zorder(0)