Back to list
ReactGeoJSONTurf.jsMapTypeScript

[React][Lib] Understand GeoJSON Spatial Data and Use Turf.js

Learn the FeatureCollection > Feature > Geometry hierarchy, GeoJSON coordinate order, and how Turf.js handles filtering, point-in-polygon, and centroids in React.

1. Environment

  • OS: Windows 11 / macOS
  • Frontend library: React.js (TypeScript recommended)
  • Spatial libraries: @turf/turf, @types/geojson
  • Terminal: PowerShell (Windows) / zsh (macOS)

2. Problem

Map engines such as Leaflet, Mapbox, or Cesium.js can draw heavy datasets with millions of coordinates. The worldwide standard format for that data is GeoJSON.

In practice you rarely paint the server payload as-is. Before render you almost always transform it on the client:

  • “From Seoul?’s polygons, draw only districts with population |–500,000.|–
  • “Is the user?’s click (Point) inside a given commercial-area polygon|–

Hand-rolling those spatial math formulas is inefficient. First understand GeoJSON?’s structure, then use the right tools for spatial work.

3. Cause

GeoJSON is not a flat list of numbers. It is a three-layer geography JSON object. Without that model you cannot even filter safely.

  • FeatureCollection: Outer shell that groups many geographic items (like an array).
  • Feature: One independent map object (one building, one district). It must pair properties (attributes) with geometry (shape).
  • Geometry: Concrete point / line / polygon coordinates for drawing.
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "Gangnam-gu",
        "population": 530000
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[127.0, 37.5], [127.1, 37.5], [127.1, 37.4], [127.0, 37.5]]]
      }
    }
  ]
}

geometry.type decides what is drawn:

Geometry type Meaning coordinates shape Typical use
Point Single location [lng, lat] Store marker, current position
LineString Linked points `[[lng, lat], –`
Polygon Closed area `[[[lng, lat], – first point again]]`

4. Solution

1) Install spatial libraries

Windows / macOS:

# npm
npm install @turf/turf
npm install -D @types/geojson

# pnpm (recommended)
pnpm add @turf/turf
pnpm add -D @types/geojson

2) Filter features by property (TypeScript)

With @types/geojson you can reach nested properties safely:

import { FeatureCollection, Polygon } from 'geojson';

// Keep only areas with population |--500,000
function filterPopulatedAreas(geoData: FeatureCollection<Polygon>) {
  const filteredFeatures = geoData.features.filter((feature) => {
    const pop = feature.properties?.population || 0;
    return pop >= 500000;
  });

  return {
    type: 'FeatureCollection',
    features: filteredFeatures,
  } as FeatureCollection<Polygon>;
}

3) Spatial math with Turf.js

Intersection, distance, and center points become one-liners:

import * as turf from '@turf/turf';
import { Feature, Polygon, Point } from 'geojson';

// Q1: Is the click Point inside Gangnam-gu?
function isUserInside(userPoint: Feature<Point>, gangnamPolygon: Feature<Polygon>) {
  return turf.booleanPointInPolygon(userPoint, gangnamPolygon);
}

// Q2: Where is the polygon center (for a map marker)?
function getPolygonCenter(polygon: Feature<Polygon>) {
  const centerPoint = turf.centroid(polygon);
  console.log('Center:', centerPoint.geometry.coordinates); // [127.x, 37.x]
  return centerPoint;
}

5. Summary

Three rules for map data on the frontend:

  1. Hierarchy: Parse as FeatureCollection |–Feature |–Geometry + Properties.
  2. Coordinate order: Many map APIs use [lat, lng], but GeoJSON is always [longitude, latitude].
  3. Use Turf.js: For containment, buffers, centroids, and similar geo math - do not invent formulas by hand.

Related posts