GeoAI: AI Agents for GIS — A Complete Technical Guide
A comprehensive technical guide to AI agents for Geographic Information Systems (GIS). Covers three-tier agent architecture, ReAct and Multi-Agent design patterns, Model Context Protocol (MCP) tool integration, and full code implementations with LangGraph and CrewAI. Includes 7 professional architecture diagrams, GIS tool function schemas, and real-world use cases for disaster response, urban planning, environmental monitoring, and infrastructure inspection. Production-ready Python code for building autonomous geospatial AI agents.
Table of Contents
- 1. Executive Summary
- 2. What is GeoAI?
- 3. Geospatial AI Agents: Core Concepts
- 4. System Architecture & Design Patterns
- 5. Three-Tier Geospatial Agent Architecture
- 6. Types of Geospatial AI Agents
- 7. AI Agent Frameworks for GeoAI
- 8. Tool Systems: MCP, RAG & Function Calling
- 9. Agent Design Patterns
- 10. Code Implementation Examples
- 11. Real-World Use Cases
- 12. GIS Platforms with AI Agent Integration
- 13. Building Your First GeoAI Agent
- 14. Production Considerations
- 15. Future Outlook
- 16. Resources & References
- 17. Glossary
1. Executive Summary
GeoAI (Geospatial Artificial Intelligence) represents the convergence of geospatial data, geographic information systems (GIS), and artificial intelligence techniques including machine learning, deep learning, computer vision, and large language models (LLMs). The emergence of AI agents capable of planning, reasoning, and executing geospatial workflows autonomously marks a paradigm shift in how spatial analysis is conducted.
This guide provides a comprehensive technical reference for understanding, architecting, and implementing AI agents for GIS workflows. It covers architecture patterns, framework comparisons, code examples, and production-ready guidance based on the current state of the industry in 2026.
Key Takeaways
| Capability | Impact |
|---|---|
| Autonomous Spatial Analysis | AI agents can plan and execute multi-step geoprocessing workflows with minimal human intervention |
| Natural Language to GIS | Users can interact with GIS systems through conversational interfaces without writing queries |
| Multi-Agent Orchestration | Teams of specialized agents collaborate on complex geospatial tasks |
| Democratized Access | Non-GIS experts can access powerful spatial analysis capabilities |
| Workflow Automation | Repetitive geoprocessing tasks are automated, reducing analysis time by 70-90% |
2. What is GeoAI?
Definition
GeoAI is the integration of geospatial data and artificial intelligence techniques within GIS platforms to automate, enhance, and scale spatial analysis. It encompasses a wide range of capabilities, from object detection in imagery and 3D scenes to natural language-powered assistants and predictive modeling.
Evolution of AI in GIS
1980s ──► Command-line GIS Systems
│
1990s ──► Graphical User Interfaces (GUI) + Desktop GIS
│
2000s ──► Web GIS + Cloud Computing + Mobile Platforms
│
2010s ──► Machine Learning for spatial analysis & image classification
│
Late 2010s ──► Deep Learning embedded in core GIS workflows
│
2020s ──► Large Language Models (LLMs) + Computer Vision
│
2024-2026 ──► AI Agents: Autonomous, multi-step geospatial workflow orchestration
The Four Pillars of GeoAI

3. Geospatial AI Agents: Core Concepts
What is a Geospatial AI Agent?
A geospatial AI agent is an autonomous or semi-autonomous software component that:
- Perceives geospatial data (vector, raster, 3D, temporal)
- Reasons about spatial relationships and domain context
- Plans multi-step analysis workflows
- Executes GIS tools, APIs, and geoprocessing services
- Responds with actionable insights, maps, or derived datasets
Core Agent Systems
Every geospatial AI agent consists of four interconnected systems:

Agent vs. Assistant vs. Traditional Script
| Dimension | Traditional Script | AI Assistant | AI Agent |
|---|---|---|---|
| Autonomy | None (deterministic) | Low (guided) | High (self-directed) |
| Reasoning | None | LLM-powered | LLM + spatial reasoning |
| Tool Use | Hardcoded | Predefined skills | Dynamic tool selection |
| Adaptability | None | Limited context | Full replanning |
| Human Input | Batch execution | Interactive | Human-in-the-loop optional |
| Error Handling | Fail/Retry | Manual recovery | Self-correction |
4. System Architecture & Design Patterns
High-Level GeoAI Platform Architecture

5. Three-Tier Geospatial Agent Architecture
A complete geospatial AI agent application follows a three-tier architecture:

6. Types of Geospatial AI Agents
6.1 Knowledge Agents (Level 1)
Purpose: Information retrieval and Q&A with geospatial domain knowledge.
Architecture:
User Query → Intent Classification → RAG Retrieval → LLM Synthesis → Cited Response
Capabilities:
- Document search with semantic understanding
- Multi-turn dialogue for complex queries
- Automatic escalation to human agents
- Knowledge base construction toolkit
Example Use Case: Real estate registration intelligent Q&A system delivering professional-grade precision in single-turn and multi-turn dialogues.
Strengths: Broad applicability, mature, high accuracy for information retrieval. Limitations: No execution capabilities; cannot perform actual geoprocessing.
6.2 Workflow Agents (Level 2)
Purpose: Execute predefined geoprocessing workflows guided by natural language.
Architecture:
| Stage | Action | Description |
|---|---|---|
| 1 | Parse Intent | LLM extracts intent and parameters from natural language |
| 2 | Load Data | Retrieve the specified dataset |
| 3 | Classify | Apply classification method (natural breaks, quantile, etc.) |
| 4 | Symbolize | Apply color scheme and styling |
| 5 | Render | Generate final map visualization |
User Command → LLM Parser → Workflow Engine → [Step 1 → Step 2 → Step 3 → Step N] → Output
Capabilities:
- Thematic map generation from commands
- Data transformation pipelines
- Format conversion workflows
- Visualization and styling automation
Example Use Case: In SuperMap iDesktopX, users input commands like "Create a thematic map segmented by national population data" and the system displays results immediately. Further commands can generate labeled thematic maps or statistical charts.
Strengths: High success rate for complex tasks, production-ready, hybrid human-AI control. Limitations: Requires predefined workflows; less flexible than autonomous agents.
6.3 Autonomous Agents (Level 3)
Purpose: Self-directed task planning and execution with iterative optimization.
Architecture (ReAct Pattern):

