2025-11-24 – Weekly GIS News : Python vs. TypeScript for web maps

Last week in the GIS forum, members engaged in thoughtful discussions around programming languages for GIS applications, particularly comparing Python and TypeScript for web map development. The community also deliberated on the next steps for those interested in deepening their knowledge of PostGIS versus branching out to TypeScript. Another key topic was the evaluation of open canopy data sources, focusing on their utility for addressing heat equity. Additionally, discussions on reliable pipelines for converting CityGML to 3D Tiles and resources for calibrating peak-hour routes provided practical insights for GIS professionals.


This Week’s Hot Topics

Python or TypeScript for first web map
The community is weighing the pros and cons of using Python versus TypeScript for creating web maps. This conversation is crucial for those deciding on a tech stack.
Read more

PostGIS deep dive or TypeScript next
Members are debating whether to deepen their PostGIS skills or pivot to learning TypeScript, a decision many GIS developers face.
Read more

Best open canopy data for heat equity
Participants are assessing different open canopy datasets to identify the best options for studying urban heat equity. It’s a timely discussion given climate concerns.
Read more

Reliable CityGML to 3D Tiles pipeline
This thread explores dependable methods to convert CityGML data into 3D Tiles, a technical challenge for those working with 3D city models.
Read more

Practical resources for peak-hour route calibration
A practical exchange on resources for calibrating routes during peak hours, offering useful takeaways for transportation-focused GIS projects.
Read more


Looking forward to another week of engaging discussions. Keep sharing your knowledge and asking insightful questions.

1 Like

I just migrated a parcel viewer: kept heavy geoprocessing in PostGIS/Python and moved the UI to TypeScript with MapLibre GL; typings caught our API shape bugs early, but if the map’s mostly static, Python + Folium is quicker… Think chef vs waiter β€” server cooks, client delivers; good starting point: https://maplibre.org/maplibre-gl-js-docs/.

β€Œβ β€β β€‹β€β€‹β€β€Œβ β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€β€Œβ β€Œβ€β€Œβ β€Œβ€β€β€β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ β€‹β€β€Œβ€β€Œβ€Œβ€Œβ β€‹β€‹β€Œβ€β β€‹β€Œβ β€β€Œβ€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€Œβ€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β β€Œβ€β€‹β β€‹β€β€‹β β€‹β β€‹β β€Œβ€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€‹β β€‹β€β€‹β β€‹β€‹β€‹β β€‹β€β€‹β β€Œβ€Œβ€‹β β€‹β€Œβ€‹β β€‹β€Œβ€‹β β€‹β€β€‹β β€Œβ€‹β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€‹β€Œβ€Œβ€Œβ β€β€Œβ€‹β β€‹β€Œβ€Œβ€‹β€Œβ€Œβ€‹β β€‹β€β€Œβ β€Œβ€β€Œβ€β€‹β β€Œβ€β€‹β€Œβ€Œβ€‹β€Œβ€‹β€Œβ€‹β β β€‹β€β β€Œβ€Œβ€β€Œβ€β€Œβ β€β€Œβ€Œβ€‹β€‹β€Œβ€Œβ€‹β€β€β€Œβ β€‹β€β€‹β€β€‹β€β€Œβ β β€Œβ€‹

Quick tip: when serving GeoJSON from PostGIS/Python to a TS frontend, run it through a tiny validator to normalize coordinates and guard against β€˜null’ geometries β€” using zod (GitHub - colinhacks/zod: TypeScript-first schema validation with static type inference) let me fail fast on swapped lon/lat and missing fields. If you’d rather keep more logic in Python, Pydantic on the API gives a similar contract so the client stays simple.

β€Œβ β€β β€‹β€β€‹β€β€Œβ β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€β€Œβ β€Œβ€β€Œβ β€Œβ€β€β€β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ β€‹β€β€Œβ€β€Œβ€Œβ€Œβ β€‹β€‹β€Œβ€β β€‹β€Œβ β€β€Œβ€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€Œβ€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β β€Œβ€β€‹β β€‹β€β€‹β β€‹β β€‹β β€Œβ€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€‹β β€‹β€β€‹β β€‹β€‹β€‹β β€‹β€β€‹β β€Œβ€Œβ€‹β β€‹β€Œβ€‹β β€‹β€Œβ€‹β β€‹β€β€‹β β€Œβ β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€Œβ€β€Œβ€Œβ β€β€Œβ€Œβ€β€‹β β€Œβ€‹β β€‹β€Œβ€β€Œβ€‹β€Œβ€‹β€Œβ β€Œβ β€Œβ€‹β€Œβ€β€Œβ€β€Œβ€β€Œβ€β€Œβ€‹β€β€‹β€‹β€β β€Œβ€Œβ€Œβ€Œβ€β€Œβ€Œβ€Œβ β€‹β β€Œβ β€Œβ€β€β€β€Œβ€Œβ€Œβ€β€‹β€β€‹β€β€Œβ β β€Œβ€‹

