Connect your AI coding assistants to the physical world with Wherobots MCP and CLI Learn More

Spatial Graph RAG for the Physical World

Introduction

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.


What is Graph RAG?

Graph RAG extends standard RAG by adding a knowledge graph layer (nodes and edges) between the raw data and the language model.

Standard RAG:

  1. User asks a question
  2. System searches a vector database for semantically similar text chunks
  3. Top-K chunks are injected into the LLM prompt as context
  4. LLM generates an answer grounded in those chunks

Graph RAG

  1. User asks a question
  2. System identifies relevant entities (nodes) in a knowledge graph
  3. System traverses the graph’s edges and nodes to retrieve a compact subgraph of connected entities, relationships, and provenance
  4. Subgraph (entities (the nodes) + relationships (the edges) + evidence (relationship provenance) is injected into the LLM prompt
  5. LLM generates an answer grounded in structured, relational context

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


The Problem: AI Hallucinates and Make Assumptions About Places and Relationships

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:

  • A business is inside a building, which is on a street, which connects to a road network, which is in an administrative boundary
  • An address might match multiple buildings, and the correct assignment depends on access points and proximity to road segments
  • Two POI records might be duplicates, a brand and a venue, or distinct businesses in the same building, and the evidence for each interpretation is spatial
  • A business is accessible via a building that is accessible via a the transportation network who’s navigation is weighted by current traffic load

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.


From Graph RAG to Spatial Graph RAG

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.

Step 1: Generate graph edges with spatial SQL

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 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()

Step 2: Run graph operations with GraphFrames

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.

Step 3: Ground the LLM in spatial evidence

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.


Standard Graph RAG vs. Spatial Graph RAG

DimensionStandard Graph RAGSpatial Graph RAG
Data sourceDocuments, wikis, databasesPhysical-world datasets (map data, imagery, sensor data)
Entity extractionNLP (named entity recognition)Spatial data themes (buildings, places, addresses, roads)
Relationship extractionText parsing, co-occurrenceSpatial operations (containment, proximity, adjacency, intersection)
Edge generationNLP pipelines (spaCy, OpenIE)Distributed spatial joins (Apache Sedona / WherobotsDB)
Graph operationsGraph databases (Neo4j, Neptune)GraphFrames on Spark — same cluster, no separate DB
Query anchorText query, entity nameCoordinate, building ID, place, address
Subgraph retrievalEntity traversal, community detectionSpatial neighborhood + motif queries (GraphFrames)
ProvenanceSource document, page, paragraphSource dataset, spatial operation, confidence score
Hallucination reducedFactual claims about documentsPlace claims, spatial relationships, address assignments
Scale challengeMillions of documentsBillions of spatial features (planetary scale)

Inside the Stack: Why Spatial Graph RAG Needs Two Layers

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:

  1. Edge and Node generation: distributed spatial joins across billions of features stored as different geometry types. This is what WherobotsDB and Apache Sedona are built for. The Overture Maps Foundation itself uses Wherobots and Apache Sedona to generate its monthly global data releases at scale.
  2. Graph operations: connected components, shortest paths, motif queries, iterative traversal. This is what GraphFrames provides, natively on the same Spark session, treating graphs as DataFrames.

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.

Why GraphFrames for Spatial Graph RAG

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:

  • Connected components : for identifying conflation clusters (which records likely refer to the same real-world entity)
  • Motif finding: for multi-hop structural queries like (place)→(building)→(admin_area) that power context retrieval
  • BFS/neighborhood expansion: for bounded subgraph extraction anchored to a query location

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.


Spatial Graph RAG Use Cases

Place Q&A for AI Assistants

“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.

Conflation and Data Quality

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.

Insurance Risk Assessment

Which properties are in flood zones, which are adjacent to wildfire-prone vegetation, which are accessed by roads that become impassable in storms.

Autonomous Systems and HD Maps

Which lane connects to which intersection, which signs apply to which road segment. Graph-structured map data provides the relational context.

Supply Chain Risk Intelligence

Facilities → nearby hazard zones, suppliers → upstream of disruption, routes → through chokepoints.


Building Spatial Graph RAG From Overture Maps Data

The Overture Maps Foundation provides the ideal source data, available directly in The Wherobots Hub. The pipeline:

  1. Load Overture themes from the Wherobots Open Data Catalog (wherobots_open_data.overture_maps_foundation.*)
  2. Generate edges via distributed spatial joins (WherobotsDB spatial SQL)
  3. Store vertex and edge tables in Apache Iceberg (Havasu format)
  4. Construct graph with GraphFrames — same Spark session, no separate DB
  5. Run graph operations — connected components for conflation, motifs for context retrieval
  6. Serve subgraphs as LLM context through the Spatial AI Assistant or MCP integration

Every build enriches The Global Hub.


Spatial Graph RAG: Key Points

  • Graph RAG adds knowledge graph context to LLM reasoning
  • Standard Graph RAG builds knowledge graphs from text. Spatial Graph RAG builds them from the physical world
  • AI hallucinates about place because it has no structured spatial context — even when the model contains relevant geographic knowledge (GeoLLM, 2024)
  • The pipeline is two layers: distributed spatial SQL generates edges; GraphFrames traverses them — both in the same WherobotsDB session
  • Provenance makes it auditable — every edge carries evidence, every conflation candidate is inspectable
  • The physical world is fundamentally relational. Spatial Graph RAG gives AI the relationships it needs to reason about place.

References

Start Building with WherobotsDB

Frequently Asked Questions

What is Spatial Graph RAG?

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.

How is Spatial Graph RAG different from Graph RAG?

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.

Do I need a graph database for Spatial Graph RAG?

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.

What data sources work for Spatial Graph RAG?

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.

How does Spatial Graph RAG make AI more accurate about places?

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.