Geographical Plots #
Warning: This post hasn't been updated for over a year. The information may be out of date.
(Maybe) Useful resources #
Courses:
- Welcome to Introduction to Python GIS -course 2018! — Intro to Python GIS CSC documentation
- Mapping and Data Visualization with Python (Full Course Material) (Covers many packages)
- Welcome to Pangeo at AOES — Pangeo-at-AOES 0.1.1 documentation
- Pythia Foundations — Pythia Foundations
- Home — Geographic Data Science with Python
- Repo: gdsbook/book
Things to check out later:
- Geographic Data with Basemap | Python Data Science Handbook (Matplotlib + Basemap)
- Mapping with Matplotlib, Pandas, Geopandas and Basemap in Python | by Ashwani Dhankhar | Towards Data Science (The main example uses Matplotlib. The author uses the
shapefilepackage which is nowpyshp? to read the map file, butGeoPandashas similar importer functions.) - Using python and geoPandas to map public green spaces in Manchester | by Camila Varó | Medium (GeoPandas + Matplotlib + contextily)
- How I Created Animated Choropleth Map and Running Bar Plot using Python | by Girish Dev Kumar Chaurasiya | Python in Plain English (GeoPandas + Matplotlib, Pillow for output as GIF, plotly)
- Easy Steps To Plot Geographic Data on a Map — Python | by Ahmed Qassim | Towards Data Science (Matplotlib, and a mini OpenStreetMap tutorial)
- PacktPublishing/Applied-Geospatial-Data-Science-with-Python: Applied Geospatial Data Science with Python, published by Packt
- PacktPublishing/Learning-Geospatial-Analysis-with-Python-Fourth-Edition: Learning Geospatial Analysis with Python - Fourth Edition, published by Packt
Things to not check out later:
- Python Folium: Create Web Maps From Your Data – Real Python (Folium)
- Geographical Plots with Python - KDnuggets (plotly + Cufflinks)
- Geography with Cartopy (Xarray + Matplotlib + cartopy)
Basic plotting flow #
- Have data (to plot) with latitude and longitude columns;
- Get map (vector map/shape file/polygons/tiles/webmap) of the desired range; (some packages, like Basemap, seem to come with global and US maps, but not of other countries)
- Import both datasets;
- Plot map data first, then layer the actual data on top of it.
Main usage of some packages #
I will be mainly using matplotlib and seaborn to plot with cartopy. If I got extra time, I will try out geoplot as well.
geopandas- Pandas DataFrame equivalence for data or map that are geographical polygons. Also plots the polygons and points.
- Introduction to GeoPandas — GeoPandas 0+untagged.50.g5558c35.dirty documentation
contextily- Auto-add webmap to data plot as background image.
- Introduction guide to contextily — contextily 1.1.0 documentation
xyzservices- API interfaces that contextily uses. Sometimes debugging contextily unavoidably leads to it, for example, looking for the now gone
Stamenprovider. - Gallery - xyzservices 2023.10.1
cartopy- Handle map projection of plots (Matplotlib axes) and data to be plotted on those axes.
- Gallery — cartopy 0.22.0 documentation
matplotlib/seaborn- My go-to Python plotting packages. See Matplotlib/Seaborn.
geoplot- Claims to be a geospatial version of
seaborn. May try later. - Gallery — geoplot 0.5.0 documentation
- Others
- I was told that
folium,plotly,bokeh,geoviewsand many other plotting packages could plot geospatial figures as well. But since I am already familiar withmatplotlibandseaborn, I decided against trying other packages. I remember tryingbokeha few years ago, but did not stick with it for some reason.
How to get dataset with latitude/longitude #
Datasets with lat/lon:
Convert postcode to lat/long:
- Worldwide by-country lookup TSVs: GeoNames (column names:
["country code", "postal code", "place name", "admin name1", "admin code1", "admin name2", "admin code2", "admin name3", "admin code3", "latitude", "longitude", "accuracy"], seereadme.txt)- Python wrapper: symerio/pgeocode (does not support the “full” version for CA,NL and UK yet)
- geopandas.tools.geocode: geopy/geopy wrapper. Most decent providers (like Google) require API keys.
How to get map #
Two methods, bring your own map (as polygons or image) and plot them manually, or use some packages to download and plot automatically.
I am a lazy person. So far, I’ve found two different automatic approach, using cartopy and contextily, respectively.
Adding map with cartopy #
It is not maps per se, but I will be abusing the “feature” function in cartopy as makeshift maps.
Refs:
- Maps with Cartopy - Research Computing in Earth Sciences
- matplotlib - Borders and coastlines interfering in Python Cartopy - Stack Overflow
- cartopy.mpl.geoaxes.GeoAxes — cartopy 0.22.0 documentation
- List of named colors — Matplotlib 3.8.3 documentation
Toy example with no projection:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import cartopy.feature as cfeature
# define extra features
states_provinces = cfeature.NaturalEarthFeature(
category='cultural',
name='admin_1_states_provinces_lines',
scale='50m',
)
urban_areas = cfeature.NaturalEarthFeature(
category='cultural',
name='urban_areas',
scale='50m',
)
# begin plotting
plt.clf()
fig,ax = plt.subplots()
sns.scatterplot(ax=ax, data=df, x="longitude", y="latitude", hue="Rating")
# start cartopy settings
ax.set_global()
ax.add_feature(cfeature.LAND, facecolor="silver")
# ax.add_feature(cfeature.OCEAN)
# ax.add_feature(cfeature.COASTLINE, edgecolor="silver")
ax.add_feature(cfeature.BORDERS, edgecolor="white")
# ax.add_feature(states_provinces, linestyle=':', edgecolor="white", facecolor="none")
# ax.add_feature(cfeature.LAKES)
# ax.add_feature(cfeature.RIVERS, edgecolor="white")
# ax.add_feature(urban_areas, facecolor="silver, alpha=0.5)
ax.gridlines(draw_labels=True, alpha=0.3)
# matplotlib settings
ax.legend(frameon=True)
plt.show()ax.add_image
cartopy has its own ax.add_image and cartopy.io.img_tiles methods, but the documentation is very ambiguous and the method not easy to use.
Docs:
- cartopy.mpl.geoaxes.GeoAxes — cartopy 0.22.0 documentation
- Input/output capabilities — cartopy 0.22.0 documentation
- Map tile acquisition — cartopy 0.22.0 documentation
Toy example with no projection: (remember Stamen is no longer available)
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import cartopy.io.img_tiles as cimgt
# begin plotting
plt.clf()
fig,ax = plt.subplots()
sns.scatterplot(ax=ax, data=df, x="longitude", y="latitude", hue="Rating")
# cartopy settings
ax.set_global()
ax.add_image(cimgt.OSM(), 5) # the larger zoom level, the smaller the label fonts
ax.gridlines(draw_labels=True, alpha=0.3)
# matplotlib settings
ax.legend(frameon=True)
plt.show()Adding map with contextily #
The maps are beautiful out of the box, but handling projection becomes a problem if you are not using geopandas.
Toy example with cartopy and seaborn with no projection, which translates to equirectangular projection on plot axes, aka PlateCarree:
| |
A more involved way of using the add_basemap function that I have yet to try: Adding a background map to plots — GeoPandas 0+untagged.50.g5558c35.dirty documentation
Arbitrary map projection with cartopy and seaborn #
Most examples online read as if only FacetGrid could use custom projections, which is not true. I think any plot could use any projection with a proper amount of matplotlib setup.
Refs:
- Matplotlib interface (cartopy.mpl) — cartopy 0.22.0 documentation (most important info:
class cartopy.mpl.geoaxes.GeoAxesis a subclass ofmatplotlib.axes.Axes) - Faceted maps with Seaborn and Cartopy.ipynb
projection vs transform #
Refs:
- Understanding the transform and projection keywords — cartopy 0.22.0 documentation
- Cartography and Mapping in Python
- Cartopy projection list — cartopy 0.22.0 documentation
- cartopy.crs.epsg — cartopy 0.22.0 documentation
- python - Use ccrs.epsg() to plot zipcode perimeter shapefile with EPSG 4326 coordinate system - Stack Overflow
Example with cartopy features #
TBA
Calculations #
Distance #
Example from: python - Getting distance between two points based on latitude/longitude - Stack Overflow, my answer
import cartopy.geodesic as cgeo
coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)
coords_3 = (52.406374, 10)
# Note: cartopy only accepts lon-lat pairs!!
globe = cgeo.Geodesic()
inv = globe.inverse(coords_1[::-1], coords_2[::-1])
print(inv.T[0]/1000)
# [279.3529016]
inv_2 = globe.inverse(coords_1[::-1], [coords_2[::-1], coords_3[::-1]])
print(inv_2.T[0]/1000)
# [279.3529016 750.45799898]Wrapper function for easier usage with DataFrame:
import cartopy.geodesic as cgeo
def distance(longitudes, latitudes):
assert(1 < len(longitudes))
assert(1 < len(latitudes))
assert(len(longitudes) == len(latitudes))
places = list(zip(longitudes, latitudes))
globe = cgeo.Geodesic()
inv = globe.inverse(places[0], places[1:])
return (inv.T[0]/1000)Entertainment #
xkcd: Map Projections
977: Map Projections - explain xkcd
xkcd’s “Map Projections”, animated - YouTube gives an idea how much the lands are distorted when you spin the globe with each projection.