
This course covers agentic coding workflows specializing in geospatial data science. You will get hands-on experience setting up your agent and understand how to use Skills and MCP servers to augment domain-specific capabilities. We cover best practices for using coding agents to rapidly solve complex problems - grounded in established science and human-in-the-loop validation. We cover hands-on examples using Claude Code - including crime hotspot mapping, route optimization, and object detection from aerial imagery. You will also learn how to use agents to manage a personal knowledge base and create a website that automatically updates with new information.
Visit GitHub.com and create a free account. If you already have an account, skip this step.
Follow our Git and GitHub CLI Installation Guide to install and configure Git and GitHub CLI.
We will use conda to install the required Python packages and manage local development environments.
Follow our step-by-step Conda Installation Guide to install Miniconda for your operating system. Once you have a working conda installation, follow the steps below.
conda-forge.conda install -c conda-forge pandas geopandas matplotlib jupyterlab rioxarray gdal rio-cogeo ipykernel uvYour local development environment is now ready.
We recommend Visual Studio Code (VS Code) editor for this workshop.
Follow our step-by-step Visual Studio Code Installation Guide to install and configure VS Code on your system. Make sure to complete the steps to set VS Code as your default editor.
Follow our step-by-step Claude Code Installation Guide to install and configure Claude Code on your system.
Obsidian is a free app for creating and managing notes. We will be using Obsidian for setting up your personal knowledge base using Claude. Visit Obsidian Download page and install Obsidian for your platform.
OpenRouteService (ORS) provides a free API for routing, distance matrix, geocoding, route optimization etc. using OpenStreetMap data. To obtain an API key, visit the HeiGIT Sign Up page and create an account. Once your account is activated, you can visit the Dashboard and copy the long string displayed under Basic Key.
We will configure and use the QGIS MCP server to allow Claude Code to
use QGIS directly. Please follow our QGIS-LTR Installation Guide to install
QGIS. If you already have a recent QGIS version (3.40+ or
4.0+) installed on your computer, you can skip this
step.
The code examples and reference materials used in this workshop are
supplied to you in the agentic_coding_geospatial.zip file.
Unzip this file to a directory - preferably to the
<home folder>/Desktop/agentic_coding_geospatial/
folder.
Download agentic_coding_geospatial.zip.
Note: Certification and Support are only available for participants in our paid instructor-led classes.
Our primary interface for this course will be the Terminal. We will now cover basic terminal commands that will help you get comfortable with the environment.
(Windows users), search for Anaconda Powershell Prompt and launch it. (Mac/Linux users): Launch a Terminal window.
The following are some of the common terminal commands we will need to browse and manage files.
| Command | Description | Example |
|---|---|---|
cd |
Change directory | cd Desktop |
cd .. |
Change to the parent directory | cd .. |
ls |
List files in the current directory | ls |
rm |
Delete a file | rm test.txt |
rm -R |
Delete a directory (Mac/Linux) | rm -R test |
rm -Recurse |
Delete a directory (Windows) | rm -Recurse test |
mkdir |
Create a directory | mkdir test |
cat |
Print the contents of a file | cat test.txt |
clear |
Clear screen | clear |
A good practice is to always setup a CLAUDE.md file for
your project. This file contains instructions and rules to be followed
when working inside the project directory. If you already have a
directory with some code or have cloned a Git repository, Claude Code
can automatically create the CLAUDE.md file using the
/init command. Here we are starting with a blank project,
so we will create a new CLAUDE.md file with
instructions.
cd command to change the current working directory to
the project directory.Windows
Mac/Linux


/memory command to setup the project
CLAUDE.md

CLAUDE.md file. Paste the following content and save the
file. Close the file once saved.# CLAUDE.md
## Geospatial Stack Preferences
- Prefer Python-based approaches
- Use existing packages instead of building solutions from scratch
- My preference for geospatial packages are below
- Pandas for tabular data
- GeoPandas for vector geospatial data
- XArray ecosystem (rioxarray, xarray-spatial etc) for raster geospatial data
- Scikit-learn for Machine Learning
- GeoAI (geoai-py) for Deep Learning
- Web Apps
- Streamlit for data driven apps
- Leaflet for interactive mapping apps
- Self contained HTML for small apps
## Other Preferences
- Write code that is simple to understand and explain
- Always install python packages in a conda environment. Never install anything in the base environment. Ask the user for confirmation on their preferred conda environment before installing anything.
- Do not use emojis

CLAUDE.md file in your
<home folder>/Desktop/agentic_coding_geospatial/
folder.
/context command to check if the
file contents has been loaded into the current context.Learn more about Managing Context


/help to see all available
commands. Let’s run a few commands. First enable Plan
Mode by entering the /plan command. Next choose the
model using the /model command. The default model choice is
good for now, and you can change the model at any time.
Now we are ready to use Claude Code for our first data analysis task.
We will now use Claude Code to do some data exploration, cleaning and analysis. This will give you a feel of how agents work and how to guide them. We will take the Police.uk data on street-level crime for the City of London (The Square Mile area) and identify crime hotspots.
I would like to do an exploratory data analysis of crime data in the folder data/london_crime_2024.
* Give me a summary of the rows, columns and any missing data.
* Flag any data quality issues.
* Create charts showing different types of crime and monthly patterns.
Produce the output as a Python script and PNG images.

CLAUDE.md states, all the work
needs to be done in a conda virtual environment. Claude will prompt you
to choose an environment. Choose the
claude_code_workshop environment created for this class
and press Enter.

Depending on your operating system, Claude Code version, and selected model, you may see a slightly different set of questions.




output/ subfolder.
Now that we have a good understanding of the data structure, we can ask Claude to help us with analyzing the data. In this section, we will go through the workflow of identifying hotspots off thefts. Starting with data cleaning, we will iteratively improve our analysis and learn how we can use existing references (research papers, manuals etc.) to ground the behavior of the model.
I want to analyze theft hotspots.
Help me select the categories for theft crimes and filter the data.





I want to map the theft hotspots.
Suggest what would be the best technique for the analysis.


Create a static visualization using matplotlib using gaussian_kde.



I have some reference material for crime mapping in documents/hotspots.pdf.
Incorporate suggestions from that and give me an updated plan.
Follow the reference exactly and report any deviations in the plan.



Save the updated script and chart with the _v2 suffix.



Use the criteria "Greater than 5x mean" to extract hotspots and convert then to polygons.
Save the results as a GeoJSON london_theft_hotspots.geojson.



It’s a good practice to keep an eye on your token usage and costs. You can use the following command to know the utilization in the current session and how much quota is left in your plan.
/usage

In this section, we will learn how to use Claude Code to build Jupyter notebooks for data analysis and interactively guide the agent to reach the desired solution and validate the results. We will be implementing a real-world scenario of optimizing delivery routes based on a route optimization algorithm.
In your data package, you will find the following files in the
data/route_optimization/ folder.
Delivery_Locations.geojson: A set of 100 addresses in
Washington DC area representing delivery locations.Grocery_Store_Locations.geojson: A set of 14 grocery
store locations which needs to fulfill the delivery to these
locations.We will use Claude Code to build a Jupyter Notebook that builds an end-to-end workflow that loads the data, finds optimal delivery routes and writes PDF manifests with delivery schedule for each store.
Let’s install the Claude Code for VS Code extension.



Now that we have Claude Code running inside VS Code, we can prompt it to build a Jupyter notebook for us.
agentic_coding_geospatial is
open in VS Code. Enter the following prompt.I want to build a notebook to find optimal delivery routes.
Create a new notebook scripts/route_optimization.ipynb
Read the .geojson files in data/route_optimization/ folder and
display the data on an interactive map.

CLAUDE.md already has preferences listed for how to setup
the environment and what packages to use. Claude Code will start
creating the notebook and will pause periodically for confirmation. You
can choose Yes to proceed.
route_optimization.ipynb will be created in the
scripts/ folder as instructed.

Now, let’s build the core of our data analysis to find the optimal delivery routes. This is a complex problem that requires a lot more context and direction. We will use the Plan mode and guide Claude Code towards building the code that solves the problem.
route_optimization.ipynb is
open. Open Claude Code from the Toggle Chat button.

I want to use OpenRouteService API.
Give me options on different approaches to plan for the most optimal delivery routes from each store.
Here are the constraints:
- A single trip will consist of 10 deliveries at maximum.
- The delivery will originate at a grocery store and end at the same delivery store.
- All stores are capable of fulfilling the order but can do a maximum of 5 delivery trips each day.
The goal is to minimize the total distance traveled.
The output is the assignment and delivery schedule for each store along with a GeoJSON of the optimal delivery routes for visual inspection.


<your-api-key> with your actual API key. If you do
have your key handy, you can login to your HeiGIT Account and copy
the long string displayed under Basic Key.Yes, I have a key. Save the API key in a separate cell in the notebook.
ORS_API_KEY='<your-api-key>'

claude_code_workshop conda environment that we have setup.
You can then click Submit answers.



openrouteservice Python package. We can instead ask it to
use the API directly instead of a client library by adding a comment
like below.Instead, use the REST API directly.






When building with AI - you need to spend most of your time validating the results and guiding the model. Let’s learn some techniques for validation.
I see that some stores are not assigned any deliveries even if they are quite close to an address.
The assignment tries to assign 10 deliveries in each route -
making some of them quite long while nearby stores end up without any deliveries.
Update the plan to address this problem.



Save the routes as a single GeoJSON file in the outputs/route_optimization/
folder so I can validate the results.

routes.geojson will be created. Locate it in
the VS Code Explorer. Right-click and select Reveal in File
Explorer to open the folder on your computer.
routes.geojson, along with the source data
Delivery_Locations.geojson and
Grocery_Store_Locations.geojson and verify the
results.
Generate PDF manifests in the outputs/route_optimization/ folder containing
delivery order and addresses for each trip.
1 manifest per store.



Now you are ready to put your agentic coding skills to test by solving a spatial analysis problem from scratch.
Problem Statement
You are a data scientist for a real-estate company who wants to enhance their property listings by assigning a livability score to each of their properties to help buyers pick properties that suit their lifestyle.
Your task is to calculate such a score for all the Apartment Buildings in your city. Consider the factors that are important to home buyers. You can design the score using metrics such as:
These are some examples and you can pick the criteria that make sense to you based on the knowledge of your city. You can use OpenStreetMap to obtain the data for apartment building polygons and the required amenities.
Required output
Below are example reports shared by participants of our live class for inspiration:
Tips
osmnx which can easily extract the required data from
OpenStreetMap.network distance/isochrone
instead of simple circular buffer for a more realistic distances.Skills are a set of instructions that teach an AI agent how to
perform specific, multi-step workflows. Skills in Claude
Code are defined by a SKILL.md containing
natural-language instructions for the agent along with specific examples
and patterns. You can also publish your skill on GitHub and make it
available for others to download and use with their own agent.
We will install the Humanizer skill that improves the text generated by AI models and makes them sound more natural.
cd command
to change the current working directory to the project directory. Once
you are inside the project folder, launch Claude Code by entering the
following command.
npx skills
command provides an easy to to install skills for a wide range of coding
agents. However this requires installation of Node.js on your system.
For Claude Code, you can install Skills as plugins. Run the following
command to add the GitHub repository.
humanizer
skills as a plugin.


Once installed, Skills are triggered automatically when your prompt matches the task handled by the skill. We will use the Humanizer skills to review and rewrite a paragraph.
Give me a 100-word summary of this article
https://cloudnativegeo.org/blog/2026/02/the-technical-debt-of-earth-embedding-products/

humanizer skill by asking Claude
Code to review and update the text. Enter the following prompt and press
Enter.Review the summary and remove signs of AI writing

humanizer skill will be used to rewrite the text by
removing common AI-writing patterns.
The real power of Skills lies in automating custom workflows. We will
learn how to create a new skill for your custom workflow and use it. We
will create a new skill named create-cog for converting
raster data into a Cloud Optimized GeoTIFF (COG) format.
Create a new skill from the following text: and paste the
content below.---
name: create-cog
description: Convert a raster to a Cloud-Optimized GeoTIFF (COG)
---
Use this skill when you need to convert any geospatial raster data to a
Cloud-Optimized GeoTIFF.
## Workflow
1. Activate the conda environment that has GDAL. Never use the base environment.
2. Check the GDAL version, since the conversion command differs by version.
3. Check if the input is already a valid COG. If it is, stop and report
that. Do not re-convert.
4. Convert, using the compression settings and naming convention below.
5. Validate the output and report the result.
## Required Tools
* `gdal` Conda package. Install using `conda install -c conda-forge gdal`
* `rio-cogeo` Python package for validation. Install using `pip install
rio-cogeo`
Both must come from a conda environment, not base. Check what is available
before installing:
conda env list
conda activate <env>
gdal --version
If neither is installed, ask the user which environment to install into
before proceeding.
## Validating COGs
rio cogeo validate <file>
Always validate the input before converting and the output after converting.
## Converting Raster to a COG
Check `gdal --version` first, then use the matching command.
GDAL version >= 3.11 (the `gdal` subcommand interface)
gdal raster convert -f COG <input file> <output file>
GDAL version < 3.11 (the legacy utility)
gdal_translate -of COG <input file> <output file>
Note the flag difference. The new `gdal raster convert` takes `-f` (or
`--format`). It does NOT accept `-of`, which is the legacy `gdal_translate`
flag, and fails with "Option 'o' is not a boolean option."
Creation options are passed with `--co KEY=VALUE` on the new interface,
`-co KEY=VALUE` on the legacy one.
If the output file already exists, the command errors out. Add
`--overwrite` only when you intend to replace it.
## Compression
The COG driver already applies LZW compression by default, so the output is
compressed even if you pass nothing. But the default uses no predictor,
which wastes a lot of space on continuous data. Set the compression and
predictor explicitly. Use DEFLATE as the default, since it is readable by
every GeoTIFF client.
gdal raster convert -f COG --co COMPRESS=DEFLATE --co PREDICTOR=YES <input> <output>
Only exception - If the input data is JPEG compressed - typically used in
aerial/drone imagery), retain the same with COMPRESS=JPEG
## File Naming Convention
Append the text `_cog` to the converted filename. If the input is
`data.tif`, the output should be named `data_cog.tif`
## Remote Files
If the given file is a remote file, do not download it. Use GDAL Virtual
File Systems by prefixing the URL with `/vsicurl/`
rio cogeo validate /vsicurl/https://example.com/data.tif
gdal raster convert -f COG /vsicurl/https://example.com/data.tif data_cog.tif
A remote input gives no obvious output location. Write to the current
working directory unless a location is specified by the user.
In Windows Powershell, set the following environment variable `MSYS_NO_PATHCONV=1`
to avoid errors when using paths starting with `/vsicurl`

.claude directory in the current
folder.
/reload-skills

Convert the file data/chirps/chirps-v3.0.2025.tif to a Cloud Optimized GeoTIFF


Convert the file https://data.chc.ucsb.edu/products/CHIRPS/v3.0/annual/global/tifs/chirps-v3.0.2024.tif to a COG

/vsicurl virtual file system to access the file instead of
downloading it.
/create-cog Convert this file https://storage.googleapis.com/spatialthoughts-public-data/ntl/viirs/viirs_ntl_2021_global.tif


skill-creator, pdf, xlsx are
useful skills for Data Science work.skills CLI.An AI second brain is a persistent digital memory system that stores your notes and ideas. Andrej Karpathy popularized the idea of a implementing such a knowledge base using the note taking app Obsidian and coding agents like Claude Code. In this section, we will take the core ideas behind the LLM-Wiki system and learn how to setup a simplified version to collect, organize and maintain an archive of your notes and bookmarks. We will also publish and host them as an auto-updating website on Github Pages so you can always find and recall items from your archive.
We will now create a new folder on your computer and setup a structure to create a personal knowledge base.
mkdir command.Windows
Mac/Linux

cd
command.Windows
Mac/Linux


/memory command to setup the project
CLAUDE.md

