Client-Side vs Server-Side Vector Processing in GeoLibre: Turf.js vs GeoPandas/Shapely Comparison
Turf.js runs pure JavaScript geometry operations directly in the browser for instant interactivity, while GeoPandas/Shapely runs Python-based spatial analysis on a FastAPI server (or in Pyodide) for complex, heavy-weight GIS workflows—a choice between speed and analytical depth.
GeoLibre implements dual-stack vector processing that lets developers choose between lightweight client-side operations and powerful server-side analysis. Understanding the architectural differences, performance trade-offs, and specific use cases for each approach helps you build responsive geospatial applications without overloading users' browsers or underpowering your analysis pipeline.
Core Architectural Differences
Implementation Stack and Language
The two processing engines share no runtime dependencies and operate in entirely different environments.
| Component | Client-Side (Turf.js) | Server-Side (GeoPandas/Shapely) |
|---|---|---|
| Language | JavaScript/TypeScript | Python |
| Core library | Turf.js—pure JavaScript spatial analysis | GeoPandas wrapping Shapely |
| Runtime | Browser main thread or WebWorker | FastAPI side-car (backend/geolibre_server) or Pyodide WebAssembly |
| Entry point | packages/processing/src/vector-tools.ts |
backend/geolibre_server/geolibre_server/vector_ops.py |
In packages/processing/src/vector-tools.ts, GeoLibre imports Turf functions directly from @turf/* modules for operations like buffer, centroid, and convex【source 1†L1-L19】. The server implementation in backend/geolibre_server/geolibre_server/vector_ops.py instead imports geopandas as gpd and uses Shapely geometry utilities for equivalent operations【source 2†L46-L65】.
Data Flow and Format Handling
Both stacks use GeoJSON as the interchange format, but handle conversion differently:
-
Turf.js: Accepts a
GeoJSON FeatureCollectionand returns transformed GeoJSON entirely in-memory—no serialization overhead beyond JSON parsing. -
GeoPandas/Shapely: Receives the same GeoJSON payload, explicitly converts via
gpd.GeoDataFrame.from_features()【source 2†L73-L80】, performs the operation, then serializes back withjson.loads(gdf.to_json())【source 2†L84-L90】.
This extra conversion step adds latency but unlocks GeoPandas' DataFrame operations, spatial indexing, and CRS transformation capabilities.
Performance Characteristics and Limits
Browser-Based Execution (Turf.js)
Turf.js operates within the browser's memory constraints but faces no artificial feature limits. The performance ceiling depends on:
- Available RAM per browser tab
- JavaScript engine optimization (V8, SpiderMonkey, JavaScriptCore)
- Whether operations run on the main UI thread or in a WebWorker
For typical interactive workflows—buffering a user-drawn polygon, calculating centroids, or simplifying geometry in real-time—Turf.js delivers sub-100ms response times without network latency.
Server-Side Execution with Resource Guards
The GeoPandas implementation enforces strict limits to protect the Python process. In backend/geolibre_server/geolibre_server/vector_ops.py, GeoLibre defines:
MAX_FEATURES = 50_000 # Hard limit for server-side vector operations
Requests exceeding this threshold trigger a VectorInputTooLarge exception (subclass of ValueError)【source 2†L11-L15】, which the FastAPI layer maps to HTTP 413 Payload Too Large【source 2†L38-L44】. This synchronous, memory-intensive execution model requires defensive limits absent in the streaming JavaScript approach.
Code Implementation Examples
Client-Side: Direct Turf.js Integration
From packages/processing/src/vector-tools.ts, GeoLibre exposes Turf operations with minimal wrapping:
import buffer from "@turf/buffer";
import centroid from "@turf/centroid";
// geojson: GeoJSON.FeatureCollection from map interaction
const buffered = buffer(geojson, 5, { units: "kilometers" });
const centroids = centroid(geojson);
The functions execute immediately with no async boundary—ideal for reactive UI updates during drawing or editing sessions【source 1†L1-L19】.
Server-Side: GeoPandas via FastAPI Endpoint
The Python implementation in vector_ops.py handles the same operations through an RPC-style interface:
from geolibre_server.vector_ops import run_vector_tool
# geojson: dict representing a FeatureCollection from HTTP request
result_geojson, messages = run_vector_tool(
tool="buffer",
geojson=geojson,
overlay=None,
parameters={"distance": 5, "units": "kilometers"},
)
The _buffer handler internally constructs a GeoDataFrame, applies Shapely's buffer operation, and returns serialized GeoJSON【source 2†L96-L124】. Network round-trip adds 20-200ms latency depending on payload size and server proximity.
Error Handling Patterns
Each stack propagates failures through its native exception model:
| Scenario | Turf.js Response | GeoPandas/Shapely Response |
|---|---|---|
| Invalid geometry | JavaScript Error thrown, caught in UI |
ValueError with descriptive message |
| Feature limit exceeded | N/A (no hard limit) | VectorInputTooLarge → HTTP 413 |
| General processing failure | Console error + UI toast | FastAPI exception handler → HTTP 400 |
The VectorInputTooLarge custom exception【source 2†L11-L15】 allows client code to distinguish capacity errors from malformed inputs and suggest falling back to Turf.js or sampling the dataset.
When to Use Each Processing Mode
Choose Turf.js for:
- Real-time geometry editing requiring instant feedback
- Offline-capable applications or low-connectivity environments
- Small-to-medium datasets (<10,000 features) where browser memory suffices
- Privacy-sensitive workflows keeping raw geometry client-side
Choose GeoPandas/Shapely for:
- Complex spatial operations like
dissolve,overlay, orsjoinlacking Turf.js equivalents - Coordinate reference system transformations requiring PROJ database access
- Large-scale analysis approaching the 50,000-feature server limit
- Batch processing pipelines where latency matters less than analytical completeness
GeoLibre also supports Pyodide execution, loading the same Python modules in-browser via WebAssembly—bridging the gap with near-zero latency while retaining GeoPandas' full feature set.
Summary
-
Turf.js provides pure-JavaScript, client-side vector processing with instant execution and no feature limits, implemented in
packages/processing/src/vector-tools.ts. -
GeoPandas/Shapely delivers server-side Python GIS capabilities through a FastAPI side-car, with richer functionality but enforced 50,000-feature limits in
backend/geolibre_server/geolibre_server/vector_ops.py. -
Both stacks consume and produce GeoJSON FeatureCollections, enabling seamless switching based on operation complexity, dataset size, and latency requirements.
-
Error handling differs by environment: JavaScript exceptions for Turf.js, HTTP 400/413 responses for server-side failures.
Frequently Asked Questions
Does GeoLibre automatically choose between Turf.js and GeoPandas?
No—explicit user selection or application configuration determines the engine. The UI typically presents a dropdown to switch between "Fast (Browser - Turf.js)" and "Advanced (Sidecar - GeoPandas)" modes, with Pyodide available as a third hybrid option when compiled.
Can I run GeoPandas operations without a server?
Yes, through Pyodide integration. The same vector_ops.py module loads in the browser via WebAssembly, as implemented in apps/geolibre-desktop/src/lib/pyodide/console_api.py. This eliminates network latency while maintaining Python's analytical depth, though initial module loading adds several seconds of overhead.
Why does the server-side implementation have a 50,000 feature limit?
The synchronous, memory-intensive nature of GeoPandas operations can exhaust server RAM or block the Python process. The MAX_FEATURES guard【source 2†L25-L30】protects against denial-of-service from accidental large payloads. For larger datasets, GeoLibre recommends preprocessing with Turf.js sampling or connecting to external GIS services.
Do Turf.js and GeoPandas produce identical geometric results?
Not guaranteed. Topological differences emerge from distinct algorithms: Turf.js uses planar (Euclidean) geometry by default, while GeoPandas/Shapely can apply geodetic calculations with proper CRS handling. Buffer operations particularly diverge—verify outputs when precision requirements are strict, or explicitly set coordinate systems in GeoPandas to match Turf.js behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →