Skip to main content

PyGeoSpace

Powerful, honest, open-source geospatial visualization for Python.

PyGeoSpace reads the common vector and raster formats, renders interactive 2D maps, true 3D scenes, and an interactive 3D globe, runs spatial + spectral analytics, and exports interactive HTML, static images, or real 3D model files (glTF / GLB / STL / PLY / OBJ). It pairs with the PyGeoFetch / PyGeoVision ecosystem through a dedicated integration layer.

This is 0.8.0 (Beta) — "leafmap-style Notebook API". It is deliberately honest about its boundaries: features listed under What works today are implemented, and anything not yet built either says so here or raises a clear, actionable error instead of silently stubbing. A full, per-component verification record ships in PACKAGE_STATUS.md.


Two engines, one API

Engine Import Renders Best for
deck.gl / PyVista / Cesium (default) import pygeospace as pgs → pgs.Map() 2D WebGL maps, true 3D (PyVista), 3D globe (CesiumJS) imagery, terrain, large data, 3D
folium (Leaflet) from pygeospace.folium import Map 2D Leaflet maps + plugins classic web maps, choropleths, clustering, draw/measure

The default engine is deck.gl-based. The folium engine is additive and selected explicitly. The integration adapters are engine-agnostic — they produce a Map on whichever engine you use.


Install

pip install pygeospace                 # core: vector IO, analytics, deck.gl, CLI
pip install "pygeospace[raster]"       # + GeoTIFF / COG / JPEG2000 (rasterio)
pip install "pygeospace[3d]"           # + true 3D (PyVista, trame, meshio)
pip install "pygeospace[folium]"       # + folium backend (folium, branca, jinja2)
pip install "pygeospace[classify]"     # + true Fisher-Jenks breaks (jenkspy)
pip install "pygeospace[osm]"          # + OpenStreetMap extraction (osmnx)
pip install "pygeospace[stac]"         # + Planetary Computer (pystac-client, planetary-computer)
pip install "pygeospace[overture]"     # + Overture Maps (overturemaps)
pip install "pygeospace[earthengine]"  # + Google Earth Engine (earthengine-api, geemap)
pip install "pygeospace[all]"          # everything pip-installable without accounts

Python 3.10+.


Quickstart (2D, under 5 minutes)

import pygeospace as pgs

m = pgs.Map(title="Cities")
m.add_basemap("carto-light")                      # registry name or raw XYZ URL
m.add_layer("cities.geojson").style(get_fill_color=[255, 90, 0, 200], get_radius=3000)

m.fit().save("map.html")                          # interactive deck.gl page
m.save("map.png", dpi=300)                        # static export

# A Map behaves like a container of its layers:
print(m)                                          # <Map mode=2d · 2 layers: [basemap, cities]>
print(m.layer_names, len(m), m["cities"])

Command line:

pygeospace visualize cities.geojson -o map.html --style choropleth --attribute pop --method jenks
pygeospace export cities.geojson -o map.png --dpi 300
pygeospace serve cities.geojson --port 8000       # live preview
pygeospace info data.gpkg

Satellite imagery — one call per scene

read_bands folds the whole discover / window / reproject / scale / stack pipeline into a single call, returning a stacked RasterLayer in [blue, green, red, nir, swir] order.

import pygeospace as pgs
from pygeospace.analytics.spectral import spectral_index

stack = pgs.read_bands("data/satellite/", bbox=(-74.05, 40.68, -73.90, 40.82))

m = pgs.Map().add_basemap()
m.add_composite(stack, "false_color")             # true_color / false_color / agriculture
m.save("scene.html")

ndvi = spectral_index(stack, "ndvi")              # band order already correct
# one-liner: m.add_bands("data/satellite/", "true_color", bbox=...)

True 3D

import pygeospace as pgs

m = pgs.Map(mode="3d")
m.add_terrain("srtm.tif", exaggeration=2.0, cmap="gist_earth")
m.export_3d("terrain.html")                       # interactive
m.render_3d("terrain.png")                        # static
m.export_3d("terrain.gltf")                       # model for Blender / Unity / printing

cam = pgs.Camera3D(position=(0, 0, 5000), focal_point=(0, 0, 0))
cam.fly_to(4000, 4000, 2000, duration=3.0).orbit(45).tilt(20)
m.render_3d("flyover.png", camera=cam)

3D globe (CesiumJS)

The same map on an interactive 3D globe, with a built-in 2D / 2.5D / 3D scene toggle. Token-free by default (open imagery); a Cesium Ion token is optional for world terrain.

import pygeospace as pgs

m = pgs.Map(title="NYC")
m.add_layer(gdf, name="Landmarks")
m.add_bands("data/satellite/", "true_color", bbox=(-74.05, 40.68, -73.90, 40.82))
m.save("globe.html", engine="cesium")             # no token required
# premium terrain (optional): m.to_cesium_html(ion_token="…", terrain=True)

folium backend (notebook-first)

Renders inline in Jupyter / Colab and shows both raster and vector on one map.

from pygeospace.folium import (Map, VectorLayer, RasterLayer, ChoroplethLayer,
                               HeatmapLayer, ClusterLayer, SplitMapLayer, TimeSeriesLayer)

m = Map(location=(5.6, -0.2), zoom_start=11, basemap="carto-dark", title="Accra")
m.add_layer(RasterLayer(ndvi_array, bounds=(-0.3, 5.5, -0.1, 5.7), cmap="RdYlGn"))  # raster overlay
m.add_layer(VectorLayer("cities.geojson", tooltip="name", popup=["name", "pop"], fill_color="#e53e3e"))
m.add_layer(ChoroplethLayer("districts.geojson", attribute="pop", classification="quantile"))
m.add_layer(HeatmapLayer("events.csv"))
m.add_layer(ClusterLayer("points.geojson"))

m.add_controls().fit()
m.show()                                            # inline in a notebook cell
# m.save("accra.html")  /  m.to_png("accra.png")    # to_png needs [export] + headless Chrome

Notebook layers: RasterLayer (numpy array / pygeospace raster / GeoTIFF / XYZ-COG overlay), SplitMapLayer (draggable before/after divider), TimeSeriesLayer (animated slider), CustomLayer (HTML overlay). Classification methods: quantile, equal_interval, natural_breaks (Fisher-Jenks via jenkspy if installed, else a dependency-free k-means fallback), std_mean.

Controls & panels

The folium Map carries a full set of custom controls — real Leaflet controls anchored in the map corners — chainable in one expression:

(m.add_title("Vegetation index", subtitle="NDVI · 2024")
  .add_legend({"Dense": "#1a9850", "Sparse": "#fee08b", "Built-up": "#d73027"})
  .add_colorbar(-0.4, 0.9, cmap="RdYlGn", caption="NDVI")   # continuous scale for rasters
  .add_info_panel("<p>How to read this map…</p>", title="About", collapsed=True)
  .add_search()             # Nominatim geocoder box
  .add_locate()             # geolocation button
  .add_scale()              # scale bar
  .add_basemap_switcher(["carto-light", "carto-dark", "esri-satellite"]))

m.add_full_ui()             # layer control + fullscreen + minimap + coords + search + locate + scale

Panels: add_title, add_legend, add_colorbar, add_info_panel (collapsible), add_panel (arbitrary HTML). Controls: add_search, add_locate, add_scale, add_basemap_switcher, plus add_layer_control/add_fullscreen/add_measure/ add_draw/add_mouse_position/add_minimap.

leafmap-style methods

If you know leafmap, the folium Map speaks the same dialect:

from pygeospace.folium import Map, colormaps as cm

m = Map(center=(40, -100), zoom=4)
m.add_geojson("cities.geojson", layer_name="Cities", style_callback=lambda f: {"color": f["properties"]["c"]})
m.add_gdf(gdf); m.add_shp("states.zip"); m.add_vector("regions.gpkg")
m.add_csv("places.csv", x="lon", y="lat", popup=["name"])
m.add_raster("dem.tif", cmap="terrain")               # single band
m.add_raster("landsat.tif", band=[4, 3, 2], vmin=1, vmax=100)
m.add_cog_layer(cog_url); m.add_stac_layer(stac_url, bands=["B3", "B2", "B1"])
m.add_marker_cluster(pts); m.add_heatmap(pts); m.add_choropleth(geo, "population")
m.add_labels("states.geojson", "name", font_color="blue")
m.add_legend(builtin_legend="NLCD")                   # or legend_dict={...}
m.add_colorbar(0, 4000, cmap="terrain", caption="Elevation")
m.split_map("carto-light", "esri-satellite", left_label="A", right_label="B")
m.add_vector_tile_layer(mvt_url, attribution="Microsoft")

