Cartopy

Programmatic Cartography (Mapping)

Install

First, install Anaconda in Windows as explained above, no need of GPU but Cartography can be used in GPU environment too, although it does not use GPU.

Then, create a new environment or use any old environment and in admin mode install cartopy :-

conda install -c conda-forge cartopy

Then, open environment variables (system) in Windows Settings, and in "system environment variable", open NEW, and put its name = PROJ_LIB ; and its path = C:\Users\yourName\anaconda3\envs\envgpu\Library\share\proj

(Windows-11 may prevent python from accessing env-var in system, hence open your anaconda env in admin mode and run=

conda env config vars set PROJ_LIB=C:\Users\yourName\anaconda3\envs\envgpu\lib\site-packages\pyproj\proj_dir\share\proj

and deactivate and reactivate env. )

Now you are ready to use Cartography through python programming. First, learn the examples in following sections, then read the online documents of Cartopy, and then experiment with various databases and other things you want, for which you can take help of chatGPT, Microsoft Copilot, Gemini, etc. chatGPT is best for programming.

For detailed info :-
Official Cartopy Documentation
Main Docs: https://scitools.org.uk/cartopy/docs/latest/
Cartopy Gallery: https://scitools.org.uk/cartopy/docs/latest/gallery/index.html

Delve into Advanced Features
Once you are comfortable with the basics, explore advanced topics:

Tile Services (like Stamen, OpenStreetMap) to fetch real map tiles.
Handling Rasters (e.g., NetCDF data, weather/climate grids) with Cartopy’s CRS transformations.
Shapely / GEOS usage for geometric operations (buffering, intersecting) under the hood.
Custom Coordinate Systems or local projections.
Vector Data from Shapefiles, GeoJSON, PostGIS queries, etc.

Starter Program

Open Anaconda (never base environment, never any env in admin mode, run your programs in any enve except Base and except admin mode, those are only for installing libraries in other envs). Then, type "python" and press enter. Then, pase this code :-
═════════════════════════

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature

plt.figure(figsize=(22, 11))
ax = plt.axes(projection=ccrs.PlateCarree()) # PlateCarree is a simple lat-lon projection

ax.add_feature(cfeature.COASTLINE, linewidth=0.5, edgecolor='black')
ax.add_feature(cfeature.BORDERS, linewidth=1.2, edgecolor='black')
ax.add_feature(cfeature.LAND, facecolor=(255/255, 220/255, 200/255))
ax.add_feature(cfeature.OCEAN, facecolor='lavender')
ax.add_feature(cfeature.LAKES, facecolor=(0/255, 150/255, 200/255), linewidth=0.5, edgecolor=(0/255, 150/255, 200/255))
ax.add_feature(cfeature.RIVERS, facecolor='blue', linewidth=1, edgecolor='blue')

states_provinces = cfeature.NaturalEarthFeature(
category="cultural",
name="admin_1_states_provinces_lines",
scale="10m",
edgecolor="gray",
facecolor="none"
)

ax.add_feature(states_provinces, linewidth=0.5)
ax.set_global()
plt.tight_layout()
plt.show()

═════════════════════════
In above code, you may change 22, 11 in "plt.figure(figsize=(22, 11))" to suit your display. All possible cfeature are in above code, you can change their linewidth, color etc. It will display all provinces of all countries in the world, but provinces and rivers and lakes in small countries will be visible only after zooming. In matplotlib, there is no zoom button, but a rectangle selection which works like zoom, you can move the selection to any direction and scan the details of whole world.

This is basic map, you can add your databases as explained in next section.
[ Optional: ax.set_global() = if you want to show the entire globe
Or you can set extent if you want (xmin, xmax, ymin, ymax) in lon/lat
ax.set_extent([65, 93, 6, 35], ccrs.PlateCarree()) ]

Database Program

"Tokyo-Yokohama", 35.68, 139.69, 37.8), = Sequence is CityName, Latitude, Longitude, Population in millions. These are populations of entire urban agglomerations including contiguous towns.

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature

big_cities = [
("Tokyo-Yokohama", 35.68, 139.69, 37.8),
("Jakarta", -6.21, 106.85, 34.0),
("Delhi", 28.61, 77.20, 32.0),
("Guangzhou-Foshan", 23.13, 113.26, 25.0),
("Mumbai", 19.07, 72.88, 24.0),
("Manila", 14.60, 120.98, 24.0),
("Shanghai", 31.23, 121.47, 24.0),
("São Paulo", -23.55, -46.63, 22.0),
("Seoul-Incheon", 37.56, 126.99, 23.0),
("Cairo", 30.04, 31.24, 21.0),
("Mexico City", 19.43, -99.13, 21.0),
("Beijing", 39.90, 116.40, 20.0),
("Osaka-Kobe-Kyoto", 34.69, 135.50, 19.0),
("New York City", 40.71, -74.01, 19.0),
("Karachi", 24.86, 67.01, 16.0),
("Buenos Aires", -34.60, -58.38, 15.0),
("Istanbul", 41.01, 28.96, 15.0),
("Kolkata", 22.57, 88.36, 15.0),
("Lagos", 6.52, 3.37, 14.0),
("Rio de Janeiro", -22.91, -43.17, 13.0),
("Bangkok", 13.76, 100.50, 13.0),
("Chongqing", 29.56, 106.55, 13.0),
("Moscow", 55.76, 37.62, 12.6)
("Lima", -12.05, -77.04, 12.0),
("Tianjin", 39.13, 117.20, 12.0),
("Kinshasa", -4.38, 15.36, 12.0),
("Shenzhen", 22.54, 114.05, 12.0),
("Bangalore", 12.97, 77.59, 12.0),
("Chennai", 13.08, 80.27, 11.0),
("Los Angeles", 34.05,-118.24, 11.0),
("Paris", 48.86, 2.35, 11.0),
("Bogotá", 4.71, -74.07, 10.9),
("Hyderabad", 17.38, 78.48, 10.8),
("London", 51.51, -0.13, 10.5),
("Ho Chi Minh City", 10.82, 106.63, 10.4),
("Chengdu", 30.66, 104.06, 10.3),
("Lahore", 31.55, 74.34, 10.2),
("Wuhan", 30.59, 114.30, 10.1),
("Hangzhou", 30.27, 120.16, 10.0),
]

medium_cities = [
("Hong Kong", 22.32, 114.17, 9.5),
("Riyadh", 24.71, 46.68, 9.4),
("Shijiazhuang", 38.04, 114.48, 9.3),
("Santiago", -33.45, -70.65, 9.2),
("Chongqing", 29.56, 106.55, 9.0),
("Ahmedabad", 23.03, 72.58, 8.8),
("Singapore", 1.35, 103.82, 8.7),
("Chengdu", 30.67, 104.06, 8.6),
("Nanjing", 32.06, 118.78, 8.5),
("Wuhan", 30.59, 114.30, 8.5),
("Tehran", 35.69, 51.39, 8.4),
("Hangzhou", 30.27, 120.15, 8.4),
("Baghdad", 33.31, 44.36, 8.2),
("Toronto", 43.65, -79.38, 8.1),
("Belo Horizonte", -19.92, -43.94, 8.0),
("Kuala Lumpur", 3.14, 101.69, 7.9),
("Madrid", 40.42, -3.70, 7.6),
("Pune", 18.52, 73.85, 7.5),
("Dallas-Fort Worth", 32.78, -96.80, 7.4),
("Houston", 29.76, -95.37, 7.3),
("Miami", 25.76, -80.19, 7.2),
("Atlanta", 33.75, -84.39, 7.1),
("Boston", 42.36, -71.06, 7.0),
("Sydney", -33.87, 151.21, 6.9),
("Melbourne", -37.81, 144.96, 6.8),
("Phoenix", 33.45,-112.07, 6.7),
("San Francisco", 37.77,-122.42, 6.6),
("Detroit", 42.33, -83.05, 6.5),
("Seattle", 47.61,-122.33, 6.4),
("Minneapolis-St. Paul", 44.98, -93.27, 6.3),
("San Diego", 32.72,-117.16, 6.2),
("Denver", 39.74,-104.99, 6.1),
("St. Louis", 38.63, -90.20, 6.0),
("Tampa", 27.95, -82.46, 5.9),
("Baltimore", 39.29, -76.61, 5.8),
("Pittsburgh", 40.44, -79.99, 5.7),
("Charlotte", 35.23, -80.84, 5.6),
("Portland", 45.52,-122.68, 5.5),
("San Antonio", 29.42, -98.49, 5.4),
("Orlando", 28.54, -81.38, 5.3),
("Sacramento", 38.58,-121.49, 5.2),
("Barcelona", 41.38, 2.17, 5.6),
("Abidjan", 5.36, -4.01, 5.5),
("Luanda", -8.84, 13.23, 5.8),
("Casablanca", 33.57, -7.59, 5.2),
("Dar es Salaam", -6.79, 39.20, 5.4),
("Khartoum", 15.50, 32.56, 5.2),
("Yangon", 16.85, 96.17, 5.5),
("Alexandria", 31.20, 29.92, 5.3),
("Chittagong", 22.36, 91.79, 5.0)
]