If you’re choosing β€œnext steps” between deeper PostGIS or TS, the biggest win for our web maps was switching the API from GeoJSON to MVT using ST_AsMVT on the DB and a TS client (OpenLayers) β€” payload dropped about 10x and the scroll jank disappeared… We still keep Python for heavy jobs, but TS types around tile metadata catch breaking changes early; watch quantization though β€” label offsets can drift, . Docs: ST_AsMVT.

β€Œβ β€β β€‹β€β€‹β€β€Œβ β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€β€Œβ β€Œβ€β€Œβ β€Œβ€β€β€β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ β€‹β€β€Œβ€β€Œβ€Œβ€Œβ β€‹β€‹β€Œβ€β β€‹β€Œβ β€β€Œβ€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€Œβ€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β β€Œβ€β€‹β β€‹β€β€‹β β€‹β β€‹β β€Œβ€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€‹β β€‹β€β€‹β β€‹β€‹β€‹β β€‹β€β€‹β β€Œβ€Œβ€‹β β€‹β€Œβ€‹β β€‹β€Œβ€‹β β€‹β€β€‹β β€β€‹β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€Œβ€‹β€Œβ€Œβ€Œβ€‹β€β€Œβ€Œβ€Œβ€β€Œβ€β β β€Œβ€β€‹β€Œβ€Œβ€‹β€β€‹β€Œβ€‹β€β β€Œβ β€‹β β€Œβ€β€Œβ β€Œβ€Œβ€β€β€Œβ€Œβ€β€‹β€Œβ€β€Œβ€β€‹β β€Œβ€‹β€Œβ€‹β€β β€Œβ€‹β€β€‹β€Œβ€‹β€Œβ β€‹β€β€‹β€β€Œβ β β€Œβ€‹

I got a nice boost by generating TS types straight from our FastAPI OpenAPI doc with openapi-typescript (GitHub - openapi-ts/openapi-typescript: Generate TypeScript types from OpenAPI 3 specs); after last week’s Python vs TypeScript chat, this caught a bbox param mismatch before it hit prod and kept the web map and API in lockstep. It took about an hour to wire up, but if you’re on Flask without an OpenAPI spec you’ll need an extra step to document endpoints.

β€Œβ β€β β€‹β€β€‹β€β€Œβ β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€β€Œβ β€Œβ€β€Œβ β€Œβ€β€β€β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ β€‹β€β€Œβ€β€Œβ€Œβ€Œβ β€‹β€‹β€Œβ€β β€‹β€Œβ β€β€Œβ€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€Œβ€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β β€Œβ€β€‹β β€‹β€β€‹β β€‹β β€‹β β€Œβ€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€‹β β€‹β€β€‹β β€‹β€‹β€‹β β€‹β€β€‹β β€Œβ€Œβ€‹β β€‹β€Œβ€‹β β€‹β€Œβ€‹β β€‹β β€‹β β€‹β€‹β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€Œβ€‹β β€Œβ€Œβ€β€‹β€‹β β€‹β€‹β€Œβ€Œβ€‹β€‹β€Œβ€‹β β€Œβ€Œβ€β€β β€Œβ€‹β€Œβ€‹β€Œβ€β β€Œβ€Œβ β€Œβ€Œβ€Œβ β€Œβ€‹β€Œβ€‹β€β€β€Œβ β€β€β€Œβ β€‹β€Œβ€Œβ€Œβ€‹β€‹β€Œβ€‹β€β€β€‹β β€Œβ€Œβ€‹β€β€‹β€β€Œβ β β€Œβ€‹

I’ve had smoother pans by pushing feature hit-tests to a Web Worker and throttling hover/popup requests to about 120 ms in the TypeScript client β€” our request count dropped a lot; just remember to clear the worker and timer on unmount, @dhernandez.

β€Œβ β€β β€‹β€β€‹β€β€Œβ β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€β€Œβ β€Œβ€β€Œβ β€Œβ€β€β€β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ β€‹β€β€Œβ€β€Œβ€Œβ€Œβ β€‹β€‹β€Œβ€β β€‹β€Œβ β€β€Œβ€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€Œβ€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β β€Œβ€β€‹β β€‹β€β€‹β β€‹β β€‹β β€Œβ€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€Œβ€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€‹β β€‹β€β€‹β β€‹β€‹β€‹β β€‹β€β€‹β β€Œβ€Œβ€‹β β€‹β€Œβ€‹β β€‹β€β€‹β β€‹β€‹β€‹β β€‹β€β€‹β€β€‹β€β€‹β€β β€‹β€‹β€β€‹β€β€Œβ€β€β€‹β€‹β€β€‹β€β€‹β β€β€β€‹β€β€‹β€β€Œβ€‹β€Œβ β€Œβ€‹β€Œβ β€Œβ€β β€β€Œβ€β€‹β€β€Œβ β€β€β€Œβ€‹β€Œβ€‹β€Œβ β€β€Œβ€Œβ€β β€Œβ€Œβ€‹β β β€Œβ€‹β€‹β€β€Œβ€Œβ€Œβ€Œβ€Œβ€β β β€‹β β€Œβ€β€Œβ€β€Œβ€Œβ€Œβ€‹β€Œβ β€Œβ β€‹β€Œβ€‹β€β€‹β€β€Œβ β β€Œβ€‹