How to Read and Plot a Shapefile (.shp) Using Python
Shapefiles are one of the most common formats for storing vector geographic data — points, lines, and polygons (for example, country borders, fire perimeters, or solar farm locations).
A shapefile actually consists of multiple files with the same base name but different extensions:
| Extension | Description |
|---|---|
.shp |
Geometry (points, lines, polygons) |
.shx |
Shape index |
.dbf |
Attribute table (tabular data) |
.prj |
Coordinate reference system (projection) |
Install Required Libraries
Make sure you have these libraries installed:
1 | pip install geopandas matplotlib |
Read the Shapefile
Let’s assume your shapefile is named global_solar_panels.shp and stored in the same folder as your notebook or script.
1 2 3 4 5 6 7 8 | import geopandas as gpd import matplotlib.pyplot as plt # Read shapefile gdf = gpd.read_file("global_solar_panels.shp") # Display the first few rows print(gdf.head()) |
This command automatically loads the geometry and attributes (from the .dbf file).
Check the Coordinate Reference System (CRS)
It’s important to know the coordinate system (e.g., WGS84, EPSG:4326).
1 | print(gdf.crs) |
If your data doesn’t have a CRS defined, or it’s not in latitude/longitude (EPSG:4326), you can convert it:
1 2 | if gdf.crs is None or gdf.crs.to_epsg() != 4326: gdf = gdf.to_crs(epsg=4326) |
How to Visualize the Data
The way you plot a shapefile depends on the size of your dataset. Large shapefiles (e.g., global coverage) can slow down plotting, so it’s often useful to inspect a subset first.
Check the GeoDataFrame Size
Before plotting, it’s good practice to see how many features your shapefile contains:
1 | print(f"Number of rows (features) in GeoDataFrame: {len(gdf)}") |
This helps you decide whether to plot the full dataset or just a sample.
Create a Sample for Testing
If your shapefile is very large, you can plot a random subset to test your code or check data structure:
1 2 3 4 5 6 7 8 9 | # Sample 1000 features (adjust as needed) gdf_sample = gdf.sample(1000, random_state=42) # Plot the sample gdf_sample.plot(figsize=(10, 8), edgecolor='black', alpha=0.6) plt.title("Sample of Shapefile Features") plt.xlabel("Longitude") plt.ylabel("Latitude") plt.show() |
Tip: Using a sample speeds up plotting and is useful for exploratory analysis without loading the full dataset.

Plot the Entire Shapefile
Once you’ve verified your data, you can plot the full dataset easily:
1 2 3 4 5 | gdf.plot(figsize=(12, 10), edgecolor='gray', alpha=0.5) plt.title("Full Shapefile Visualization") plt.xlabel("Longitude") plt.ylabel("Latitude") plt.show() |
Visualize Shapefile Data on a Satellite Map Using Folium
Install Required Libraries
Make sure you have the needed packages installed:
1 | pip install geopandas folium |
Load and Sample Your GeoDataFrame
For example:
1 2 3 4 5 6 7 | import geopandas as gpd import folium gdf = gpd.read_file("global_solar_panels.shp") # Take a small sample if the dataset is large gdf_sample = gdf.sample(1000) |
Create a Folium Map with a Satellite Basemap
You can use Esri World Imagery for a satellite background:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Get the centroid to center the map center = [gdf_sample.geometry.centroid.y.mean(), gdf_sample.geometry.centroid.x.mean()] # Create the map again m = folium.Map( location=[ gdf_sample.geometry.centroid.y.mean(), gdf_sample.geometry.centroid.x.mean() ], zoom_start=5, tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attr='Esri World Imagery' ) |
Add GeoDataFrame to the Map
You can easily overlay the geometries using Folium’s GeoJson layer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # Get only non-geometry columns tooltip_fields = [col for col in gdf_sample.columns if col != 'geometry'] # Use only up to 5 for clarity tooltip_fields = tooltip_fields[:5] folium.GeoJson( gdf_sample, name="Shapefile Sample", style_function=lambda x: { "color": "yellow", "weight": 2, "fillOpacity": 0.3 }, tooltip=folium.features.GeoJsonTooltip(fields=tooltip_fields) ).add_to(m) folium.LayerControl().add_to(m) m |
(Optional) Save as an Interactive HTML Map
You can export the result as an interactive file viewable in any browser:
1 | m.save("shapefile_sample_satellite.html") |
(Optional) Use a Different Satellite or Basemap Provider
Folium supports several built-in tile providers via folium.TileLayer.
You can add multiple for comparison:
1 2 3 4 5 6 7 | folium.TileLayer('OpenStreetMap').add_to(m) folium.TileLayer('Stamen Terrain').add_to(m) folium.TileLayer('Stamen Toner').add_to(m) folium.TileLayer('CartoDB positron').add_to(m) # Esri satellite (already added manually above) folium.LayerControl().add_to(m) |
Example Output

References
| Links | Site |
|---|---|
| https://geopandas.org/en/stable/docs/user_guide/io.html | GeoPandas: Reading and Writing Files — official guide for loading shapefiles and GeoJSON data. |
| https://geopandas.org/en/stable/docs/user_guide/mapping.html | GeoPandas: Plotting Maps — how to visualize geographic data using Matplotlib. |
| https://python-visualization.github.io/folium/ | Folium Documentation — create interactive leaflet maps directly in Python notebooks. |
| https://contextily.readthedocs.io/en/latest/ | Contextily — add basemaps (satellite, terrain, street maps) to GeoPandas plots. |
| https://epsg.io/4326 | EPSG:4326 (WGS84) — standard latitude/longitude coordinate system reference. |
| https://matplotlib.org/stable/ | Matplotlib Documentation — plotting library used by GeoPandas for static maps. |