cm.get_palette("terrain", n_class=8)                  # colormaps module

add_cog_layer / add_stac_layer use a titiler endpoint and add_vector_tile_layer uses Leaflet.VectorGrid, so those three need network at view time; everything else renders offline.

Basemap registry

46 attributed XYZ basemaps (27 key-free), shared by both engines.

import pygeospace as pgs
from pygeospace.basemaps import basemap_url

pgs.list_basemaps(category="satellite", free_only=True)   # ['esri-satellite', 'usgs-imagery', ...]
pgs.get_basemap("carto-dark").attribution

# folium engine resolves registry names directly:
from pygeospace.folium import Map
Map(basemap="esri-satellite")
# deck.gl engine: pass a resolved URL
pgs.Map().add_basemap(basemap_url("esri-satellite"))

Key-required providers (Mapbox, Stadia/Stamen, Thunderforest, OpenWeatherMap) are flagged and fail loudly without a key rather than serving dead tiles.

Integrations

Import-light adapters that turn other tools' outputs into a PyGeoSpace map. Heavy or account-bound dependencies load lazily with clear error messages.

from pygeospace.integrations import geofetch, geovision, osm, pc, overture
from pygeospace.integrations import ee as earth_engine
from pygeospace.integrations import available

available()   # which integrations are usable right now

geofetch.visualize_search(results).save("search.html")        # PyGeoFetch footprints by provider
geofetch.visualize_downloads(downloaded).save("status.html")  # coloured by ok / failed / corrupt
geovision.visualize_predictions(preds).save("preds.html")     # model predictions, per-class layers

osm.from_place("Accra, Ghana", tags={"amenity": "hospital"})  # needs [osm]
pc.search("sentinel-2-l2a", bbox=bbox, datetime="2024-06")    # needs [stac]
overture.buildings((-0.21, 5.55, -0.17, 5.58))                # needs [overture]
earth_engine.tile_url(ee_image, {"min": 0, "max": 3000})      # needs [earthengine] + auth

What works today (0.7.0)

Area Capability Status
IO Shapefile, GeoJSON, GeoPackage, KML, GPX, GML; CSV with coords; PostGIS; format autodetect ✅
GeoTIFF / COG / JPEG2000 read ✅ [raster]
LAS/LAZ point cloud read ✅ [pointcloud]
read_bands() — per-band satellite discover/window/reproject/scale/stack ✅ [raster]
2D viz deck.gl scatter / GeoJSON / heatmap / hexagon; rich interactive HTML (layer panel, basemap switch, measure, coords, legend) ✅
Choropleth: quantile, equal-interval, Jenks ✅
Georeferenced raster display + static PNG/PDF ✅
3D globe CesiumJS globe, 2D/2.5D/3D toggle, token-free imagery ✅
True 3D Terrain, point clouds (LAS classes), polygon extrusion, cut planes ✅ [3d]
Export glTF/GLB/STL/PLY/OBJ/VTK; Camera3D fly/orbit/tilt ✅ [3d]
Raster analytics Spectral indices NDVI/NDWI/NDBI/SAVI/EVI; band composites; slope; reproject; mosaic ✅
Vector analytics Buffer (true meters via UTM), intersection, difference, dissolve; KMeans/DBSCAN; H3/grid binning; contours; O-D flow ✅
Basemaps 46-entry registry with attribution + key-guarding ✅
folium engine Map, Vector/Choropleth/Heatmap/Cluster; Raster overlays (array/COG/tiles), Split, TimeSeries, Custom; classification ✅ [folium]
Inline Jupyter/Colab display (show); panels: title, legend, colorbar, info; controls: search, locate, scale, basemap-switcher, draw, measure, fullscreen, minimap ✅ [folium]
leafmap-style API: add_geojson/gdf/shp/vector/csv, add_raster, add_cog_layer, add_stac_layer, add_labels, split_map, linked_maps, colormaps, built-in legends ✅ [folium]
Integrations geofetch, geovision (ecosystem); osm, pc, overture, ee (lazy) ✅ (extras per backend)
Streaming WebSocket & MQTT clients; trail buffer; alerts ✅ [streaming]
Publishing Interactive HTML, static PNG/PDF, Jupyter _repr_html_, FastAPI REST server ✅
Extensibility Decorator plugin system ✅

