Mapping with Shapely Polygons - python

I am at a loss to get the following code to work. For whatever reason, the GeoPandas *.plot() doesn't work, but I want to use both Pandas and GeoPandas for some simple plots.
I have been trying to take the Shapely objects from GeoPandas and plot them on a Basemap. The problem is that the polygons won't plot. I iterate through them from the GeoPandas.geometry, add them to the axes collection, and then use plot() - to no avail. Basemap seems to work fine, the code doesn't give any errors, but the polygons - counties - don't appear...
Thank you for the help!
import geopandas as gpd
from descartes import PolygonPatch
import matplotlib as mpl
import mpl_toolkits.basemap as base
import matplotlib.pyplot as plt
counties_file = r'C:\Users\...\UScounties\UScounties.shp'
counties = gpd.read_file(counties_file)
#new plot
fig = plt.figure(figsize=(5,5),dpi=300)
#ax = fig.add_subplot(111)
ax = ax = plt.gca()
minx, miny, maxx, maxy = counties.total_bounds
#map
m = base.Basemap(llcrnrlon=minx, llcrnrlat=miny,
urcrnrlon=maxx, urcrnrlat=maxy,
resolution='h', area_thresh=100000,
projection='merc')
patches = []
#add polygons
for poly in counties.geometry:
#deal with single polygons and multipolygons
if poly.geom_type == 'Polygon':
p = PolygonPatch(poly, facecolor='blue', alpha=1)
#plt.gca().add_patch(p)
#ax.add_patch(p)
patches.append(p)
elif poly.geom_type == 'MultiPolygon':
for single in poly:
q = PolygonPatch(single,facecolor='red', alpha=1)
#ax.add_patch(p)
patches.append(q)
m.drawcoastlines(linewidth=.1)
m.fillcontinents()
m.drawcountries(linewidth=.25,linestyle='solid')
m.drawstates(linewidth=.25,linestyle='dotted')
m.drawmapboundary(fill_color='white')
ax.add_collection(mpl.collections.PatchCollection(patches, match_original=True))
ax.plot()
plt.show()

Check if your shapefile is in the right projection system. Basemap is currently set to Mercator Projection. After that it worked for me.

Related

Geopandas world map in Polar Stereographic projection with coloured oceans

Adding a further requirement to this question, I also need to have the oceans in blue (or any other colour).
For the 'PlateCarree' projection I can simply do this
crs = ccrs.PlateCarree()
crs_proj4 = crs.proj4_init
world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
w = world.to_crs(crs_proj4)
g = w.plot(facecolor='sandybrown', edgecolor='black')
And now adding the ocean colour
g.set_facecolor('#A8C5DD')
If I now want to use a polar stereographic peojection
ccrs.NorthPolarStereo()
or
ccrs.SouthPolarStereo()
the projection does not work. When applying the answer to this question, I cannot get the oceans coloured
You need to plot the map geometries on Cartopy geoaxes, and use cartopy.feature.OCEAN to plot the ocean. Here is the working code that you may try. Read the comments in the code for clarification.
import geopandas as gpd
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy
facecolor = 'sandybrown'
edgecolor = 'black'
ocean_color = '#A8C5DD'
#crs1 = ccrs.SouthPolarStereo()
crs1 = ccrs.NorthPolarStereo()
world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
w1 = world.to_crs(crs1.proj4_init)
fig1, ax1 = plt.subplots(figsize=(7,7), subplot_kw={'projection': crs1})
# useful code to set map extent,
# --- if you want maximum extent, comment out the next line of code ---
ax1.set_extent([-60.14, 130.4, -13.12, -24.59], crs=ccrs.PlateCarree())
# at maximum extent, the circular bound trims map features nicely
ax1.add_geometries(w1['geometry'], crs=crs1, \
facecolor=facecolor, \
edgecolor=edgecolor, \
linewidth=0.5)
# this adds the ocean coloring
ax1.add_feature(cartopy.feature.OCEAN, facecolor=ocean_color, edgecolor='none')
plt.show()
The output plot will be:

Basemap great circle longitudinal wrapping and missing data issue