User Query → Task Planning → [Execute → Observe → Reason] (loop) → Final Output
Operational Logic:
- Task Planning: Decompose high-level goal into actionable steps
- Step Execution: Execute the current step using available tools
- Observation: Capture execution results
- Re-Planning: Evaluate outcomes, optimize subsequent strategies
- Iteration: Repeat until task completion
Capabilities:
- Independent reasoning and planning
- Dynamic tool chain discovery
- Self-correction and replanning
- Multi-source data synthesis
Example Use Case: Autonomous landslide risk assessment combining DEM analysis, rainfall data, soil properties, and land cover classification without predefined workflow.
Strengths: Highest autonomy, can handle novel tasks, adapts to changing conditions. Limitations: Lower task success rate (currently), higher token consumption, requires robust guardrails.
Agent Type Comparison Matrix
| Dimension | Knowledge Agent | Workflow Agent | Autonomous Agent |
|---|---|---|---|
| Autonomy Level | Low | Medium | High |
| Task Success Rate | High (>90%) | High (>85%) | Medium (60-75%) |
| Production Readiness | Production | Production | Experimental/Preview |
| Use Case Fit | Q&A, Search | Automation, Mapping | Novel Analysis |
| Human Oversight | Minimal | Workflow design | Heavy guardrails |
| Token Cost | Low | Medium | High |
| LLM Requirements | Standard | Standard | Advanced reasoning |
7. AI Agent Frameworks for GeoAI
7.1 Framework Comparison
| Framework | Architecture | Best For | Multi-Agent | Learning Curve | Geo Integration |
|---|---|---|---|---|---|
| LangChain/LangGraph | Modular / Graph-based | Complex workflows, RAG, stateful apps | Yes (via LangGraph) | Steep | Excellent (600+ integrations) |
| CrewAI | Role-based / Crew+Flow | Rapid prototyping, team-based tasks | Native | Easy | Good (Python-based) |
| AutoGen | Conversation-driven | Multi-agent collaboration, code execution | Native | Moderate | Moderate |
| Strands Agents | Event-driven | Geospatial tool binding, QGIS integration | Yes | Moderate | Native (GeoAgent) |
| Custom (FastAPI) | API-based | Production deployments, fine-grained control | Custom | Flexible | Full control |
7.2 LangChain / LangGraph for GeoAI
Strengths:
- 600+ integrations including major GIS libraries
- Graph-based stateful workflows (LangGraph)
- First-class persistence and checkpointing
- LangSmith observability
Best For: Complex, long-running geospatial workflows requiring state management and precise control over data flow.
Architecture Pattern:
# LangGraph-based GeoAI Agent (Conceptual)
from langgraph.graph import StateGraph, START, END
from langchain_core.tools import tool
# State definition
class GeoAgentState(TypedDict):
messages: Annotated[list, operator.add]
spatial_data: dict
analysis_results: dict
active_tools: list[str]
# Tool definitions
@tool
def buffer_geometry(layer_name: str, distance: float) -> str:
"""Buffer a geometry layer by specified distance."""
...
@tool
def spatial_join(target_layer: str, join_layer: str) -> str:
"""Perform spatial join between two layers."""
...
@tool
def clip_raster(raster_path: str, bounds: tuple) -> str:
"""Clip raster to specified bounds."""
...
# Graph construction
workflow = StateGraph(GeoAgentState)
workflow.add_node("understand_query", understand_node)
workflow.add_node("plan_analysis", planning_node)
workflow.add_node("execute_gis", ToolNode(gis_tools))
workflow.add_node("evaluate_results", evaluation_node)
workflow.add_edge(START, "understand_query")
workflow.add_conditional_edges("understand_query", route_to_planning)
workflow.add_conditional_edges("plan_analysis", route_to_execution)
workflow.add_edge("execute_gis", "evaluate_results")
workflow.add_conditional_edges("evaluate_results", should_continue)
geo_agent = workflow.compile()
7.3 CrewAI for GeoAI
Strengths:
- Fastest prototyping (working multi-agent in <100 lines)
- Role-based abstraction maps well to GIS team structures
- Sequential and Hierarchical execution patterns
- YAML configuration for non-developers
Best For: Pipeline workflows where different agents own different phases (data acquisition → processing → analysis → visualization).
Architecture Pattern:
# CrewAI Geo Crew Configuration (YAML)
agents:
data_acquisition_agent:
role: "Geospatial Data Acquisition Specialist"
goal: "Find and retrieve the most relevant spatial datasets for analysis"
backstory: "You are an expert in geospatial data sources, STAC catalogs, and API endpoints. You know exactly where to find elevation data, satellite imagery, and vector datasets."
tools:
- stac_search
- wfs_query
- data_download
spatial_analyst:
role: "Senior Spatial Analyst"
goal: "Perform rigorous geospatial analysis to extract meaningful insights"
backstory: "You are a GIS professional with 15 years of experience in spatial analysis, geoprocessing, and cartography."
tools:
- buffer_tool
- overlay_analysis
- interpolation
- cluster_analysis
cartographer:
role: "Professional Cartographer"
goal: "Create beautiful, informative maps that communicate findings clearly"
backstory: "You are an award-winning cartographer specializing in thematic mapping and data visualization."
tools:
- thematic_map
- symbolization
- layout_design
tasks:
acquire_data:
description: "Retrieve DEM, land cover, and infrastructure data for {study_area}"
agent: data_acquisition_agent
expected_output: "List of acquired datasets with metadata"
perform_analysis:
description: "Analyze flood risk using acquired datasets"
agent: spatial_analyst
context: [acquire_data]
expected_output: "Risk assessment results with statistics"
create_map:
description: "Generate final risk map with legend and annotations"
agent: cartographer
context: [perform_analysis]
expected_output: "Publication-ready map document"
7.4 AutoGen for GeoAI
Strengths:
- Native multi-agent conversation
- Mix-and-match LLM support (Claude for reasoning + GPT-4o for tools)
- Built-in sandboxed code execution
- Asynchronous event-driven architecture
Best For: Research automation, iterative analysis, and scenarios where agents need to collaborate dynamically.
7.5 Strands Agents / GeoAgent (Geospatial-Native)
GeoAgent is a shared AI agent layer specifically built for geospatial Python packages, live map widgets, and QGIS plugins.
Architecture:
┌─────────────────────────────────────────────────────────────┐
│ GeoAgent │
│ (Shared Geospatial Agent Layer) │
├─────────────────────────────────────────────────────────────┤
│ Core Concepts: │
│ · GeoAgent - High-level facade around Strands Agent│
│ · GeoAgentConfig - Provider, model, temperature settings │
│ · GeoAgentContext - Runtime objects (map, QGIS iface) │
│ · @geo_tool - Decorator for geospatial functions │
│ · GeoToolRegistry - Tool metadata, safety flags │
├─────────────────────────────────────────────────────────────┤
│ Factory Functions: │
│ · for_leafmap() - Bind to leafmap.Map │
│ · for_anymap() - Bind to anymap.Map │
│ · for_qgis() - Bind to QGIS iface/project │
│ · for_stac() - Bind to STAC catalog tools │
│ · for_nasa_opera() - Bind to NASA OPERA tools │
│ · create_agent() - Custom tool integration │
├─────────────────────────────────────────────────────────────┤
│ Provider Support: │
│ OpenAI · Anthropic · Google Gemini · Ollama · LiteLLM │
│ OpenRouter · vLLM · Amazon Bedrock · OpenAI Codex OAuth │
├─────────────────────────────────────────────────────────────┤
│ Package Integrations: │
│ leafmap · anymap · geoai · geemap · STAC · NASA Earthdata │
└─────────────────────────────────────────────────────────────┘
Quick Start Example:
import leafmap
from geoagent import for_leafmap
# Create map and bind agent
m = leafmap.Map()
agent = for_leafmap(m)
# Natural language geospatial interaction
resp = agent.chat("Add a marker for Knoxville and zoom to it.")
resp = agent.chat("Load the STAC catalog for Sentinel-2 and display imagery.")
# Display map
m
8. Tool Systems: MCP, RAG & Function Calling
8.1 Model Context Protocol (MCP)
MCP is an open protocol that standardizes how AI agents connect to external tools and services. It provides bidirectional communication (read + write + execute).

