Geospatial Analytics
Parent: data analysis · researched 2026-05-30T22:41:44.933Z· 17 sources · 13 concepts · skill da-26-geospatial-analytics
Spatial analytics studies data with a geographic/locational dimension, where the
Geospatial Analytics
- Spatial analytics studies data with a geographic/locational dimension, where the [source]
- core methodological premise is Tobler's First Law: "everything is related to [source]
- everything else, but near things are more related than distant things." This makes [source]
- location an explanatory variable, not just an attribute - and means standard [source]
- non-spatial statistics (which assume independent observations) are often invalid [source]
- on spatial data. This skill covers general spatial analysis. For MongoDB geo [source]
- queries (2dsphere, $geoNear, $geoWithin), defer to mongodb-geospatial. [source]
1. Vector vs Raster Data Models
- Two fundamental representations of geographic phenomena: [source]
- Vector: discrete features as points, lines, and polygons defined by [source]
- coordinate vertices. Best for objects with crisp boundaries (parcels, roads, [source]
- administrative areas). In Python, vector geometry is handled by Shapely and [source]
- exposed through GeoPandas as a GeoSeries/GeoDataFrame (a pandas DataFrame [source]
- with one or more geometry columns; only one is the active geometry, accessed [source]
- via .geometry and switched with set_geometry()) ([GeoPandas, Data [source]
- structures, 2026](https://geopandas.org/en/stable/docs/user_guide/data_structures.html)). [source]
- Raster: a regular grid of cells/pixels, each holding a value. Best for [source]
- continuous fields (elevation, temperature, satellite imagery). Handled in [source]
- Python by rasterio/xarray/rioxarray. [source]
- Choose vector for object/topology-centric analysis (joins, networks); raster [source]
- for surface/field analysis (interpolation outputs, map algebra). Conversion [source]
- (rasterize/vectorize) loses information - avoid round-tripping. [source]
2. Coordinate Reference Systems (CRS) & Projections
- A CRS maps coordinates to real locations; without it, geometries are just numbers [source]
- in arbitrary space ([GeoPandas, Projections, [source]
- 2026](https://geopandas.org/en/stable/docs/user_guide/projections.html)). [source]
- Geographic CRS uses lat/lon on a 3D ellipsoid. EPSG:4326 (WGS84) is the [source]
- GPS/GeoJSON default; its units are degrees, not meters. [source]
- Projected CRS flattens the earth onto a plane with linear (meter) units. [source]
- EPSG:3857 (Web Mercator) is the default for web tiles (Google/OSM/Mapbox): [source]
- good for display, bad for area (massively distorts toward the poles). [source]
- UTM divides earth into 60 zones for accurate local distance/area; pick the [source]
- zone covering your data ([8th Light, Geographic Coordinate Systems 101, [source]
- 2023](https://8thlight.com/insights/geographic-coordinate-systems-101); [source]
- [Esri, Spatial references, [source]
- 2024](https://developers.arcgis.com/documentation/spatial-references/)). [source]
- set_crs() vs to_crs(): set_crs assigns/labels the CRS without moving [source]
- coordinates (use when CRS is missing/wrong); to_crs reprojects (transforms [source]
- coordinate values). Never confuse them. Use estimate_utm_crs() to pick a local [source]
- metric CRS (GeoPandas, Projections, 2026; [source]
- Geocomputation with Python, ch.6 Reprojecting, 2024). [source]
3. Spatial Predicates & DE-9IM
- Topological relationships between two geometries are formalized by the [source]
- Dimensionally Extended 9-Intersection Model (DE-9IM) - a 3×3 matrix comparing [source]
- the interior/boundary/exterior of each geometry. Named predicates are shortcuts [source]
- over this matrix ([PostGIS, ch.5 Spatial Queries, [source]
- 2024](https://postgis.net/docs/manual-dev/using_postgis_query.html); [source]
- Shapely 2.1 manual, 2025): [source]
- intersects (share any space - the inverse of disjoint), contains, [source]
- within (inverse of contains), touches (share only a boundary), overlaps, [source]
- crosses, equals, covers/covered_by. [source]
- ST_Relate (PostGIS) / Shapely relate() return the raw DE-9IM string for [source]
- custom relationships. [source]
4. Spatial Joins
- A spatial join attaches attributes from one layer to another by spatial [source]
- relationship rather than a key. `geopandas.sjoin(left, right, predicate=..., [source]
- how=...) supports intersects (default), within, contains. sjoin_nearest` [source]
- joins to the closest feature. PostGIS performs the equivalent with predicate [source]
- functions in the WHERE/JOIN ON clause, automatically using a spatial index [source]
- when present ([PostGIS workshop, §13 Spatial Joins, [source]
- 2024](https://postgis.net/workshops/postgis-intro/joins.html); [pythonGIS, Spatial [source]
- queries, 2024](https://pythongis.org/part2/chapter-06/nb/05-spatial-queries.html)). [source]
5. Geometric (Constructive) Operations
- Unary: buffer(d) (zone within distance d - units follow the CRS!), [source]
- centroid, simplify(tol) (Douglas-Peucker vertex reduction), [source]
- convex_hull, envelope. [source]
- Binary / set: intersection, union (union_all()/unary_union to [source]
- dissolve a collection), difference, symmetric_difference. [source]
- GeoPandas overlay(df1, df2, how=...) applies set operations across two whole [source]
- layers (intersection/union/identity/difference/symmetric_difference) [source]
- ([GeoPandas, Set operations with overlay, [source]
- 2026](https://geopandas.org/en/stable/docs/user_guide/set_operations.html); [source]
- [Geocomputation with Python, ch.4 Geometry operations, [source]
- 2024](https://py.geocompx.org/04-geometry-operations)). [source]
6. Spatial Indexing
- Without an index, every pairwise spatial test is O(n²). Two index families: [source]
- Tree indexes (R-tree): bounding-box hierarchy used internally by GeoPandas [source]
- (.sindex), Shapely STRtree, and PostGIS GiST. Fast pairwise filtering; node [source]
- rectangles may overlap ([Corso, Geospatial Indexing, [source]
- 2020](https://austincorso.com/2020/12/02/geospatial-indexing.html)). [source]
- Discrete global grid systems (DGGS) encode location as a hierarchical [source]
- cell ID for prefix/integer lookups and aggregation: [source]
- Geohash (Niemeyer, 2008): Z-order rectangles; shared string prefix ⇒ [source]
- shared parent cell. Suffers boundary discontinuity (adjacent points can [source]
- differ at the first char). [source]
- Google S2: projects sphere onto cube faces, Hilbert-curve ordered [source]
- 64-bit IDs; square cells; used in Google Maps. Strong for hierarchical [source]
- coverings/aggregation. [source]
- Uber H3 (open-sourced 2018): hexagonal cells; near-uniform centroid [source]
- spacing and a single neighbor distance, ideal for grid traversal, binning, [source]
- and ML features. Hexagons can't perfectly nest, so parent/child is [source]
- approximate ([Feifke, Geospatial Indexing Explained, [source]
- 2023](https://benfeifke.com/posts/geospatial-indexing-explained/); [KunYu, [source]
- H3 vs Geohash vs S2, 2024](https://ky-gis.com/en/blog/h3-vs-geohash-vs-s2)). [source]
- Rule of thumb: H3 for neighbor/traversal and binning; S2 for exact [source]
- nesting/aggregation; geohash for simple prefix-range queries in a B-tree. [source]
7. Spatial Weights (W)
- ESDA and spatial regression require a spatial weights matrix encoding which [source]
- observations are neighbors. Built with libpysal ([Geographic Data Science with [source]
- Python, ch.4 Spatial Weights, [source]
- 2024](https://geographicdata.science/book/notebooks/04_spatial_weights.html); [source]
- [libpysal 4.13 user guide, [source]
- 2024](https://pysal.org/libpysal/user-guide/weights/weights.html)): [source]
- Contiguity: Queen (share a vertex or edge) vs Rook (share an edge [source]
- only) - for polygons. [source]
- Distance-based: KNN (k nearest), DistanceBand (all within a threshold), [source]
- Kernel (distance-decayed weights). [source]
- Row-standardization (w.transform = 'r') rescales each row to sum to 1 so [source]
- the spatial lag is a neighbor average; usually required before Moran's I / [source]
8. Spatial Autocorrelation (ESDA)
- Measures whether similar values cluster in space ([Geographic Data Science with [source]
- Python, ch.7 Local Autocorrelation, [source]
- 2024](https://geographicdata.science/book/notebooks/07_local_autocorrelation.html); [source]
- PySAL esda, 2025; [source]
- [r-spatial book, ch.15 Measures of Spatial Autocorrelation, [source]
- 2023](https://r-spatial.org/book/15-Measures.html)): [source]
- Global Moran's I: one statistic for the whole map: positive ⇒ clustering, [source]
- ~0 ⇒ spatial randomness, negative ⇒ dispersion/checkerboard. Significance via [source]
- permutation inference (esda.Moran). [source]
- Geary's C: ranges ~0–2 (1 = no autocorrelation); more sensitive to local [source]
- differences and inversely related to Moran's I but not identical. [source]
- LISA / Local Moran's I (esda.Moran_Local): decomposes the global [source]
- statistic per location, classifying significant units into HH, LL (spatial [source]
- clusters) and HL, LH (spatial outliers); visualize with a Moran scatterplot [source]
- and LISA cluster map (splot). [source]
9. Point-Pattern Analysis
- Analyzes the locations of events themselves (not attribute values), testing [source]
- against Complete Spatial Randomness (CSR) ([Geographic Data Science with [source]
- Python, ch.8 Point Pattern Analysis, [source]
- 2024](https://geographicdata.science/book/notebooks/08_point_pattern_analysis.html); [source]
- [PySAL pointpats v2.5, [source]
- 2025](http://pysal.org/pointpats/)): [source]
- Kernel Density Estimation (KDE): smooth continuous intensity surface [source]
- (hotspot map); bandwidth choice dominates the result. [source]
- Nearest-neighbor / G & F functions: distribution of nearest-neighbor [source]
- distances; clustered if observed distances < CSR expectation. [source]
- Ripley's K (and the variance-stabilized L): counts neighbors within [source]
- increasing radii to test clustering vs dispersion across scales; assess [source]
- against simulation envelopes. [source]
10. Interpolation & Kriging
- Predict values at unsampled locations from sampled points ([pygis, Spatial [source]
- Interpolation, 2024](https://pygis.io/docs/e_interpolation.html); [Columbia MSPH, [source]
- Kriging Interpolation, 2024](https://www.publichealth.columbia.edu/research/population-health-methods/kriging-interpolation); [source]
- PyKrige 1.7 docs, 2024): [source]
- IDW (Inverse Distance Weighting): deterministic; weight ∝ 1/dist^p. Simple, [source]
- no uncertainty estimate, prone to "bull's-eyes." [source]
- Kriging: geostatistical; weights derive from a fitted variogram [source]
- (semi-variance vs lag distance), so it accounts for spatial structure and [source]
- yields prediction variance. Ordinary kriging assumes an unknown constant [source]
- mean; PyKrige supports linear/power/spherical/gaussian/exponential variogram [source]
- models and 2D/3D ordinary & universal kriging. [source]
11. Geocoding
- Forward geocoding = address → coordinates; reverse geocoding = coordinates [source]
- → address. geopy wraps providers (OSM Nominatim = free, Google/Bing/etc.). [source]
- Wrap calls in geopy.extra.rate_limiter.RateLimiter and set a unique [source]
- user_agent - Nominatim enforces ≤1 req/s and bans bulk abuse ([GeoPy 2.4 docs, [source]
- 2024](https://geopy.readthedocs.io/); [Spatial Dev Guru, Geocoding with geopy, [source]
- 2023](https://spatial-dev.guru/2023/03/12/geocoding-and-reverse-geocoding-in-python-using-geopy/)). [source]
12. Choropleth Mapping & Classification
- A choropleth shades areal units by a value; the classification scheme [source]
- (binning) drives the visual message ([Geographic Data Science with Python, ch.5 [source]
- 2024](https://geographicdata.science/book/notebooks/05_choropleth.html); [source]
- PySAL mapclassify, 2024; [GIS Geography, [source]
- Choropleth data classification, 2024](https://gisgeography.com/choropleth-maps-data-classification/)): [source]
- Equal Interval: equal value ranges; intuitive but skewed data collapses [source]
- Quantiles: equal count per class; good general-purpose readability but [source]
- can place similar values in different classes. [source]
- Natural Breaks (Fisher-Jenks): minimizes within-class variance, maximizes [source]
- between-class variance; respects data structure but breaks aren't comparable [source]
- Always normalize counts to rates/densities before mapping, and use [source]
- mapclassify (NaturalBreaks, Quantiles, EqualInterval, FisherJenks). [source]
13. Spatial Regression
- Standard OLS on spatial data violates the independence assumption; residuals are [source]
- autocorrelated. Two model families ([Spatial Modelling for Data Scientists, ch.9 [source]
- GWR, 2024](https://gdsl-ul.github.io/san/09-gwr.html); [Esri, GWR tool reference, [source]
- 2024](https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-statistics/geographically-weighted-regression.htm); [source]
- PySAL spreg/mgwr): [source]
- Spatial lag model (SAR): adds a spatially-lagged dependent variable [source]
- (Wy); models spillover/interdependence between units. [source]
- Spatial error model (SEM): autocorrelation in the error term (Wε); [source]
- unmodeled spatially-structured omitted variables. [source]
- Choose between them with Lagrange Multiplier diagnostics in spreg. [source]
- Geographically Weighted Regression (GWR): fits a local regression at each [source]
- location with distance-weighted neighbors, producing spatially-varying [source]
- coefficients (models non-stationarity, not interdependence). Watch local [source]
- multicollinearity and bandwidth selection. [source]
Tools & Frameworks
- Shapely 2.x: geometry engine (GEOS); vectorized ops on geometry arrays. [source]
- GeoPandas 1.x (2026): pandas + Shapely + pyproj + Fiona/pyogrio; the [source]
- Python workhorse for vector I/O, CRS, joins, overlay, plotting. [source]
- PostGIS: spatial extension for PostgreSQL; production spatial SQL with GiST [source]
- indexes; the most feature-complete OSS spatial engine. [source]
- PySAL: spatial statistics (libpysal weights, esda autocorrelation, [source]
- pointpats, mapclassify, spreg/mgwr regression). [source]
- H3 / S2: DGGS libraries for binning, indexing, and ML features. [source]
- DuckDB spatial extension: INSTALL spatial; LOAD spatial;; fast in-process [source]
- analytical spatial SQL, reads/writes GeoParquet; lighter than PostGIS but fewer [source]
- functions ([DuckDB Spatial Extension docs, [source]
- 2025](https://duckdb.org/docs/current/core_extensions/spatial/overview)). [source]
- Apache Sedona / SedonaDB: distributed (Spark) and single-node (SedonaDB, [source]
- released 2025) engines treating spatial as first-class; for cluster-scale data [source]
- ([Apache Sedona, Introducing SedonaDB, [source]
- 2025](https://sedona.apache.org/latest/blog/2025/09/24/introducing-sedonadb-a-single-node-analytical-database-engine-with-geospatial-as-a-first-class-citizen/)). [source]
- kepler.gl 3.1: browser-based large-scale visualization; embeds DuckDB to [source]
- query GeoParquet client-side ([Foursquare, Kepler.gl 3.1, [source]
- 2024](https://foursquare.com/resources/blog/products/foursquare-brings-enterprise-grade-spatial-analytics-to-your-browser-with-kepler-gl-3-1/)). [source]
- GeoParquet: columnar, compressed interchange format read by GeoPandas, [source]
- DuckDB, Sedona, QGIS, kepler.gl; the emerging standard for analytical vector [source]
- Tool selection ([Forrest, Geospatial Tools Compared, [source]
- 2025](https://forrest.nyc/geospatial-tools-compared-when-to-use-geopandas-postgis-duckdb-apache-sedona-and-wherobots/)): [source]
- single-machine exploration/notebooks → GeoPandas; persistent transactional [source]
- spatial DB → PostGIS; fast analytical queries on files → DuckDB; cluster-scale [source]
- batch → Sedona; spatial statistics/modeling → PySAL. [source]
Methodology (end-to-end)
- Ingest & set CRS: load, confirm .crs; set_crs if missing, never to fix [source]
- Reproject: to_crs to a metric/projected CRS (UTM via [source]
- estimate_utm_crs()) before any distance/area/buffer step. [source]
- Clean geometry: fix invalidities (make_valid/buffer(0)), drop [source]
- empties, set precision. [source]
- Build/attach index: rely on .sindex / GiST; for binning encode H3/S2. [source]
- Operate: joins, overlays, geometric ops. [source]
- Analyze: build weights → ESDA (Moran/LISA) → point pattern / interpolation [source]
- / spatial regression as the question demands. [source]
- Communicate: choropleth with a justified classifier on normalized rates; [source]
- interactive map (kepler.gl/folium) for exploration. [source]
Practical Patterns
- Reproject to UTM/equal-area before measuring length, area, or buffering; [source]
- back to 4326/3857 only for output/display. [source]
- Pre-filter with the spatial index (or H3 cell join) before exact predicate [source]
- tests on large datasets. [source]
- Use H3 to turn messy point data into tidy, joinable grid features for ML and [source]
- Map rates/densities, not raw counts; pick the classifier deliberately [source]
- (quantiles for readability, Jenks for structure, equal interval for comparison). [source]
- Push heavy joins/aggregations into DuckDB-spatial or PostGIS; keep GeoPandas for [source]
Anti-Patterns
- Computing distance/area in EPSG:4326: degrees aren't meters; results are [source]
- nonsense and vary with latitude. [source]
- set_crs to "fix" wrong coordinates: it only relabels; you need to_crs [source]
- (or the correct source CRS). [source]
- Mixing CRS across layers: silently wrong joins/overlays; always reproject [source]
- to a common CRS first. [source]
- Using Web Mercator for area/statistics: extreme high-latitude distortion; [source]
- use an equal-area projection. [source]
- Skipping row-standardization of W before Moran's I / spatial lag. [source]
- Mapping raw counts as a choropleth (population artifact) instead of rates. [source]
- Bulk-hammering Nominatim without rate-limiting/user_agent - gets you banned. [source]
- Trusting OLS on spatial data without checking residual autocorrelation. [source]
Troubleshooting
- *"Geometry is in a geographic CRS. Results may be incorrect" (GeoPandas [source]
- warning)* → reproject to a projected CRS before the area/length/buffer op. [source]
- Empty/NaN spatial join result → CRS mismatch between layers, or wrong [source]
- predicate; check .crs on both and the relationship direction [source]
- (within vs contains). [source]
- TopologyException / invalid geometry → run make_valid() or buffer(0); [source]
- inspect with .is_valid and .explain_validity. [source]
- Moran's I ≈ 0 but a visible pattern → wrong/under-connected weights (try [source]
- Queen vs KNN), or scale mismatch; verify W connectivity (no islands). [source]
- Kriging variogram won't fit → too few points, duplicate coordinates, or wrong [source]
- model; try IDW as a baseline and inspect the empirical variogram. [source]
- H3/geohash boundary artifacts → neighbors split across cells; buffer the query [source]
- or use grid_disk/kRing to include adjacent cells. [source]
- DuckDB function missing → spatial coverage is narrower than PostGIS; fall back [source]
- to PostGIS/GeoPandas for that op. [source]
References
- GeoPandas, Data structures / Projections / Set operations (2026): https://geopandas.org/en/stable/docs/user_guide/ [source]
- Shapely 2.1 User Manual (2025): https://shapely.readthedocs.io/en/stable/manual.html [source]
- PostGIS, Spatial Queries & Joins workshop (2024): https://postgis.net/workshops/postgis-intro/joins.html [source]
- Geographic Data Science with Python, Weights/ESDA/Point Patterns/Choropleth (2024): https://geographicdata.science/book/ [source]
- libpysal Spatial Weights v4.13 (2024): https://pysal.org/libpysal/user-guide/weights/weights.html [source]
- PySAL esda / pointpats / mapclassify / spreg / mgwr (2024-2025): https://pysal.org/ [source]
- PyKrige 1.7 docs (2024): https://geostat-framework.readthedocs.io/projects/pykrige/ [source]
- GeoPy 2.4 docs (2024): https://geopy.readthedocs.io/ [source]
- Geocomputation with Python, Reprojection & Geometry ops (2024): https://py.geocompx.org/ [source]
- Feifke, Geospatial Indexing Explained, Geohash/S2/H3 (2023): https://benfeifke.com/posts/geospatial-indexing-explained/ [source]
- KunYu, H3 vs Geohash vs S2 (2024): https://ky-gis.com/en/blog/h3-vs-geohash-vs-s2 [source]
- DuckDB Spatial Extension (2025): https://duckdb.org/docs/current/core_extensions/spatial/overview [source]
- Apache Sedona, Introducing SedonaDB (2025): https://sedona.apache.org/latest/blog/2025/09/24/introducing-sedonadb-a-single-node-analytical-database-engine-with-geospatial-as-a-first-class-citizen/ [source]
- Foursquare, Kepler.gl 3.1 (2024): https://foursquare.com/resources/blog/products/foursquare-brings-enterprise-grade-spatial-analytics-to-your-browser-with-kepler-gl-3-1/ [source]
- Forrest, Geospatial Tools Compared (2025): https://forrest.nyc/geospatial-tools-compared-when-to-use-geopandas-postgis-duckdb-apache-sedona-and-wherobots/ [source]
- Esri, GWR & Spatial references (2024): https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-statistics/geographically-weighted-regression.htm [source]
Children
- Vector vs Raster Data Models (frontier)
- Coordinate Reference Systems & Projections (frontier)
- Spatial Predicates & DE-9IM (frontier)
- Spatial Joins (frontier)
- Geometric Operations (frontier)
- Spatial Indexing (R-tree, Geohash, H3, S2) (frontier)
- Spatial Weights (frontier)
- Spatial Autocorrelation (Moran's I, LISA, Geary's C) (frontier)
- Point-Pattern Analysis (frontier)
- Interpolation & Kriging (frontier)
- Geocoding (frontier)
- Choropleth Mapping & Classification (frontier)
- Spatial Regression (GWR, Spatial Lag/Error) (frontier)
Frontier under this node: Choropleth Mapping & Classification, Coordinate Reference Systems & Projections, Geocoding, Geometric Operations, Interpolation & Kriging, Point-Pattern Analysis, Spatial Autocorrelation (Moran's I, LISA, Geary's C), Spatial Indexing (R-tree, Geohash, H3, S2), Spatial Joins, Spatial Predicates & DE-9IM, Spatial Regression (GWR, Spatial Lag/Error), Spatial Weights, Vector vs Raster Data Models