I am attempting to plot multiple great circles using a for loop in conjunction with a set of lat/lon points. I am using the animation function with matplotlib to make the plots update when the data source is updated. This is all working well.
I noticed that plotting greatcircles where the shortest distance is wrapping the image, the plot will use that and appear on the other side of the map. Is there an argument that prevents this?
Also, depending on where the plot is I notice the "middle" of the plot arc is missing. What could be causing this? Map and code below:
The CSV uses the following points:(Moscow and Tokyo)
sourcelon sourcelat destlon destlat
55.44 37.51 -80.84 35.22
139 35.6 -80.84 35.22
Minimal code:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.animation
# setup mercator map projection.
fig = plt.figure(figsize=(27, 20))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='r', linewidth=1.0)
def animate(i):
df = pd.read_csv('c:/python/scripts/test2.csv', sep='\s*,\s*',header=0, encoding='ascii', engine='python'); df
for x,y,z,w in zip(df['sourcelon'], df['sourcelat'], df['destlon'], df['destlat']):
line, = m.drawgreatcircle(x,y,z,w,color='r')
ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000)
plt.tight_layout()
plt.show()
As wikipedia tells us
The great-circle distance or orthodromic distance is the shortest distance between two points on the surface of a sphere, measured along the surface of the sphere.
So the path shown goes the shortest distance, which might wrap from one side of the image to the other.
The missing points in the line are a bit of a mystery, but it might be that there is some problem with the projection in use. Using a different projection, this works fine, e.g. projection='robin':
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
m = Basemap(projection='robin',lon_0=0,resolution='c')
m.drawcoastlines(color='grey', linewidth=1.0)
a = [[55.44, 37.51, -80.84, 35.22],[139, 35.6, -80.84, 35.22]]
x,y,z,w = a[0]
line, = m.drawgreatcircle(x,y,z,w,color='r')
plt.show()
The problem can be circumvented if the distance between points in enlarged, e.g.
line, = m.drawgreatcircle(x,y,z,w,del_s=1000,color='r')
would give
Another workaround would be to get the data from the line and plot it manually,
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='grey', linewidth=1.0)
a = [[55.44, 37.51, -80.84, 35.22],[139, 35.6, -80.84, 35.22]]
x,y,z,w = a[0]
line, = m.drawgreatcircle(x,y,z,w,color='r')
line.remove()
mx,my = line.get_data()
m.plot(mx,my, color="limegreen")
plt.show()

How to display stock_img() using Basemap?

According to Cartopy docs, stock_img() has only one (and default) option - a down sampled version of the Natural Earth shaded relief raster.
How do I display this image using Basemap?
Here is an example:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
ax = plt.gca()
bmap = Basemap( projection='ortho', lat_0=45, lon_0=-100, resolution='l', ax=ax )
bmap.shadedrelief(scale=0.25)
plt.show()

Using Python to plot Natural Earth shapes as polygons in Matplotlib Basemap

I'm close to getting the map that I want. Matplotlib's Basemap is great, but the coastlines are too coarse when I zoom in. I can read the Natural Earth shapefiles and plot them, which are much better... but when I try and fill the polygons, I think it's treating all of the points as belonging to a single polygon. How can I iterate through the polygons and display the map correctly?
Thanks in advance!
Here's the code:
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
%matplotlib inline
landColor, coastColor, oceanColor, popColor, countyColor = '#eedd99','#93ccfa','#93ccfa','#ffee99','#aa9955'
fig = plt.figure()
ax = fig.add_subplot(111)
s = 1900000
m = Basemap(projection='ortho',lon_0=-86.5,lat_0=30.3,resolution='l',llcrnrx=-s,llcrnry=-s,urcrnrx=s,urcrnry=s)
m.drawmapboundary(fill_color=oceanColor) # fill in the ocean
# generic function for reading polygons from file and plotting them on the map. This works with Natural Earth shapes.
def drawShapesFromFile(filename,facecolor,edgecolor,m):
m.readshapefile(filename, 'temp', drawbounds = False)
patches = []
for info, shape in zip(m.temp_info, m.temp): patches.append( Polygon(np.array(shape), True) )
ax.add_collection(PatchCollection(patches, facecolor=facecolor, edgecolor=edgecolor, linewidths=1))
# read the higher resolution Natural Earth coastline (land polygons) shapefile and display it as a series of polygons
drawShapesFromFile('\\Conda\\notebooks\\shapes\\ne_10m_coastline',landColor,coastColor,m)
drawShapesFromFile('\\Conda\\notebooks\\shapes\\ne_10m_urban_areas',popColor,'none',m)
m.drawcounties(color=countyColor)
plt.gcf().set_size_inches(10,10)
As requested, here's the updated code and resulting map. All I had to do was change ne_10m_coastline to ne_10m_land like this:
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
%matplotlib inline
landColor, coastColor, oceanColor, popColor, countyColor = '#eedd99','#93ccfa','#93ccfa','#ffee99','#aa9955'
fig = plt.figure()
ax = fig.add_subplot(111)
s = 1900000
m = Basemap(projection='ortho',lon_0=-86.5,lat_0=30.3,resolution='l',llcrnrx=-s,llcrnry=-s,urcrnrx=s,urcrnry=s)
m.drawmapboundary(fill_color=oceanColor) # fill in the ocean
# generic function for reading polygons from file and plotting them on the map. This works with Natural Earth shapes.
def drawShapesFromFile(filename,facecolor,edgecolor,m):
m.readshapefile(filename, 'temp', drawbounds = False)
patches = []
for info, shape in zip(m.temp_info, m.temp): patches.append( Polygon(np.array(shape), True) )
ax.add_collection(PatchCollection(patches, facecolor=facecolor, edgecolor=edgecolor, linewidths=1))
# read the higher resolution Natural Earth coastline (land polygons) shapefile and display it as a series of polygons
drawShapesFromFile('\\Conda\\notebooks\\shapes\\ne_10m_land',landColor,coastColor,m)
drawShapesFromFile('\\Conda\\notebooks\\shapes\\ne_10m_urban_areas',popColor,'none',m)
m.drawcounties(color=countyColor)
plt.gcf().set_size_inches(10,10)

