📘 Examples¶
This page provides end-to-end example workflows showcasing GeoPandas-AI in action, including how to extend it with custom backends (cache, injector, descriptor, executor).
1. Interactive Visualization & Refinement¶
import geopandasai as gpdai
# Load a GeoJSON of world capitals
gdfai = gpdai.read_file("data/world_capitals.geojson")
# 1.1 Plot capitals sized by population
fig1 = gdfai.chat("Create a scatter plot of world capitals, sizing points by population")
fig1
# 1.2 Improve: add title, legend, and basemap
fig2 = gdfai.improve(
"Add a descriptive title, show a legend for population size, and overlay a contextily basemap"
)
fig2
````
---
## 2. Filtering & Tabular Output
```python
from geopandasai import GeoDataFrameAI
import geopandas as gpd
# Load neighborhoods dataset
gdf = gpd.read_file("data/neighborhoods.geojson")
gdfai = GeoDataFrameAI(gdf, description="Neighborhood polygons with demographics")
# 2.1 List neighborhoods with population density > 5000 people/km²
high_density = gdfai.chat(
"Return a GeoDataFrame of neighborhoods with density over 5000 people per square kilometer",
return_type=type(gdf) # returns a GeoDataFrame
)
high_density.head()
3. Multi-Dataset Spatial Join¶
# Load two datasets
schools = gpdai.read_file("data/schools.geojson")
zones = gpdai.read_file("data/zoning.geojson")
# Describe each
schools.set_description("Point locations of public schools")
zones.set_description("Polygon boundaries of administrative zones")
# 3.1 Count schools per zone
count_df = schools.chat(
"For each zone, count how many schools fall within it and return a DataFrame with zone_id and count",
zones,
return_type=type(zones) # DataFrame or GeoDataFrame
)
count_df.head()
4. Clustering & Map Styling¶
# Cluster city crime incidents
incidents = gpdai.read_file("data/crime_incidents.geojson")
incidents.set_description("Crime incident points with latitude, longitude, and type")
# 4.1 Cluster incidents into 4 clusters using KMeans
clusters = incidents.chat(
"Cluster the crime incidents into 4 spatial clusters using scikit-learn",
provided_libraries=["scikit-learn"],
return_type=int
)
# clusters is a pandas Series of cluster labels
# 4.2 Visualize clusters with distinct colors
incidents.improve(
"Plot the incident points colored by cluster label and add a legend"
)
5. Caching & Reuse¶
# Running the same prompt twice uses cache
parks = gpdai.read_file("data/parks.geojson")
parks.set_description("City park polygons with area")
# First run invokes LLM
parks.chat("Plot parks with area > 100 hectares")
# Second run reuses cached result (no LLM call)
parks.chat("Plot parks with area > 100 hectares")
6. Code Inspection & Injection¶
# Inspect last generated code
print(parks.code)
# Inject the function into ai.py for reuse
parks.inject("plot_large_parks")
# In another script or cell:
import ai
ai.plot_large_parks(parks)
7. Custom Configuration¶
from geopandasai import update_geopandasai_config
from geopandasai.external.cache.backend.file_system import FileSystemCacheBackend
from geopandasai.services.inject.injectors.print_inject import PrintCodeInjector
from geopandasai.services.code.executor import TrustedCodeExecutor
# Redirect cache to custom folder and use print injector
update_geopandasai_config(
cache_backend=FileSystemCacheBackend(cache_dir="custom_cache"),
injector=PrintCodeInjector(),
executor=TrustedCodeExecutor()
)
8. Extending GeoPandas-AI with Custom Backends¶
8.1 Custom Code Injector¶
Subclass ACodeInjector
to define how code is inserted into your project. For example, the built-in PythonCodeInjector
searches your source file for a pattern and replaces it:
import inspect, re
from typing import Callable, Optional
from geopandasai.services.inject.injectors.base import ACodeInjector
class PythonCodeInjector(ACodeInjector):
def inject(
self,
pattern: re.Pattern,
function_call_builder: Callable[[Optional[re.Match]], str],
import_statement: str,
):
# Locate the calling file
frame = inspect.currentframe()
filename = frame.f_back.f_back.f_back.f_code.co_filename
with open(filename, "r") as f:
code = f.read()
# Find the first match
match = pattern.search(code)
if not match:
raise ValueError("Pattern not found in code.")
# Add import if missing
if import_statement not in code:
code = f"{import_statement}\n{code}"
# Replace the matched call with your generated function call
new_code = code.replace(match.group(0), function_call_builder(match))
with open(filename, "w") as f:
f.write(new_code)
Configure GeoPandas-AI to use it:
from geopandasai import update_geopandasai_config
from mymodule.injectors import PythonCodeInjector
update_geopandasai_config(injector=PythonCodeInjector())
8.2 Custom Data Descriptor¶
Subclass ADescriptor
to control the data summary sent to the LLM. Example: PublicDataDescriptor
includes schema, stats, and sample rows:
import geopandas as gpd
import pandas as pd
from geopandasai.services.description.descriptor.base import ADescriptor
from geopandasai.shared.return_type import type_to_literal
class PublicDataDescriptor(ADescriptor):
def __init__(self, sample_rows: int = 20):
super().__init__()
self.sample_rows = sample_rows
def describe(self, instance) -> str:
desc = f"Type: {type_to_literal(type(instance))}\n"
if isinstance(instance, gpd.GeoDataFrame):
desc += f"CRS: {instance.crs}\n"
desc += f"Geometry types: {', '.join(instance.geometry.geom_type.unique())}\n"
desc += f"Shape: {instance.shape}\n"
desc += instance.describe().to_string() + "\n"
sample = instance.sample(min(len(instance), self.sample_rows), random_state=42)
desc += "Sample rows:\n" + sample.to_string(index=False)
return desc
Activate it:
from geopandasai import update_geopandasai_config
from mymodule.descriptors import PublicDataDescriptor
update_geopandasai_config(descriptor=PublicDataDescriptor(sample_rows=10))
8.3 Custom Code Executor¶
Subclass TrustedCodeExecutor
to add safety checks or confirmations. Example: UntrustedCodeExecutor
shows code with syntax highlighting and asks for user approval before executing:
import re
from colorama import Fore, Style
from geopandasai.services.code.executor.trusted import TrustedCodeExecutor
def highlight(code: str) -> str:
# Simple color highlighting for Python keywords and strings
code = re.sub(r"\b(def|return|if|else|import|from)\b",
lambda m: Fore.BLUE + m.group(0) + Style.RESET_ALL, code)
code = re.sub(r"(\".*?\"|\'.*?\')",
lambda m: Fore.GREEN + m.group(0) + Style.RESET_ALL, code)
return code
class UntrustedCodeExecutor(TrustedCodeExecutor):
def execute(self, code: str, return_type, *dfs):
print(highlight(code))
confirm = input("Execute this code? (y/N): ").lower() == "y"
if not confirm:
raise RuntimeError("Execution aborted by user.")
return super().execute(code, return_type, *dfs)
Use it:
from geopandasai import update_geopandasai_config
from mymodule.executors import UntrustedCodeExecutor
update_geopandasai_config(executor=UntrustedCodeExecutor())
❗ Tip: After customizing any backend, call
.reset()
on yourGeoDataFrameAI
instance to clear prior state and ensure new settings take effect.
Happy hacking!