MCP vs RAG vs Agents:
| Dimension | MCP | RAG | AI Agent |
|---|---|---|---|
| Purpose | Connect LLM to tools | Retrieve knowledge | Orchestrate workflows |
| Direction | Bidirectional (R/W/E) | Read-only | Orchestration |
| Scope | Any API/tool/service | Document retrieval | End-to-end task |
| Analogy | The "how" (connection) | The "what" (knowledge) | The "who" (orchestrator) |
8.2 RAG for Geospatial Knowledge
Retrieval-Augmented Generation grounds agent responses in domain-specific geospatial knowledge.
User Query → Query Embedding → Vector Search → Context Retrieval → LLM + Context → Response
│ │
▼ ▼
┌──────────────┐ ┌──────────────────────┐
│ Embedding │ │ Geospatial KB │
│ Model │ │ · Documentation │
│ │ │ · Standards │
└──────────────┘ │ · Best Practices │
│ · Error Guides │
│ · Domain Papers │
└──────────────────────┘
8.3 GIS Tool Function Calling Schema
Example tool definitions that agents can invoke:
# GIS Tool Schema for LLM Function Calling
GIS_TOOLS = [
{
"name": "spatial_buffer",
"description": "Create buffer zones around input features",
"parameters": {
"type": "object",
"properties": {
"input_layer": {"type": "string", "description": "Input feature layer name or path"},
"distance": {"type": "number", "description": "Buffer distance in map units"},
"unit": {"type": "string", "enum": ["meters", "kilometers", "feet", "miles"]},
"dissolve": {"type": "boolean", "description": "Dissolve overlapping buffers"}
},
"required": ["input_layer", "distance"]
}
},
{
"name": "spatial_intersection",
"description": "Compute geometric intersection of two layers",
"parameters": {
"type": "object",
"properties": {
"layer_a": {"type": "string"},
"layer_b": {"type": "string"},
"output_layer": {"type": "string"}
},
"required": ["layer_a", "layer_b"]
}
},
{
"name": "extract_by_location",
"description": "Select features based on spatial relationship",
"parameters": {
"type": "object",
"properties": {
"target_layer": {"type": "string"},
"source_layer": {"type": "string"},
"relationship": {
"type": "string",
"enum": ["intersect", "within", "contains", "within_distance", "bounds"]
},
"distance": {"type": "number", "description": "Search distance for within_distance"}
},
"required": ["target_layer", "source_layer", "relationship"]
}
},
{
"name": "zonal_statistics",
"description": "Calculate statistics for raster within zones",
"parameters": {
"type": "object",
"properties": {
"zone_layer": {"type": "string", "description": "Zone polygon layer"},
"value_raster": {"type": "string", "description": "Input raster dataset"},
"statistics": {
"type": "array",
"items": {"enum": ["mean", "max", "min", "sum", "std", "count", "majority"]},
"description": "Statistics to calculate"
}
},
"required": ["zone_layer", "value_raster"]
}
},
{
"name": "geocode_addresses",
"description": "Convert addresses to point features",
"parameters": {
"type": "object",
"properties": {
"addresses": {"type": "array", "items": {"type": "string"}},
"country_code": {"type": "string", "description": "ISO country code for bias"},
"output_fields": {"type": "array", "items": {"type": "string"}}
},
"required": ["addresses"]
}
},
{
"name": "generate_thematic_map",
"description": "Create a thematic/choropleth map from a dataset",
"parameters": {
"type": "object",
"properties": {
"layer": {"type": "string"},
"attribute": {"type": "string", "description": "Field to visualize"},
"method": {"type": "string", "enum": ["natural_breaks", "equal_interval", "quantile", "manual"]},
"classes": {"type": "integer", "description": "Number of classes (default: 5)"},
"color_scheme": {"type": "string", "description": "Color brewer scheme name"},
"output_format": {"type": "string", "enum": ["png", "svg", "pdf", "web"], "default": "web"}
},
"required": ["layer", "attribute"]
}
}
]
9. Agent Design Patterns
9.1 ReAct (Reason + Act) Pattern
The most common pattern for autonomous geospatial agents:

9.2 Routing Patterns
| Pattern | Description | Use Case |
|---|---|---|
| Rule-based | Hardcoded rules trigger specific functions | Simple keyword detection |
| Semantic | LLM understands intent to route requests | Natural language queries |
| Hierarchical | Master agent delegates to sub-agents | Complex multi-domain analysis |
| LLM-based | LLM decides which tool/agent to call | Full autonomy |
9.3 Multi-Agent Patterns

9.4 Reflective Agent Pattern
| Stage | Description |
|---|---|
| Execute | Run the geoprocessing task |
| Output | Capture raw results |
| Critic Review | Evaluate: spatial accuracy, methodology, completeness, edge cases |
| Improve Plan | Refine approach based on critique |
| Iterate | Repeat N times until quality threshold met |
Execute Task → Output Results → Critic Review → Improve Plan → (iterate)
10. Code Implementation Examples
10.1 Complete GeoAI Agent with LangGraph
"""
GeoAI Agent - Complete Implementation
A production-ready geospatial AI agent using LangGraph
"""
import operator
from typing import Annotated, TypedDict, Sequence
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage, ToolMessage
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode
import json
# ============================================================================
# STATE DEFINITION
# ============================================================================
class GeoAgentState(TypedDict):
"""State object flowing through the agent graph."""
messages: Annotated[Sequence[BaseMessage], operator.add]
active_skills: list[str]
spatial_context: dict
execution_log: list[dict]
requires_human: bool
# ============================================================================
# GEOSPATIAL TOOLS
# ============================================================================
@tool
def buffer_analysis(
layer_name: str,
distance: float,
unit: str = "meters",
dissolve: bool = False
) -> str:
"""
Create buffer zones around features in a layer.
Args:
layer_name: Name of the input feature layer
distance: Buffer distance value
unit: Unit of distance (meters, kilometers, feet, miles)
dissolve: Whether to dissolve overlapping buffers
Returns:
Summary of buffer operation results
"""
# Implementation would call ArcPy, GeoPandas, or GIS API
result = {
"operation": "buffer",
"input_layer": layer_name,
"distance": f"{distance} {unit}",
"features_created": 47,
"total_area_km2": 12.5,
"dissolved": dissolve,
"output_layer": f"{layer_name}_buffer_{distance}{unit[0]}"
}
return json.dumps(result, indent=2)
@tool
def spatial_intersection(layer_a: str, layer_b: str, output_name: str = None) -> str:
"""
Compute geometric intersection between two layers.
Args:
layer_a: First input layer
layer_b: Second input layer
output_name: Optional output layer name
Returns:
Summary of intersection results
"""
result = {
"operation": "intersection",
"layer_a": layer_a,
"layer_b": layer_b,
"features_output": 23,
"output_layer": output_name or f"{layer_a}_intersect_{layer_b}"
}
return json.dumps(result, indent=2)
@tool
def extract_by_attribute(layer: str, field: str, operator: str, value) -> str:
"""
Select features by attribute query.
Args:
layer: Input layer name
field: Field name to query
operator: Comparison operator (=, <>, <, >, <=, >=, LIKE)
value: Value to compare against
Returns:
Summary of selected features
"""
result = {
"operation": "attribute_selection",
"layer": layer,
"query": f"{field} {operator} {value}",
"features_selected": 156,
"total_features": 500
}
return json.dumps(result, indent=2)
@tool
def zonal_statistics(zone_layer: str, value_raster: str, statistics: list) -> str:
"""
Calculate statistics for raster values within zone polygons.
Args:
zone_layer: Zone polygon layer name
value_raster: Input raster dataset path
statistics: List of statistics (mean, max, min, sum, std, count)
Returns:
Statistics table as JSON
"""
result = {
"operation": "zonal_statistics",
"zone_layer": zone_layer,
"value_raster": value_raster,
"statistics": {
stat: round(10 + hash(f"{zone_layer}{stat}") % 90, 2)
for stat in statistics
}
}
return json.dumps(result, indent=2)
@tool
def generate_map(layer: str, attribute: str, method: str = "natural_breaks", classes: int = 5) -> str:
"""
Generate a thematic map from a layer.
Args:
layer: Layer to visualize
attribute: Attribute field to map
method: Classification method
classes: Number of classes
Returns:
Map generation summary
"""
result = {
"operation": "thematic_map",
"layer": layer,
"attribute": attribute,
"classification": method,
"classes": classes,
"map_url": f"/maps/{layer}_{attribute}_{method}.html",
"legend": f"Generated {classes}-class {method} legend for {attribute}"
}
return json.dumps(result, indent=2)
# Tool registry
GIS_TOOLS = [buffer_analysis, spatial_intersection, extract_by_attribute,
zonal_statistics, generate_map]
# ============================================================================
# LLM INITIALIZATION
# ============================================================================
# Primary reasoning model (capable)
llm = ChatOpenAI(model="gpt-4o", temperature=0)
llm_with_tools = llm.bind_tools(GIS_TOOLS)
# ============================================================================
# GRAPH NODES
# ============================================================================
def reasoning_node(state: GeoAgentState):
"""Central intelligence node - analyzes state and determines next action."""
response = llm_with_tools.invoke(state["messages"])
return {
"messages": [response],
"execution_log": state.get("execution_log", []) + [{
"node": "reasoning",
"tool_calls": response.tool_calls if hasattr(response, 'tool_calls') else []
}]
}
def planning_node(state: GeoAgentState):
"""Breaks down complex geospatial tasks into executable steps."""
planner_prompt = """You are a geospatial task planner. Given a user request,
break it down into specific GIS operations.
Available operations:
- buffer_analysis: Create buffer zones
- spatial_intersection: Find overlapping areas
- extract_by_attribute: Filter by attributes
- zonal_statistics: Calculate raster stats by zone
- generate_map: Create thematic visualizations
Provide your plan as a numbered list of tool calls with specific parameters."""
messages = state["messages"] + [SystemMessage(content=planner_prompt)]
response = llm.invoke(messages)
return {"messages": [response]}
def human_review_node(state: GeoAgentState):
"""Human-in-the-loop checkpoint for critical operations."""
return {"requires_human": True, "messages": state["messages"]}
# ============================================================================
# ROUTING LOGIC
# ============================================================================
def router(state: GeoAgentState) -> str:
"""Inspects last message to determine next operational node."""
last_message = state["messages"][-1]
# Route to tool execution if tool calls present
if hasattr(last_message, 'tool_calls') and last_message.tool_calls:
return "execute_tools"
# Check for completion signal
content = last_message.content.lower() if hasattr(last_message, 'content') else ""
if "final answer" in content or "task complete" in content:
return "end"
# Check if human review needed
if state.get("requires_human", False):
return "human_review"
return "end"
def should_human_review(state: GeoAgentState) -> str:
"""Determine if destructive or expensive operations need approval."""
last_message = state["messages"][-1]
if hasattr(last_message, 'tool_calls'):
for tc in last_message.tool_calls:
if tc.get("name") in ["buffer_analysis", "spatial_intersection"]:
# Auto-approve standard operations
return "execute_tools"
return "execute_tools"
# ============================================================================
# GRAPH CONSTRUCTION
# ============================================================================
workflow = StateGraph(GeoAgentState)
# Add nodes
workflow.add_node("reason", reasoning_node)
workflow.add_node("plan", planning_node)
workflow.add_node("execute_tools", ToolNode(GIS_TOOLS))
workflow.add_node("human_review", human_review_node)
# Define edges
workflow.add_edge(START, "reason")
# Conditional routing from reasoning
workflow.add_conditional_edges(
"reason",
router,
{
"execute_tools": "execute_tools",
"human_review": "human_review",
"end": END
}
)
# Return paths
workflow.add_edge("execute_tools", "reason")
workflow.add_edge("human_review", "reason")
workflow.add_edge("plan", "reason")
# Compile
geo_agent = workflow.compile()
# ============================================================================
# USAGE EXAMPLE
# ============================================================================
if __name__ == "__main__":
# Example query
query = """
I need to identify flood risk areas for schools near the river.
1. Find all schools within 500 meters of the river
2. Create 500m buffers around the river
3. Intersect buffers with school locations
4. Generate a risk map
"""
initial_state = {
"messages": [HumanMessage(content=query)],
"active_skills": [],
"spatial_context": {},
"execution_log": [],
"requires_human": False
}
result = geo_agent.invoke(initial_state)
print("Agent Response:")
print(result["messages"][-1].content)
10.2 CrewAI Multi-Agent Geospatial Team
"""
GeoAI Crew - Multi-Agent Geospatial Analysis Team
Uses CrewAI for collaborative spatial analysis
"""
from crewai import Agent, Task, Crew, Process
from crewai.tools import tool
import os
# ============================================================================
# TOOLS
# ============================================================================
@tool
def search_stac_catalog(query: str, bbox: list, date_range: str) -> str:
"""Search STAC catalogs for satellite imagery."""
return f"Found 12 Sentinel-2 scenes for {query} in bbox {bbox} from {date_range}"
@tool
def run_geoprocessing(tool_name: str, inputs: dict) -> str:
"""Execute a geoprocessing tool."""
return f"Executed {tool_name} with inputs: {inputs}. Output: result.shp"
@tool
def classify_land_cover(image_path: str, model: str) -> str:
"""Run land cover classification on imagery."""
return f"Classified {image_path} using {model}. 8 classes identified."
@tool
def generate_report(analysis_results: str, template: str) -> str:
"""Generate analysis report from results."""
return f"Report generated using {template}. Contains: {analysis_results}"
# ============================================================================
# AGENTS
# ============================================================================
data_specialist = Agent(
role="Geospatial Data Specialist",
goal="Acquire and prepare the highest quality spatial data for analysis",
backstory="""You are an expert geospatial data engineer with 10+ years of experience
in remote sensing, satellite imagery, and spatial data infrastructure. You know every
STAC catalog, OGC service, and geospatial API. You excel at finding, downloading, and
preprocessing spatial data for analysis workflows.""",
tools=[search_stac_catalog],
verbose=True,
allow_delegation=False
)
spatial_analyst = Agent(
role="Senior Spatial Analyst",
goal="Perform rigorous geospatial analysis to extract actionable insights",
backstory="""You are a senior GIS analyst specializing in environmental modeling,
spatial statistics, and geoprocessing. You have deep expertise in raster analysis,
vector operations, and spatial modeling. You always validate your results and document
your methodology thoroughly.""",
tools=[run_geoprocessing, classify_land_cover],
verbose=True,
allow_delegation=False
)
report_writer = Agent(
role="Geospatial Report Writer",
goal="Create clear, comprehensive reports that communicate findings effectively",
backstory="""You are a technical writer specializing in geospatial analysis documentation.
You transform complex spatial analysis results into clear, actionable reports with
beautiful maps and visualizations. Your reports are used by decision-makers at all levels.""",
tools=[generate_report],
verbose=True,
allow_delegation=False
)
# ============================================================================
# TASKS
# ============================================================================
task_acquire_data = Task(
description="""
Search for and acquire satellite imagery for the study area (bounding box:
[-95.5, 29.5, -95.0, 30.0]) covering January-June 2024.
Find Sentinel-2 L2A products with \<10% cloud cover.
Also search for elevation data (DEM) and land cover reference data.
Return a complete inventory of available datasets with their IDs and metadata.
""",
agent=data_specialist,
expected_output="A structured list of all acquired datasets with IDs, dates, and quality metrics"
)
task_analyze = Task(
description="""
Using the acquired satellite imagery, perform the following analysis:
1. Preprocess imagery (atmospheric correction, cloud masking)
2. Calculate NDVI vegetation index
3. Perform land cover classification using supervised classification
4. Identify areas of vegetation change between time periods
5. Generate statistics: total area by land cover class, change detection summary
Document all parameters and methods used.
""",
agent=spatial_analyst,
context=[task_acquire_data],
expected_output="Analysis results with methodology documentation and key statistics"
)
task_report = Task(
description="""
Create a comprehensive geospatial analysis report including:
1. Executive summary of findings
2. Methodology section with all parameters
3. Results with tabulated statistics
4. Visual description of map outputs
5. Recommendations based on analysis
Format as a professional technical report suitable for stakeholder review.
""",
agent=report_writer,
context=[task_analyze],
expected_output="Complete technical report in markdown format with all sections"
)
# ============================================================================
# CREW EXECUTION
# ============================================================================
crew = Crew(
agents=[data_specialist, spatial_analyst, report_writer],
tasks=[task_acquire_data, task_analyze, task_report],
process=Process.sequential,
verbose=True
)
if __name__ == "__main__":
result = crew.kickoff()
print("\n" + "="*60)
print("FINAL REPORT")
print("="*60)
print(result)
10.3 GeoAgent with Live Map Integration
"""
GeoAgent Live Map Example
Interactive geospatial AI agent bound to a live map widget
"""
import leafmap
from geoagent import for_leafmap, GeoAgentConfig
# Create map
m = leafmap.Map(center=[35.9606, -83.9207], zoom=10)
# Configure agent with specific provider
config = GeoAgentConfig(
provider="openai",
model="gpt-4o",
temperature=0.1
)
# Bind agent to map
agent = for_leafmap(m, config=config)
# Interactive geospatial queries
queries = [
"Add a marker for Knoxville, Tennessee and zoom to it",
"Change the basemap to satellite imagery",
"Load the OpenStreetMap buildings layer for this area",
"Set the opacity of the buildings layer to 0.6",
"Add a buffer of 1km around the Knoxville marker",
"List all layers currently on the map",
"Save the current map view as map_export.png"
]
# Execute queries
for query in queries:
print(f"\n🗣️ User: {query}")
response = agent.chat(query)
print(f"🤖 Agent: {response.answer_text}")
# Display interactive map
m
10.4 MCP Server for GIS Tools
"""
GIS MCP Server - Expose geoprocessing tools via Model Context Protocol
"""
from mcp.server import Server
from mcp.types import Tool, TextContent
import json
# Initialize MCP server
server = Server("gis-mcp-server")
# Define GIS tools exposed via MCP
@server.list_tools()
async def list_tools():
return [
Tool(
name="buffer_geometry",
description="Create buffer zones around input geometries",
inputSchema={
"type": "object",
"properties": {
"layer": {"type": "string"},
"distance": {"type": "number"},
"unit": {"type": "string", "enum": ["m", "km", "ft", "mi"]}
},
"required": ["layer", "distance"]
}
),
Tool(
name="spatial_query",
description="Query features by spatial relationship",
inputSchema={
"type": "object",
"properties": {
"target_layer": {"type": "string"},
"query_layer": {"type": "string"},
"relationship": {"type": "string", "enum": ["intersects", "within", "contains", "near"]}
},
"required": ["target_layer", "query_layer", "relationship"]
}
),
Tool(
name="raster_analysis",
description="Perform raster analysis operations",
inputSchema={
"type": "object",
"properties": {
"operation": {"type": "string", "enum": ["clip", "reclassify", "calculate_slope", "calculate_aspect"]},
"input_raster": {"type": "string"},
"parameters": {"type": "object"}
},
"required": ["operation", "input_raster"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
"""Execute GIS tool calls from MCP clients."""
if name == "buffer_geometry":
result = execute_buffer(
layer=arguments["layer"],
distance=arguments["distance"],
unit=arguments.get("unit", "m")
)
return [TextContent(type="text", text=json.dumps(result))]
elif name == "spatial_query":
result = execute_spatial_query(
target=arguments["target_layer"],
query=arguments["query_layer"],
relationship=arguments["relationship"]
)
return [TextContent(type="text", text=json.dumps(result))]
elif name == "raster_analysis":
result = execute_raster_op(
operation=arguments["operation"],
raster=arguments["input_raster"],
params=arguments.get("parameters", {})
)
return [TextContent(type="text", text=json.dumps(result))]
else:
raise ValueError(f"Unknown tool: {name}")
def execute_buffer(layer: str, distance: float, unit: str):
"""Execute buffer geoprocessing."""
return {
"status": "success",
"operation": "buffer",
"input": {"layer": layer, "distance": distance, "unit": unit},
"output_layer": f"{layer}_buffer_{distance}{unit}",
"features_created": 47,
"area_km2": 12.5
}
def execute_spatial_query(target: str, query: str, relationship: str):
"""Execute spatial query."""
return {
"status": "success",
"operation": "spatial_query",
"relationship": relationship,
"features_selected": 23,
"total_features": 150
}
def execute_raster_op(operation: str, raster: str, params: dict):
"""Execute raster analysis."""
return {
"status": "success",
"operation": operation,
"input_raster": raster,
"output_raster": f"{raster}_{operation}.tif",
"parameters": params
}
if __name__ == "__main__":
import asyncio
from mcp.server.stdio import stdio_server
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
asyncio.run(main())
11. Real-World Use Cases
11.1 Disaster Response & Emergency Management
Scenario: Post-hurricane damage assessment
┌─────────────────────────────────────────────────────────────────┐
│ AUTONOMOUS DISASTER RESPONSE AGENT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ INPUT: "Assess flood damage in affected coastal region" │
│ │
│ STEP 1: Data Acquisition │
│ ├── Retrieve pre-event satellite imagery (Sentinel-2) │
│ ├── Retrieve post-event satellite imagery (Maxar) │
│ ├── Collect DEM for flood modeling │
│ └── Load critical infrastructure layer (hospitals, roads) │
│ │
│ STEP 2: Change Detection │
│ ├── Run deep learning change detection model │
│ ├── Identify destroyed buildings │
│ └── Detect road blockages │
│ │
│ STEP 3: Flood Modeling │
│ ├── Run hydrological model with rainfall data │
│ ├── Generate flood extent polygons │
│ └── Calculate flood depth raster │
│ │
│ STEP 4: Impact Analysis │
│ ├── Intersect flood zones with population data │
│ ├── Identify affected critical facilities │
│ └── Calculate population at risk │
│ │
│ STEP 5: Report Generation │
│ ├── Generate damage assessment map │
│ ├── Prioritize rescue routes │
│ └── Create situation report with statistics │
│ │
│ OUTPUT: Damage map, prioritized response plan, statistics │
└─────────────────────────────────────────────────────────────────┘
11.2 Urban Planning & Smart Cities
Scenario: Automated zoning compliance checking
| Agent | Task | Tools Used |
|---|---|---|
| Data Agent | Retrieve building permits, zoning layers, height restrictions | WFS queries, attribute extraction |
| Analysis Agent | Check compliance: setbacks, height limits, density | Overlay, buffer, spatial join |
| Reporting Agent | Generate compliance report with maps | Thematic mapping, layout generation |
11.3 Environmental Monitoring
Scenario: Deforestation early warning system
# Conceptual workflow
def deforestation_monitor():
"""
Autonomous monitoring agent that:
1. Ingests monthly Sentinel-2 imagery via STAC
2. Runs random forest classifier for forest/non-forest
3. Compares with baseline land cover
4. Detects anomalous clearing events
5. Alerts authorities with precise locations
6. Generates enforcement priority map
"""
pass
11.4 Infrastructure Inspection
Application: Automated road condition assessment
- Input: Aerial/drone imagery
- Agent Pipeline: Image ingestion → crack detection AI → severity classification → priority map → work order generation
- Impact: 90% reduction in manual inspection time
11.5 Insurance Risk Assessment
Application: Property risk scoring
- Data: Elevation, flood zones, historical claims, building footprints, vegetation proximity
- Agent: Autonomous multi-factor risk model with spatial interpolation
- Output: Risk surface with per-property scores
12. GIS Platforms with AI Agent Integration
12.1 ArcGIS (Esri)
| Component | Description | Status |
|---|---|---|
| AI Framework | Modular foundation for AI capabilities | Production |
| AI Assistants | Natural language UX across products | Multiple in Beta |
| AI Agents | Workflow orchestration components | Preview |
| GeoAI Tools | Pre-trained ML models for spatial analysis | Production |
| Notebook Assistant | Python code generation for ArcPy | Beta |
| Arcade Assistant | Expression writing helper | Beta |
Key Features:
- 75+ pre-trained models for common GIS workflows
- AI framework supports both hosted and bring-your-own-model
- Cross-product assistants (Map Viewer, Pro, Online)
- Trusted AI principles: Security, Privacy, Transparency, Fairness, Reliability, Accountability
12.2 CARTO
| Feature | Description |
|---|---|
| AI Agents | Public preview, natural language map interaction |
| Context Layer | Automatic map configuration, tool definitions |
| Agent Configuration Assistant | Conversational agent setup |
| MCP Tools | CARTO MCP Server for geospatial operations |
12.3 SuperMap AgentX Server
| Feature | Specification |
|---|---|
| LLM Support | Qwen series, DeepSeek series |
| MCP Tools | 200+ operators (iServer), 60+ operators (iPortal) |
| Agent Types | Knowledge, Workflow, Autonomous |
| Architecture | x86, ARM (Kunpeng), multi-OS |
| Database | Yugong Spatial Database (co-developed with Huawei) |
12.4 Open Source Ecosystem
| Project | Description | Link |
|---|---|---|
| GeoAgent | Shared agent layer for geospatial Python | github.com/opengeos/GeoAgent |
| GeoAI | Python package for AI + geospatial analysis | opengeoai.org |
| Leafmap | Interactive mapping with AI integration | leafmap.org |
| TorchGeo | Deep learning for geospatial data | torchgeo.readthedocs.io |
| GeoAI-ML | ML toolkit for ArcGIS / open source | esri.github.io/geoai-ml |
13. Building Your First GeoAI Agent
Step-by-Step Guide
Step 1: Define Your Use Case
├── What geospatial problem are you solving?
├── Who are the users?
├── What data sources are needed?
└── What is the desired output?
Step 2: Choose Your Architecture
├── Single agent or multi-agent?
├── Knowledge, Workflow, or Autonomous?
├── Which framework (LangChain, CrewAI, AutoGen, Custom)?
└── LLM provider (OpenAI, Anthropic, Local)?
Step 3: Design Your Tools
├── List all GIS operations needed
├── Define tool schemas (name, params, returns)
├── Implement tool functions
└── Register with agent
Step 4: Build Context Layer
├── Data catalog with field descriptions
├── Spatial reference system definitions
├── Domain knowledge (RAG or fine-tuning)
└── Example queries and expected outputs
Step 5: Implement Agent Logic
├── Define state management
├── Set up routing/decision logic
├── Add memory and context handling
└── Configure error handling and retries
Step 6: Add Safety & Guardrails
├── Confirmation for destructive operations
├── Token and cost limits
├── Human-in-the-loop checkpoints
└── Output validation
Step 7: Test & Iterate
├── Unit tests for individual tools
├── Integration tests for workflows
├── User acceptance testing
└── Performance optimization
Step 8: Deploy & Monitor
├── API endpoint or web app
├── Logging and observability
├── Usage analytics
└── Continuous improvement loop
14. Production Considerations
14.1 Cost Management
| Strategy | Implementation | Savings |
|---|---|---|
| Model Tiering | Use GPT-4o for reasoning, GPT-4o-mini for execution | 50-70% |
| Tool Limiting | Start with 3-5 tools, expand as needed | 30-40% |
| Caching | Cache frequent queries and results | 20-30% |
| Max Iterations | Set hard limits on agent loops | Prevents runaway costs |
| Async Processing | Queue expensive operations | Better resource use |
14.2 Reliability Patterns
┌─────────────────────────────────────────────────────────────────┐
│ PRODUCTION SAFEGUARDS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. OBSERVABILITY │
│ ├── Log every LLM call with prompt and response │
│ ├── Track tool invocations and results │
│ ├── Monitor token usage and costs │
│ └── Measure latency per operation │
│ │
│ 2. ERROR HANDLING │
│ ├── Retry with exponential backoff │
│ ├── Circuit breaker for failing tools │
│ ├── Graceful degradation (partial results) │
│ └── Fallback to human escalation │
│ │
│ 3. SAFETY GUARDRAILS │
│ ├── Confirmation gates for destructive ops │
│ ├── Rate limiting on tool calls │
│ ├── Input sanitization and validation │
│ └── Output schema validation │
│ │
│ 4. HUMAN-IN-THE-LOOP │
│ ├── Review checkpoints for critical analysis │
│ ├── Approval workflow for data publication │
│ ├── Override capability for expert users │
│ └── Feedback collection for improvement │
│ │
└─────────────────────────────────────────────────────────────────┘
14.3 Performance Optimization
| Technique | When to Use |
|---|---|
| Fast Mode | Quick map controls, simple queries |
| Streaming | Long-running analysis for UX feedback |
| Parallel Tool Calls | Independent operations |
| Progressive Disclosure | Load skills only when needed |
| Vector Search | Large tool registries |
14.4 Security Checklist
- API keys stored in secure vault (not code)
- Rate limiting enabled on all endpoints
- Input validation on all tool parameters
- SQL injection prevention for database tools
- Output encoding for web display
- Audit logging for all agent actions
- Data privacy compliance (GDPR, etc.)
- Access control for sensitive spatial data
- Model poisoning detection
- Dependency vulnerability scanning
15. Future Outlook
2026-2030 Roadmap
2026 ──► Production workflow agents become mainstream
├── Multi-modal agents (text + map + image + 3D)
├── Standardized MCP tool ecosystem for GIS
└── Real-time streaming spatial analysis
2027 ──► Autonomous agents reach 85%+ task success rate
├── Edge deployment for field devices
├── Cross-platform agent interoperability
└── Self-improving agents from feedback
2028 ──► Agent marketplaces for geospatial skills
├── Domain-specific agents (cadastral, utilities, etc.)
├── Federated multi-organization analysis
└── Digital twin integration
2029+ ──► Fully autonomous geospatial operations centers
├── Predictive maintenance for infrastructure
├── Self-updating spatial databases
└── AI-driven urban planning recommendations
Emerging Trends
| Trend | Impact |
|---|---|
| Agentic RAG (GeoAgentic-RAG) | Multi-agent retrieval with spatial reasoning |
| MCP Ecosystem Standardization | Universal GIS tool connectors |
| Local LLM Deployment | Edge AI for sensitive geospatial data |
| Multi-Modal Vision | Agents that interpret maps, charts, and satellite imagery |
| Federated Agents | Cross-organizational spatial analysis without data sharing |
16. Resources & References
Official Documentation
Research Papers
- Liang et al. (2026). GeoAgentic-RAG: A Multi-Agent framework for autonomous geospatial analysis. Science of Remote Sensing.
- ACM SIGSPATIAL (2025). GeoAgent: An Agentic AI Framework for Spatial Query Understanding.
- Song Guanfu (2025). Exploring Geospatial AI Agent Technology. SuperMap.
Framework Documentation
- LangChain Documentation
- LangGraph Documentation
- CrewAI Documentation
- AutoGen Documentation
- Strands Agents
Learning Resources
- Cognitive Class: AI-Powered Agent for Geospatial Insights
- CARTO Academy: AI Agents Tutorial
- Esri Training: GeoAI Fundamentals
Community
- GeoAI Slack / Discord communities
- r/gis and r/AI_Agents on Reddit
- Stack Overflow: [geoai] and [langchain] tags
17. Glossary
| Term | Definition |
|---|---|
| Agent | An AI system that autonomously plans and executes tasks using tools |
| CrewAI | Role-based multi-agent orchestration framework |
| GeoAI | Integration of geospatial data and AI techniques |
| GIS | Geographic Information System |
| LangChain | Modular framework for LLM applications |
| LangGraph | Graph-based stateful agent framework (LangChain extension) |
| LLM | Large Language Model |
| MCP | Model Context Protocol - standardized tool connection |
| RAG | Retrieval-Augmented Generation |
| ReAct | Reason + Act agent pattern |
| RS | Remote Sensing |
| STAC | SpatioTemporal Asset Catalog |
| Tool Calling | LLM invoking external functions |
| Vector Store | Database for semantic search embeddings |
| Workflow Agent | Agent executing predefined analysis pipelines |
Contributing: This guide is a living document. Contributions, corrections, and additions are welcome via pull requests.
License: This document is shared under CC BY-SA 4.0.
Disclaimer: AI agent capabilities evolve rapidly. Verify current feature availability with respective platform vendors before production deployment.
Discussion