small_cities = [
("Darbhanga", 26.16, 85.89, 0.318)
]

plt.figure(figsize=(18, 9))
ax = plt.axes(projection=ccrs.PlateCarree()) # PlateCarree is a simple lat-lon projection

ax.add_feature(cfeature.COASTLINE, linewidth=0.5, edgecolor='black')
ax.add_feature(cfeature.BORDERS, linewidth=0.5, edgecolor='black')
ax.add_feature(cfeature.LAND, facecolor=(255/255, 220/255, 200/255))
ax.add_feature(cfeature.OCEAN, facecolor='lavender')
ax.add_feature(cfeature.LAKES, facecolor='blue', linewidth=0.5, edgecolor='black')
ax.add_feature(cfeature.RIVERS, facecolor='blue', linewidth=0.3, edgecolor='blue')

states_provinces = cfeature.NaturalEarthFeature(
category="cultural",
name="admin_1_states_provinces_lines",
scale="10m",
edgecolor="gray",
facecolor="none"
)
ax.add_feature(states_provinces, linewidth=0.5, linestyle='—')

ax.set_global()

for city, lat, lon, pop in big_cities:
# Scale circle size by population. You can tweak the multiplier to your liking.
marker_size = pop ** 1.3 # a simple non-linear scale

ax.plot(
lon, lat,
marker='o',
markersize=marker_size,
color='red',
alpha=0.4,
transform=ccrs.PlateCarree()
)
ax.text(lon+1, lat, city, fontsize=8, transform=ccrs.PlateCarree())

for city, lat, lon, pop in medium_cities:
marker_size = pop ** 1.3 # a simple non-linear scale
ax.plot(lon, lat,
marker='o',
markersize=marker_size,
color='blue',
alpha=0.4,
transform=ccrs.PlateCarree())
ax.text(lon+1, lat, city, fontsize=8, transform=ccrs.PlateCarree())

for city, lat, lon, pop in small_cities:
marker_size = pop ** 1.3 # a simple non-linear scale
ax.plot(lon, lat,
marker='o',
markersize=marker_size,
color='blue',
alpha=0.4,
transform=ccrs.PlateCarree())
ax.text(lon+1, lat, city, fontsize=8, transform=ccrs.PlateCarree())

plt.title("Approximate Population of Major World Cities (Circle Size ∝ Population)")
plt.tight_layout()
plt.show()

StreetView

If you enter wrong or too large field, nothing will display. In the following example, Field is = 82.95, 83.05, 25.28, 25.33, which means Longitude-Left, Longitude-Right, Latitude-Bottom, Latitude-Top. As it is street level view, too large field should not be selected. It works with internet connection because your selection will be downloaded.
═════════════════════════
import cartopy.io.img_tiles as cimgt
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
plt.figure(figsize=(18, 9))
osm_tiles = cimgt.OSM()
ax = plt.axes(projection=osm_tiles.crs)
ax.set_extent([82.95, 83.05, 25.28, 25.33], crs=ccrs.PlateCarree())
ax.add_image(osm_tiles, 16)
plt.show()

Offline OSM

Download Offline OSM Data:
Visit Geofabrik and download .osm.pbf or shapefiles for your region

Then, in admin mode in anaconda env :-

pip install osmnx

Then, enter "python" and paste =


import os
from pyproj import datadir
os.environ["PROJ_LIB"] = datadir.get_data_dir()
import osmnx as ox
import matplotlib.pyplot as plt

graph = ox.graph_from_place("Varanasi, India", network_type="all")
fig, ax = ox.plot_graph(graph, node_size=0, edge_linewidth=0.5)
plt.show()

For saving the map=

ox.plot_graph(graph, node_size=0, edge_linewidth=0.5, save=True, filepath="Z:\\varanasi_graph.png")


It will take some time to download data of Varanasi.

You can now fully explore the features of OSMnx and create detailed maps or analyses of street networks. If you need guidance on:

Customizing your plots (e.g., colors, line widths, annotations)
Adding overlays like points of interest, building footprints, or routes
Exporting the graph data for further analysis (e.g., saving as GeoJSON or shapefiles)
Ask chatGPT.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-Noncommercial 2.5 License.