Drawing a graph with NetworkX on a Basemap

I want to plot a graph on a map where the nodes would be defined by coordinates (lat, long) and have some value associated.
I have been able to plot points as a scatterplot on a basemap but can't seem to find how to plot a graph on the map.
Thanks.
EDIT: I have added code on how I plotted the points on a basemap. Most of it has been adapted from code in this article.
from mpl_toolkits.basemap import Basemap
from shapely.geometry import Point, MultiPoint
import pandas as pd
import matplotlib.pyplot as plt
m = Basemap(
projection='merc',
ellps = 'WGS84',
llcrnrlon=-130,
llcrnrlat=25,
urcrnrlon=-60,
urcrnrlat=50,
lat_ts=0,
resolution='i',
suppress_ticks=True)
# Create Point objects in map coordinates from dataframe lon
# and lat values
# I have a dataframe of coordinates
map_points = pd.Series(
[Point(m(mapped_x, mapped_y))
for mapped_x, mapped_y in zip(df['lon'],
df['lat'])])
amre_points = MultiPoint(list(map_points.values))
plt.clf()
fig = plt.figure()
ax = fig.add_subplot(111, axisbg='w', frame_on=False)
fig.set_size_inches(18.5, 10.5)
# Create a scatterplot on the map
dev = m.scatter(
[geom.x for geom in map_points],
[geom.y for geom in map_points],
20, marker='o', lw=.25,
facecolor='#33ccff', edgecolor='w',
alpha=0.9,antialiased=True,
zorder=3)
m.fillcontinents(color='#555555')
I get this image:
Here is one way to do it:
import networkx as nx
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap as Basemap
m = Basemap(
projection='merc',
llcrnrlon=-130,
llcrnrlat=25,
urcrnrlon=-60,
urcrnrlat=50,
lat_ts=0,
resolution='i',
suppress_ticks=True)
# position in decimal lat/lon
lats=[37.96,42.82]
lons=[-121.29,-73.95]
# convert lat and lon to map projection
mx,my=m(lons,lats)
# The NetworkX part
# put map projection coordinates in pos dictionary
G=nx.Graph()
G.add_edge('a','b')
pos={}
pos['a']=(mx[0],my[0])
pos['b']=(mx[1],my[1])
# draw
nx.draw_networkx(G,pos,node_size=200,node_color='blue')
# Now draw the map
m.drawcountries()
m.drawstates()
m.bluemarble()
plt.title('How to get from point a to point b')
plt.show()
As of today there is a nice alternative to basemap. Mplleaflet is a library inspired by mpld3. It plots faster than basemap, is more easy to use and allows to visualizing geographic data on beautiful interactive openstreetmap. The input can be longitude and latitude the library automatically projects the data properly.
Input dictionary pos, where the node (country) is the key and long lat are saved as value.
pos = {u'Afghanistan': [66.00473365578554, 33.83523072784668],
u'Aland': [19.944009818523348, 60.23133494165451],
u'Albania': [20.04983396108883, 41.14244989474517],
u'Algeria': [2.617323009197829, 28.158938494487625],
.....
Plotting is as easy as:
import mplleaflet
fig, ax = plt.subplots()
nx.draw_networkx_nodes(GG,pos=pos,node_size=10,node_color='red',edge_color='k',alpha=.5, with_labels=True)
nx.draw_networkx_edges(GG,pos=pos,edge_color='gray', alpha=.1)
nx.draw_networkx_labels(GG,pos, label_pos =10.3)
mplleaflet.display(fig=ax.figure)

Categories

Resources