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 FeatureCollection and 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 with json.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, or sjoin lacking 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →