# Client-Side vs Server-Side Vector Processing in GeoLibre: Turf.js vs GeoPandas/Shapely Comparison

> Explore client-side vector processing with Turf.js versus server-side with GeoPandas Shapely in GeoLibre. Choose between instant browser interactivity and powerful Python GIS analysis for your geospatial workflows.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: comparison
- Published: 2026-08-05

---

**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](https://turfjs.org/)—pure JavaScript spatial analysis | [GeoPandas](https://geopandas.org/) wrapping [Shapely](https://shapely.readthedocs.io/) |
| **Runtime** | Browser main thread or WebWorker | FastAPI side-car (`backend/geolibre_server`) or Pyodide WebAssembly |
| **Entry point** | [`packages/processing/src/vector-tools.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/vector-tools.ts) | [`backend/geolibre_server/geolibre_server/vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/vector_ops.py) |

In [`packages/processing/src/vector-tools.ts`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/vector_ops.py), GeoLibre defines:

```python
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`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/vector-tools.ts), GeoLibre exposes Turf operations with minimal wrapping:

```typescript
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`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) handles the same operations through an RPC-style interface:

```python
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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) module loads in the browser via WebAssembly, as implemented in [`apps/geolibre-desktop/src/lib/pyodide/console_api.py`](https://github.com/opengeos/GeoLibre/blob/main/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.