The deck.gl engine ships with a pytest suite (105 tests passing as of the 0.6.x API work). The 0.7.0 additions (basemaps, folium engine, integrations) were verified when this build was assembled — see PACKAGE_STATUS.md for the exact matrix, including what was not re-verified in that environment.

Not built yet / roadmap

Where the public API touches these, it raises a clear error rather than pretending.

Planned Feature
folium engine to_png/to_pdf (selenium/playwright, weasyprint) — wired, needs a headless browser; ipywidgets / Streamlit widgets; full Jinja2 theme system; raster TimeSeriesLayer frames
More backends ipyleaflet, plotly
Scale COG tile server with disk cache; vector tiles; WebGPU path for multi-million-point scenes
Analytics Zonal statistics charts; Getis-Ord Gi* hotspots; OSMnx routing

Design principles

  • Honesty over surface area. A smaller set of things that genuinely work beats a large set that breaks on first use. Unimplemented features error clearly; they are never silently stubbed.
  • Real units. Buffers reproject to the local UTM zone so "250 m" means 250 meters, not 250 degrees.
  • Token-free by default. Open imagery for both the 2D maps and the globe; paid keys are optional and, when required, fail loudly.
  • Lazy optional deps. Heavy/optional libraries import only when used, with an install hint on failure.

Examples & notebooks

Runnable notebooks live in examples/notebooks/ and double as integration tests:

  • 00_complete_tour.ipynb — the full tour: every layer type, the leafmap-style API, and all advanced custom controls/panels in one notebook
  • 01_quickstart.ipynb — vectors, tooltips, basemaps
  • 02_raster_and_vector.ipynb — raster overlays + colorbar + vector, RGB composite, before/after split
  • 03_controls_legends_colormaps.ipynb — title/legend/colorbar/info panels, search/locate/scale, colormaps

They open directly in Jupyter or Colab (the folium engine renders inline).

Development

pip install -e ".[folium,dev]"
pytest                       # core suite + notebook execution tests
pytest tests/test_notebooks.py   # just run the example notebooks end-to-end
ruff check .

The notebook tests execute every example in a fresh kernel and fail if any cell raises — they skip automatically if the notebook stack isn't installed.

License

MIT.

Metadata

Release files for pygeospace 0.8.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pygeospace 0.8.1
File Size Uploaded Provenance
pygeospace-0.8.1.tar.gz 116.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pygeospace 0.8.1
File Interpreter ABI Platform
pygeospace-0.8.1-py3-none-any.whl Python 3 none any Details

Total release size: 231.7 kB

Release files / pygeospace-0.8.1.tar.gz

Download URL pygeospace-0.8.1.tar.gz
Size 116.3 kB
Tags Source
SHA-256 checksum
How to use checksums
aa72e42728e42afd5f489ed2389f90566f6afe048691a91902df7baeaf2cfb1a
BLAKE2b-256 checksum
How to use checksums
365a5bd48e2f319f1b682216bbbddc4611abddcf672a5c885464135aef7791ce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / pygeospace-0.8.1-py3-none-any.whl

Download URL pygeospace-0.8.1-py3-none-any.whl
Size 115.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6a1be921bb37958747618cbf7d46fcbababbce546a11917bdb1bc9aa944e74cb
BLAKE2b-256 checksum
How to use checksums
c3e7a4dd6ccbcbaf6205e07176be08c9cf5f8d66516c24195ee0bc96c9184e36
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Security for pygeospace 0.8.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page