Planetary-scale answers, unlocked.
A Hands-On Guide for Working with Large-Scale Spatial Data. Learn more.
Authors
RAG (Retrieval Augmented Generation) has addressed one of AI’s biggest challenges for enterprise users: missing or hallucinating empirical business and real world context . Instead of generating answers from nothing, RAG retrieves relevant documents and feeds them to the model as context. It works. Ask an AI about your company’s Q4 revenue, and RAG pulls the earnings report before answering.
But RAG has a blind spot. It retrieves documents, flat chunks of text ranked by semantic similarity. It doesn’t understand relationships between assets. It doesn’t know that your VP of Sales reports to the CRO, or that Product A competes with Product B, or that the supplier in Shenzhen feeds the factory in Guadalajara that ships to the warehouse in Memphis. You can leave the connections to be made by the LLM, but seeding control over simple facts to a nondeterministic process is not the best business decision.
Graph RAG fixes this by building a knowledge graph of entities and their relationships, then retrieving compact subgraphs, not just documents, as context for the model. It’s one of the fastest-growing concepts in AI infrastructure, and for good reason: it makes AI dramatically better at reasoning about connected information.
There’s just one problem. Every Graph RAG implementation today builds knowledge graphs from text, semantics, and ontologies. Documents, wikis, databases, web pages. The relationships are extracted from language.
But the most consequential relationships in the physical world aren’t always written down. They’re spatial. Which building contains which addresses . Which buildings area accessible by which road. Which facilities sit inside which flood zone. Which agriculture fields are adjacent to which water sources and who owns those water rights. These relationships exist in geometry, not in sentences… No amount of document retrieval will surface them.
This is the case for Spatial Graph RAG: building knowledge graphs from the physical world, using spatial operations instead of NLP, and retrieving spatial relation-anchored subgraphs that ground AI in physical reality.
Graph RAG extends standard RAG by adding a knowledge graph layer (nodes and edges) between the raw data and the language model.
Standard RAG:
Graph RAG
The difference matters. Standard RAG might retrieve three paragraphs that each mention a company name. Graph RAG retrieves the company node, its relationships to subsidiaries, suppliers, competitors, and executives, and the evidence supporting each relationship. The model doesn’t just get text fragments, it gets structured understanding.
Microsoft Research’s GraphRAG paper (Edge et al., 2024) demonstrated this dramatically: on questions requiring synthesis across entire datasets, Graph RAG achieved 72-83% comprehensiveness win rates versus standard RAG.
Reference: From Local to Global: A Graph RAG Approach to Query-Focused Summarization — Microsoft Research, 2024
Ask a language model “What’s at this address?” or “Is this restaurant inside that building?” or “Which of these two locations is closer to the highway?” and you’ll often get confident, specific, wrong answers.
This isn’t a model intelligence problem. It’s a context problem. The model has no spatial context, no understanding of physical relationships between real-world entities.
Research bears this out directly. GeoLLM (Manvi et al., ICLR 2024) demonstrated that querying an LLM with geographic coordinates alone consistently fails, even when the model contains relevant geographic knowledge internally. Performance improves substantially only when structured, map-derived context is provided alongside the query. In other words: the problem isn’t that LLMs don’t know about geography , it’s that they can’t use that knowledge without grounded relational structure to anchor it.
The root cause goes deeper than missing map data. Place understanding is fundamentally relational:
These relationships can be ambiguous, inconsistent across data sources, and impossible to resolve from text alone. This is exactly the problem knowledge graphs solve in the document world and it’s exactly the problem spatial knowledge graphs solve for the physical world.
Spatial Graph RAG applies the Graph RAG architecture to physical-world data, using spatial operations instead of NLP to construct the knowledge graph, and location-anchored queries instead of text queries to retrieve subgraphs.
The pipeline has three stages. The first two are distinct engineering layers that work in sequence: distributed, transformations, aggregations, data generation, and spatial joins generate the nodes and edges, then graph operations traverse them.
Spark and Spatial SQL comprise the graph creation factory. Using WherobotsDB‘s spatial functions on Overture Maps data, we materialize node and edge tables into Apache Iceberg. Each edge carries provenance whether it was asserted by a source dataset, derived by a spatial join, or scored based on multiple signals .
Containment edges (place → in building):
containment_edges = sedona.sql(""" SELECT p.id AS src, b.id AS dst, 'CONTAINED_IN' AS relationship, ST_Area(ST_Intersection(p.geometry, b.geometry)) / ST_Area(p.geometry) AS overlap_pct, 'spatial_join' AS provenance FROM wherobots_open_data.overture_maps_foundation.places_place p JOIN wherobots_open_data.overture_maps_foundation.buildings_building b ON ST_Contains(b.geometry, p.geometry) """)
Proximity edges (place → near road access point), using WherobotsDB’s native ST_KNN for nearest-neighbor joins at scale:
ST_KNN
# ST_KNN is WherobotsDB's distributed k-nearest-neighbor join # (available in WherobotsDB and Apache Sedona 1.7+) access_edges = sedona.sql(""" SELECT p.id AS src, s.id AS dst, 'ACCESSED_VIA' AS relationship, ST_Distance(p.geometry, s.geometry) AS distance, 'knn_join' AS provenance FROM wherobots_open_data.overture_maps_foundation.places_place p INNER JOIN wherobots_open_data.overture_maps_foundation.transportation_segment s ON ST_KNN(p.geometry, s.geometry, 1, false) """)
Admin containment edges (building → in administrative boundary):
admin_edges = sedona.sql(""" SELECT b.id AS src, d.id AS dst, 'IN_BOUNDARY' AS relationship, d.subtype AS boundary_type, 'spatial_join' AS provenance FROM wherobots_open_data.overture_maps_foundation.buildings_building b JOIN wherobots_open_data.overture_maps_foundation.divisions_division_area d ON ST_Within(b.geometry, d.geometry) WHERE d.subtype IN ('locality', 'county', 'region') """)
All edge tables are unioned and written to Iceberg for durability and incremental updates:
unified_edges = ( containment_edges.withColumn("edge_type", lit("contained_in")) .union(access_edges.withColumn("edge_type", lit("accessed_via"))) .union(conflation_edges.withColumn("edge_type", lit("likely_same_as"))) .union(admin_edges.withColumn("edge_type", lit("in_boundary"))) ) unified_edges.writeTo("my_catalog.spatial_graph.edges").createOrReplace()
Node and Edge tables are not a graph, they’re raw material. GraphFrames, the DataFrame-native graph library for Apache Spark, turns those tables into a traversable graph inside the same WherobotsDB session. No separate graph database required.
Construct the GraphFrame:
from graphframes import GraphFrame from pyspark.sql import functions as F # Vertex table: union all entity types with a common schema nodes = sedona.sql(""" SELECT * FROM wherobots_open_data.overture_maps_foundation.nodes """) # Edge table: union all relationship types edges = sedona.sql(""" SELECT * FROM my_catalog.spatial_graph.edges """) g = GraphFrame(vertices, edges)
Connected components for conflation clustering:
# Find clusters of likely-same-entity candidates — each component # represents a set of records that may refer to the same real-world place sc.setCheckpointDir("/tmp/graphframes-checkpoints") components = g.connectedComponents() # Records sharing a component_id are conflation candidates components.filter(F.col("entity_type") == "place") \\ .groupBy("component") \\ .count() \\ .filter("count > 1") \\ .show()
Motif finding for multi-hop subgraph extraction:
GraphFrames’ motif syntax lets you query for structural patterns — the same way you’d write a path query in a graph database, but natively in Spark:
# Find: place → contained_in → building → in_boundary → admin_area # This is the "what's here?" retrieval pattern for LLM context place_context = g.find("(place)-[e1]->(building); (building)-[e2]->(admin)") \\ .filter("e1.relationship = 'CONTAINED_IN' AND e2.relationship = 'IN_BOUNDARY'") \\ .select("place", "e1", "building", "e2", "admin")
Subgraph extraction anchored to a location (the RAG retrieval step):
def retrieve_subgraph(anchor_id: str, hop_depth: int = 2): """ Given an entity ID (building, place, or address), retrieve the connected subgraph up to hop_depth hops — this is the context bundle that gets passed to the LLM. """ # Breadth-first expansion from the anchor node frontier = {anchor_id} subgraph_edges = [] for _ in range(hop_depth): neighbors = g.edges.filter( F.col("src").isin(frontier) | F.col("dst").isin(frontier) ) subgraph_edges.append(neighbors) new_nodes = neighbors.select("src").union( neighbors.select(F.col("dst").alias("src")) ).distinct() frontier = {row["src"] for row in new_nodes.collect()} from functools import reduce all_edges = reduce(lambda a, b: a.union(b), subgraph_edges).distinct() subgraph_vertices = g.vertices.filter(F.col("id").isin(frontier)) return GraphFrame(subgraph_vertices, all_edges) # Usage: retrieve context for a specific building context_graph = retrieve_subgraph("building_id_4821", hop_depth=2)
Even in a dense urban area with thousands of entities, the relevant subgraph is small, typically 10–50 nodes with their connecting edges.
The extracted subgraph replaces the flat text chunks that standard RAG provides. Instead of a paragraph about a restaurant, the model receives structured spatial evidence:
Entity: Joe's Pizza (place, confidence: 0.95) → CONTAINED_IN: Building #4821 (overlap: 99.2%, provenance: spatial_join) → ACCESSED_VIA: Carmine St segment (distance: 4.1m, provenance: knn_join) → LIKELY_SAME_AS: Joe's Pizza [OSM] (name_score: 0.94, distance: 3.2m) → IN_BOUNDARY: Greenwich Village → Manhattan → New York County
Now the model can answer “Is Joe’s Pizza inside that building?” with evidence, not guessing.
The compute layer runs on WherobotsDB, built for native spatial execution at scale. Standard graph databases (Neo4j, Amazon Neptune, TigerGraph) are built for entity-relationship traversal on pre-loaded graphs. They have no native ability to perform the distributed spatial joins needed to construct edges at planetary scale. You can’t load 2.6 billion Overture building geometries into Neo4j and ask it to compute containment relationships with 1.2 billion places.
Spatial Graph RAG needs two distinct capabilities in the same session:
Running both inside WherobotsDB means the edge tables generated in Step 1 are immediately available to GraphFrames in Step 2 — no data movement, no format conversion, no separate system to operate.
GraphFrames extends Spark DataFrames with graph-parallel primitives: connected components, PageRank, triangle count, shortest paths, and motif finding. For Spatial Graph RAG, the most important capabilities are:
(place)→(building)→(admin_area)
The “graphs as tables” design (vertices and edges as DataFrames) maps directly onto the Iceberg edge tables generated by spatial SQL, which is why the two layers compose cleanly.
“What’s at this location?” “Is this business still open?”, questions AI handles poorly today. A spatial knowledge graph provides evidence-bearing subgraphs that make answers accurate and traceable.
Determining whether two records from different sources represent the same real-world entity. Spatial Graph RAG turns conflation from an irreversible merge into an inspectable, evidence-driven process, connected components expose the clusters, motif queries explain the evidence.
Which properties are in flood zones, which are adjacent to wildfire-prone vegetation, which are accessed by roads that become impassable in storms.
Which lane connects to which intersection, which signs apply to which road segment. Graph-structured map data provides the relational context.
Facilities → nearby hazard zones, suppliers → upstream of disruption, routes → through chokepoints.
The Overture Maps Foundation provides the ideal source data, available directly in The Wherobots Hub. The pipeline:
wherobots_open_data.overture_maps_foundation.*
Every build enriches The Global Hub.
Spatial Graph RAG applies the Graph RAG architecture to physical-world data. It uses spatial operations instead of NLP to construct the knowledge graph, and location-anchored queries instead of text queries to retrieve subgraphs. The retrieved subgraph grounds the LLM in physical reality with structured evidence, not flat text chunks.
Standard Graph RAG builds knowledge graphs from documents, wikis, and databases. Relationships are extracted from language with NLP. Spatial Graph RAG builds them from physical-world datasets like map data, imagery, and sensor data. Relationships come from spatial operations: containment, proximity, adjacency, intersection. The most consequential relationships in the physical world are not written down. They exist in geometry.
No. WherobotsDB generates the node and edge tables with distributed spatial SQL, and GraphFrames turns those tables into a traversable graph inside the same Spark session. Standard graph databases like Neo4j and Neptune are not built to perform the distributed spatial joins needed to construct edges at planetary scale.
Any physical-world dataset with geometry: map data, imagery, and sensor data. The Overture Maps Foundation provides the ideal source, available directly in the Wherobots Global Hub through the Wherobots Open Data Catalog. Themes include places, buildings, transportation segments, and administrative divisions.
Standard RAG retrieves flat text chunks ranked by semantic similarity. Spatial Graph RAG retrieves a compact subgraph of entities, relationships, and provenance anchored to a location. Instead of a paragraph about a restaurant, the model receives structured spatial evidence: which building contains the restaurant, which road segment provides access, which administrative boundary it sits within, and the source of every relationship. The model answers with evidence, not a guess.
Spatial Data in Apache Iceberg: Optimizations and Management That Matter
Spatial data in Apache Iceberg needs different optimization than tabular data. A geometry column has no natural sort order, so unsorted files carry wide, overlapping bounding boxes and query planners cannot prune them… At all… This behaviour turns a selective spatial filter into a full table scan. A second problem compounds it: one oversized geometry […]
Building the Wherobots Mobility Solution Accelerator: A Technical Deep Dive
Three Notebooks, One Medallion Architecture, Full 4D GPS Trajectory Processing: Part 2 of 2
How well does SAM3 detect building footprints? We asked the Wherobots Spatial AI Coding Assistant
In a recent post, we showed how easy it is to use RasterFlow and Meta’s Segment Anything 3 Model (SAM3) to detect features in the physical world. A single end-to-end pipeline built a 133 GB NAIP mosaic of Marion County, Oregon, ran SAM3 against it with text prompts spanning eight classes, and produced approximately one […]
share this article
Awesome that you’d like to share our articles. Where would you like to share it to: