Building a Zoning Chatbot with GeoPandas, Folium, and the OpenAI API
I wanted to create a proof-of-concept chatbot that could answer basic zoning questions using spatial data. The idea was simple: enter an address, and get back a zoning summary with a map. I used a combination of Python tools I already lean on—GeoPandas, Folium, and the OpenAI API—and wired them up into a notebook that does just that.
🧠 The chatbot takes in an address, geocodes it with Nominatim, runs a spatial join with a zoning geojson (in this case, for Marin County), and then sends the resulting zoning description to the OpenAI API for summarization. It returns both the original zoning text and a cleaned-up explanation in plain English. I also generate an interactive map with Folium showing the parcel boundary and surrounding zoning.
One of my main goals here was accessibility—I wanted a zoning lookup tool that produces output that’s easy to read aloud by a screen reader or text-to-speech tool. A lot of existing GIS interfaces aren’t optimized for accessibility or non-technical users. This approach keeps the logic spatial but outputs something lightweight, plain-text, and structured for assistive tech.
🔧 Tech Stack
GeoPandas
for spatial joinsFolium
for web map generationgeopy
+ Nominatim for geocodingopenai
Python library for GPT-based summarization- A zoning geojson of Unincorporated Marin County from Open Data
📂 Files & Structure
This notebook defines four core functions:
lookup_zoning(address)
: Geocodes the input address, converts it to a GeoDataFrame, reprojects to match the zoning data, and performs a spatial join to find the intersecting zoning polygon.summarize_zoning(zone_gdf)
: Extracts zoning attributes from the joined polygon, builds a prompt, and sends it to the OpenAI API for summarization.show_result_map(point_geom, result_gdf)
: Uses Folium to visualize the point and intersecting zoning polygon.zoning_chatbot(address)
: High-level wrapper function that ties everything together, prints the zoning attributes, gets the summary, and shows the map.
🗺️ Try it yourself
You can view the notebook on GitHub or interact with the embedded version below: