import urllib.request
import geopandas
import matplotlib.pyplot as plt
# New York City's five boroughs, as a zipped shapefile
url = "https://raw.githubusercontent.com/geopandas/geodatasets/main/data_backup/nybb_16a.zip"
urllib.request.urlretrieve(url, "nybb.zip")
gdf = geopandas.read_file("zip://nybb.zip!nybb_16a/nybb.shp")
ax = gdf.plot(column="Shape_Area", cmap="viridis", legend=True, figsize=(8, 8))
ax.set_axis_off()
plt.tight_layout()
plt.show()GeoPandas extends pandas to geographic data. A GeoDataFrame is a DataFrame
with a geometry column of points, lines or polygons. GeoPandas reads and
writes shapefiles and GeoJSON, converts between coordinate systems,
measures areas and distances, and joins tables by location. People use it
to map regions and to find which area each address falls in. This page is an online GeoPandas compiler: the code runs in your browser,
so you can try it without installing anything.
Run the example first, then paste any snippet below into a new cell to try it.
urllib.request.urlretrieve() downloads a zipped shapefile of New York
City's five boroughs and saves it as nybb.zip. geopandas.read_file()
opens the shapefile inside it: the zip://nybb.zip!nybb_16a/nybb.shp path
names the archive, then the file within it. The result, gdf, has one row
per borough and a geometry column of polygons. gdf.plot(column="Shape_Area", cmap="viridis", legend=True) shades each borough by its area and adds a
color bar. The file uses EPSG:2263, a New York projection measured in US
survey feet, so Shape_Area is in square feet. Queens is the largest
borough and Manhattan the smallest.
points_from_xy() turns latitude and longitude columns into points. It
takes x first, and x is longitude. crs="EPSG:4326" records that the
numbers are degrees:
import geopandas
import matplotlib.pyplot as plt
import pandas as pd
cities = pd.DataFrame({
"city": ["Seattle", "Denver", "Chicago", "Atlanta", "Boston"],
"lat": [47.61, 39.74, 41.88, 33.75, 42.36],
"lon": [-122.33, -104.99, -87.63, -84.39, -71.06],
})
gdf = geopandas.GeoDataFrame(
cities, geometry=geopandas.points_from_xy(cities.lon, cities.lat), crs="EPSG:4326"
)
print(gdf)
ax = gdf.plot(markersize=40, color="crimson")
for x, y, name in zip(gdf.geometry.x, gdf.geometry.y, gdf.city):
ax.annotate(name, (x, y), xytext=(4, 4), textcoords="offset points")
plt.show()
A degree is not a fixed distance: one degree of longitude is about 111 km
at the equator and 56 km at latitude 60°. Convert to a projected system
with to_crs() before measuring. estimate_utm_crs() picks the UTM zone
that covers your data, and its units are meters:
import geopandas
from shapely.geometry import Polygon
# Central Park's four corners, as (longitude, latitude)
corners = [(-73.9819, 40.7681), (-73.9580, 40.8003),
(-73.9493, 40.7968), (-73.9730, 40.7644)]
park = geopandas.GeoSeries([Polygon(corners)], crs="EPSG:4326")
utm = park.estimate_utm_crs()
park_m = park.to_crs(utm)
print(utm.name)
print(f"Area: {park_m.area.iloc[0] / 1e6:.2f} km²")
print(f"Perimeter: {park_m.length.iloc[0] / 1000:.2f} km")
It picks UTM zone 18N and prints 3.47 km² and 9.91 km.
sjoin() joins two GeoDataFrames by location instead of by a key column,
so each shop gets the zone it is in. how="left" keeps shop D, which is
outside both zones, with NaN as its zone:
import geopandas
from shapely.geometry import Point, box
zones = geopandas.GeoDataFrame(
{"zone": ["West", "East"]}, geometry=[box(0, 0, 5, 5), box(5, 0, 10, 5)]
)
shops = geopandas.GeoDataFrame(
{"shop": ["A", "B", "C", "D"]},
geometry=[Point(1, 1), Point(4, 3), Point(7, 2), Point(12, 1)],
)
joined = geopandas.sjoin(shops, zones, how="left", predicate="within")
print(joined[["shop", "zone"]])
print(joined["zone"].value_counts())
folium.GeoJson() draws a GeoDataFrame on a Leaflet web map, and
GeoJsonTooltip shows a column when the pointer is over a shape. The
Folium example covers more:
import folium
import geopandas
from shapely.geometry import Polygon
corners = [(-73.9819, 40.7681), (-73.9580, 40.8003),
(-73.9493, 40.7968), (-73.9730, 40.7644)]
park = geopandas.GeoDataFrame(
{"name": ["Central Park"]}, geometry=[Polygon(corners)], crs="EPSG:4326"
)
m = folium.Map(location=[40.782, -73.966], zoom_start=13)
folium.GeoJson(park, tooltip=folium.GeoJsonTooltip(fields=["name"])).add_to(m)
m
gdf.explore() raises an ImportError here. It needs mapclassify,
whose current release requires pandas 2.3, and this Python environment
has pandas 2.2.3. Use folium as above..area returns square degrees
with a warning that the results "are likely incorrect"..shp
inside it as the example does. The bare .zip path fails. To use your
own file, click Mount folder in the sidebar's Data tab (Chrome or
Edge on a computer) and choose the folder it is in.
gdf.to_file("out.geojson", driver="GeoJSON") saves a copy.