How to Plot Maps in Python with GeoPandas and Matplotlib
A common GIS task is to display spatial data quickly so you can confirm that it loaded correctly, inspect feature locations, or create a simple static map for a report. In Python, the standard workflow for vector data is to load the data in
Problem statement
A common GIS task is to display spatial data quickly so you can confirm that it loaded correctly, inspect feature locations, or create a simple static map for a report. In Python, the standard workflow for vector data is to load the data into a GeoDataFrame and plot it with GeoPandas.
This page shows how to plot maps in Python with GeoPandas and Matplotlib. It covers common vector inputs such as shapefiles and GeoJSON, and shows how to:
- check imported GIS data visually
- plot points, lines, and polygons
- create a simple thematic map
- save the result to an image file
If you need a fast way to visualize vector spatial data in Python, this is the usual approach.
Quick answer
To plot spatial data with GeoPandas, read the file into a GeoDataFrame and call .plot():
import geopandas as gpd
import matplotlib.pyplot as plt
gdf = gpd.read_file("data/cities.geojson")
gdf.plot()
plt.show()
This works well for:
- quick static maps
- exploratory GIS analysis
- simple output for reports or automation scripts
For more control, pass styling arguments to .plot() and use Matplotlib to adjust the figure.
Step-by-step solution
Load spatial data into a GeoDataFrame
GeoPandas can read common vector GIS formats including:
- shapefile
- GeoJSON
- GeoPackage
Use gpd.read_file() to load the data:
import geopandas as gpd
gdf = gpd.read_file("data/admin_boundaries.shp")
print(gdf.head())
print(gdf.crs)
To plot correctly, the data must contain a geometry column. You should also confirm that the file loaded with the expected coordinate reference system.
You can do the same with GeoJSON:
gdf = gpd.read_file("data/roads.geojson")
Plot the map with GeoPandas
The simplest way to create a map is with the GeoDataFrame .plot() method:
import matplotlib.pyplot as plt
gdf.plot()
plt.show()
Default behavior depends on geometry type:
- points are drawn as markers
- lines are drawn as line paths
- polygons are drawn as filled shapes
This is the fastest way to verify that the data loaded and that the geometry is where you expect it to be.
Customize the map appearance with Matplotlib
For clearer output, create a Matplotlib figure and axis first, then pass the axis to GeoPandas:
fig, ax = plt.subplots(figsize=(8, 6))
gdf.plot(
ax=ax,
color="lightblue",
edgecolor="black",
linewidth=0.8
)
ax.set_title("Administrative Boundaries")
plt.show()
Useful options include:
figsizefor map sizecolorfor fill or marker coloredgecolorfor polygon borderslinewidthfor line or border thicknessmarkersizefor point layers
Plot a column as a thematic map
To create a choropleth map, pass a numeric column to column=:
fig, ax = plt.subplots(figsize=(10, 8))
gdf.plot(
column="population",
ax=ax,
cmap="OrRd",
legend=True,
edgecolor="black",
linewidth=0.5
)
ax.set_title("Population by District")
plt.show()
This is useful when you want to visualize an attribute such as:
- population
- area
- density
- land value
- any other numeric field
For categorical fields, you can also use column=, though the output is usually clearer when the number of classes is small.
Plot multiple layers on the same map
A common GIS workflow is to plot one layer on top of another, such as roads over administrative boundaries or points over polygons. The main requirement is that both layers use the same CRS.
import geopandas as gpd
import matplotlib.pyplot as plt
boundaries = gpd.read_file("data/admin_boundaries.shp")
schools = gpd.read_file("data/schools.geojson")
schools = schools.to_crs(boundaries.crs)
fig, ax = plt.subplots(figsize=(10, 8))
boundaries.plot(
ax=ax,
color="white",
edgecolor="black",
linewidth=0.8
)
schools.plot(
ax=ax,
color="red",
markersize=20
)
ax.set_title("Schools Within Administrative Boundaries")
ax.set_axis_off()
plt.show()
Use this pattern when you need to check alignment between layers or produce a simple reference map.
Remove axes and improve map readability
Map axes are often unnecessary in a simple static output. You can hide them:
fig, ax = plt.subplots(figsize=(8, 8))
gdf.plot(ax=ax, color="lightgreen", edgecolor="gray")
ax.set_title("Park Boundaries")
ax.set_axis_off()
plt.show()
For thematic maps, keep legend=True if the attribute values need explanation.
Save the map to an image file
To export the map for a report or automated workflow, save the figure with Matplotlib:
fig, ax = plt.subplots(figsize=(10, 8))
gdf.plot(
column="population",
ax=ax,
cmap="Blues",
legend=True,
edgecolor="black"
)
ax.set_title("Population Map")
ax.set_axis_off()
plt.savefig("output/population_map.png", dpi=300, bbox_inches="tight")
plt.close(fig)
This is the standard pattern for repeatable GIS reporting scripts.
Code examples
Example 1: Plot a shapefile as a basic map
Use this when you want to confirm that a shapefile loaded correctly.
import geopandas as gpd
import matplotlib.pyplot as plt
gdf = gpd.read_file("data/parcels.shp")
gdf.plot()
plt.show()
Example 2: Style polygon boundaries and fill colors
This is a better choice for a simple reporting map.
import geopandas as gpd
import matplotlib.pyplot as plt
gdf = gpd.read_file("data/landuse.shp")
fig, ax = plt.subplots(figsize=(9, 7))
gdf.plot(
ax=ax,
color="beige",
edgecolor="darkslategray",
linewidth=0.6
)
ax.set_title("Land Use Areas")
ax.set_axis_off()
plt.show()
Example 3: Create a choropleth map from an attribute column
Use a numeric field to color polygons.
import geopandas as gpd
import matplotlib.pyplot as plt
gdf = gpd.read_file("data/districts.geojson")
fig, ax = plt.subplots(figsize=(10, 8))
gdf.plot(
column="pop_density",
ax=ax,
cmap="YlGnBu",
legend=True,
edgecolor="black",
linewidth=0.4
)
ax.set_title("Population Density")
ax.set_axis_off()
plt.show()
Example 4: Plot point data on a clean map layout
Point datasets often need marker styling to be readable.
import geopandas as gpd
import matplotlib.pyplot as plt
gdf = gpd.read_file("data/schools.geojson")
fig, ax = plt.subplots(figsize=(8, 8))
gdf.plot(
ax=ax,
color="red",
markersize=20
)
ax.set_title("School Locations")
ax.set_axis_off()
plt.show()
Example 5: Save the plotted map to a PNG file
Use this pattern in automated GIS workflows.
import geopandas as gpd
import matplotlib.pyplot as plt
gdf = gpd.read_file("data/admin_boundaries.shp")
fig, ax = plt.subplots(figsize=(10, 8))
gdf.plot(ax=ax, color="lightgray", edgecolor="black", linewidth=0.5)
ax.set_title("Boundary Map")
ax.set_axis_off()
plt.savefig("output/boundary_map.png", dpi=300, bbox_inches="tight")
plt.close(fig)
Explanation
GeoPandas and Matplotlib work together:
- GeoPandas reads and stores spatial data in a GeoDataFrame
- Matplotlib renders the figure and handles styling
When you call gdf.plot(), GeoPandas converts the geometry into a Matplotlib plot. In many GIS workflows, this is enough for quick visualization.
This method is best for static maps. It is not intended for interactive web maps with pan, zoom, popups, or tiled basemaps.
Geometry type affects the output automatically:
- Point geometries become markers
- LineString geometries become lines
- Polygon geometries become filled areas with optional borders
For many tasks, GeoPandas plotting is the fastest way to inspect data before moving on to reprojection, clipping, spatial joins, or export.
Edge cases and notes
Coordinate reference systems can affect map output
If a layer appears in the wrong place or looks distorted, check its CRS:
print(gdf.crs)
If you are plotting multiple layers together, make sure they use the same CRS before plotting:
roads = roads.to_crs(boundaries.crs)
For single-layer plotting, reprojection is only necessary if your workflow requires a different CRS.
Missing or invalid geometry can affect plotting
Null or invalid geometries may cause blank output, incomplete plots, unexpected rendering, or errors.
Check for missing geometry:
print(gdf.geometry.isna().sum())
Check validity:
print(gdf.geometry.is_valid.value_counts())
If necessary, filter or repair problematic rows before plotting.
Large datasets may plot slowly
Very large shapefiles or GeoJSON files can be slow to render. Common fixes include:
- selecting fewer columns
- filtering rows
- plotting a sample
- simplifying geometry before plotting
This is common with parcel, road, or building datasets.
GeoPandas plots static maps only
If you need interactive maps, GeoPandas .plot() is not the right tool. This page covers static plotting for analysis, reporting, and script-based GIS workflows.
Internal links
If you need more context before plotting, see Working with GeoDataFrames in GeoPandas.
Related tasks:
- How to Read a Shapefile in Python with GeoPandas
- How to Reproject Spatial Data in Python (GeoPandas)
If your output is blank, misplaced, or distorted, check Why GeoPandas Plot Is Not Showing Correctly.
FAQ
How do I plot a shapefile in Python with GeoPandas?
Read the shapefile with gpd.read_file() and call .plot():
import geopandas as gpd
import matplotlib.pyplot as plt
gdf = gpd.read_file("data/boundaries.shp")
gdf.plot()
plt.show()
Can GeoPandas plot GeoJSON files directly?
Yes. GeoPandas can read GeoJSON with read_file() the same way as a shapefile:
gdf = gpd.read_file("data/points.geojson")
gdf.plot()
How do I color a map by an attribute column in GeoPandas?
Use the column argument and optionally add a legend:
gdf.plot(column="population", cmap="OrRd", legend=True)
This is the standard way to create a choropleth map in GeoPandas.
How do I save a GeoPandas plot as a PNG image?
Create the plot, then save it with Matplotlib:
fig, ax = plt.subplots()
gdf.plot(ax=ax)
plt.savefig("map.png", dpi=300, bbox_inches="tight")
plt.close(fig)
This is useful for reports, dashboards, and batch GIS scripts.
Related articles
Keep exploring with more guides in this category.
How to Fix Invalid Geometries in Python (GeoPandas)
How to find and fix invalid geometries in GeoPandas using buffer(0), make_valid, and geometry validation checks.
Read article →
How to Read a Shapefile in Python with GeoPandas
Step-by-step guide to reading a shapefile in Python using GeoPandas, with examples and common issues covered.
Read article →
How to Reproject Spatial Data in Python (GeoPandas)
How to reproject spatial data in Python using GeoPandas to_crs(), with examples for common coordinate systems.
Read article →