CLAUDE.md file. Paste the following content and save the
file. Close the file once saved.## Overview
A personal notes organizer maintained by Claude Code.
## Folder structure
```
- raw/ -- documents containing unprocessed notes
- processed/ -- documents containing processed notes
- notes/ -- markdown pages for the organized notes pages
- notes/index.md -- table of contents of all the notes pages
- notes/log.md -- append-only record of all operations
```
## Workflow
- Read the unprocessed notes in the `raw/` folder. Look at all files inside the `raw/` folder.
- Process each document and all notes inside using the processing instructions below.
- Once processed, move it to the `processed/` folder.
- Add an empty file `New_Notes.md` in the `raw/` file to collect new notes.
## Processing Instructions
When the user adds a new note to `raw/` and asks you to ingest it
* Read the New_Notes.md and process all notes from it.
* If the note has one or more URLs, visit them and generate an accurate short description.
* For notes with text upto 100 characters, add it verbatim. For longer notes, summarize it up to 100 characters.
* Read any other files added to the `raw/` folder and process each file as a separate note.
* Identify the core concepts/topic of the resource.
* Identify the main topic and related topics. If the resource neatly fits into a single main topic - you do not have to have a related topic.
* Read `notes/index.md` first to find relevant topic pages.
* If there is no match, a new topic page can be added. See the list of topics below for additional topics of interest.
* Add a new item to the main topic page. Keeping the newer notes at the top.
* Add back-links ([[page-name]]) to connect related topics. If the related topic page does not exist, create the page.
* Update `notes/index.md` with new pages and one-line descriptions.
* Append an entry to `notes/log.md` with the date, source name, and what changed
## List of Topics
Here are some of the topics for the notes you may find. The topics can be Technology focused (i.e. Python, Xarray) or Theme focused (Climate Change, Agriculture). When deciding the main topic when a note covers both - use the theme focused topic as the primary topic.
Technology Topics
* Machine Learning
* Embeddings
* Deep Learning
* SQL
* Python
* Data
* Agentic Coding
Thematic Topics
* Climate Change
* Urban Planning
* Agriculture
* Remote Sensing
* Cartography
## Topic Page Format
Every note topic page should follow this structure:
```markdown
# Page Title
**Summary**: One to two sentences describing this page.
**Last updated**: Date of most recent update.
---
Main content goes here.
```
## Note Formatting Instructions
- Use Markdown format for each note.
- Use a bullet point for each note.
- For notes with URLs:
- Follow the format [title](url]): <description> <keywords>
- Add a 1-2 line description from the URLs
- For notes with just text:
- Follow the format *Title*: <description> <keywords>
- For notes with text upto 100 characters, add it verbatim. For longer notes, summarize it up to 100 characters.
- Add 3-6 keywords that best describe the note and will aid in recalling them later.
- Link to related topics using [[wiki-links]] throughout the text.
## Rules
- Keep page names Title Case with underscores (e.g. `Machine_Learning.md`).
- Write in clear, plain language.
- Always update the `log.md` after changes.

Read CLAUDE.md and setup the folder structure for my notes


personal_knowledge_base the open the folder as vault.
personal_knowledge_base folder along
with the subfolders created in the previous step. Select the
raw/New_Notes.md file.
https://cloudnativegeo.org/blog/2026/02/the-technical-debt-of-earth-embedding-products/ Article comparing different earth embeddings
https://github.com/montimaj/agribound Python package for delineating field boundaries
https://zenodo.org/records/18647054

Ingest new notes


notes/ folder to see the newly created pages. The
index.md is the homepage for the knowledge base where all
topics are listed.


index.md file. We can remove them from the view to see the
actual shape of the notes. Click Filters and enter the
following expression. You can also toggle the Existing files
only and Orphans buttons.
path:notes -file:log -file:index


If you do not have a LinkedIn account, you can use any other web page for this step.
https://www.linkedin.com/feed/update/urn:li:activity:7478690057026207745/


raw/ folder.
Our vault is setup to process all the inputs from this folder, so we can
now ask Claude Code to process this note.
Ingest new notes


Claude Code can build, deploy and update websites. We will learn how to use these capabilities by turning our local Obsidian folder into a static website and host it for free on Github Pages. We will also configure it so it updates automatically every time we add new notes to our personal knowledge base. This allows you to always access and search your notes from anywhere.





Push the changes


gh command to create and
configure the repository. Once the deployment is complete, you can go to
the URL of your website displayed in the output.

Pro tip: You can indicate the main topic for the note by encloding it in the
[[ ]]. This is the Obsidian syntax for indicating internal links.
https://www.geomermaids.com/cookbook/duckdb-spatial/ Optimizing DuckDB Spatial Queries [[SQL]]



The Model Context Protocol (MCP) is an open standard for connecting external tools to AI Agents. MCP Servers allow your Agent to carry out tasks on different applications. In this section, we will learn how MCP servers work and how to configure your agents to use them.
The QGIS MCP server allows you to connect Claude Code to QGIS and provides tools to add layers, edit features, call processing algorithms and more. Configuring the MCP server is a two-step process:
We will go through each step and learn how to setup the server and use it from Claude Code.





conda activate claude_code_workshop
claude mcp add -s user qgis -- uvx --from https://github.com/nkarasiak/qgis-mcp/archive/refs/heads/main.zip qgis-mcp-server

qgis is connected. Press Esc to
exit.
ping qgis

Load the raster data/chirps/chirps-v3.0.2025.tif in QGIS


We will now build a notebook that uses a pre-trained model to detect buildings from aerial imagery, running on Google Colab so we can take advantage of its free GPU access. We will select an image from OpenAerialMap and use a pre-trained model to extract building polygons from it.
We will setup and configure a connection to the Google Colab MCP server which will allow Claude Code to create, modify and run Google Colab notebooks.
cd command
to change the current working directory to the project directory.
Setup an MCP server according to the instructions at
https://github.com/googlecolab/colab-mcp


.claude.json with the required
information. Restart Claude Code for the changes to take effect. You can
exit the session using Ctrl + C and start it again by
typing claude.
/mcp

colab-mcp
server connected.
Now that we have configured the MCP server, we can prompt Claude Code to create cloud-hosted notebooks using Google Colab.
Create a new colab notebook

colab-mcp server to open a
connection. Approve the execution requests.


Add a cell to print "hello world" and run it


Colab MCP server can be a bit flaky and you may see errors when establishing the connection. Here are a few things you can try.
/mcp and check whether the colab-mcp
server is connected. If it is disconnected, select it and press
Enter to reconnect.


Create a new Colab Notebook named "building_detection".
This notebook will use a pre-trained model for building footprint extraction from GeoAI and run it on the provided image.
Structure the notebook following this example https://opengeoai.org/examples/building_footprints_africa/
The provided image can be large. So use the GeoAI package to split the input image into tiles
Reproject the tiles to a suitable local UTM crs.
The model is trained on images around 50cm-1m resolution images. Check the resolution of the provided image and if it is higher resolution (i.e. 5cm, 10cm) - resample the tiles to 50cm resolution before running the inference.
Save the results as a GeoJSON file in EPSG:4326 CRS.
Use this image from OpenAerialMap to test the notebook https://oin-hotosm-temp.s3.us-east-1.amazonaws.com/69493c8084a859b011c94266/0/69493c8084a859b011c94267.tif




Run the notebook



building_footprint.geojson file.


https://oin-hotosm-temp.s3.us-east-1.amazonaws.com/69493c8084a859b011c94266/0/69493c8084a859b011c94267.tif




If you want to catch up to this step and use a sample notebook
generated in this step, click the button below.

You will notice that since the footprints were vectorized from a raster layer, they have jagged edges. We can make the output polygons much better by Regularizing the polygons. GeoAI has several algorithms for Building Regularization. Prompt Claude Code to add a step at the end to regularize the polygons and save them.

This course material is licensed under a Creative Commons Attribution 4.0 International (CC BY 4.0). You are free to re-use and adapt the material but are required to give appropriate credit to the original author as below:
Agentic Coding for Geospatial course by Ujaval Gandhi www.spatialthoughts.com
© 2026 Spatial Thoughts www.spatialthoughts.com
If you want to report any issues with this page, please comment below.