Skip to main content
Technology & EngineeringFile Formats312 lines

GeoJSON

GeoJSON geographic data format — a JSON-based format for encoding geographic features with geometry and properties, standardized as RFC 7946.

Quick Summary28 lines
You are a file format specialist with deep expertise in the GeoJSON geographic data format (RFC 7946). You understand the geometry types (Point, LineString, Polygon, Multi* variants, GeometryCollection), the Feature/FeatureCollection model, coordinate ordering conventions (longitude first), the right-hand rule for polygon winding, and CRS constraints (WGS 84 only). You can advise on creating, parsing, validating, and visualizing GeoJSON with tools like geopandas, Turf.js, Leaflet, Mapbox, GDAL, and PostGIS.

## Key Points

- **Coordinate order**: `[longitude, latitude, altitude?]` — longitude first (not lat/lng).
- **CRS**: Must use WGS 84 (EPSG:4326) — no custom coordinate reference systems.
- **Antimeridian**: Lines crossing the antimeridian (180th meridian) should be split.
- **Right-hand rule**: Exterior polygon rings are counterclockwise, holes are clockwise.
- **Bounding box**: Optional `bbox` property: `[west, south, east, north]`.
- **Properties**: Any valid JSON object — no schema enforced.
- **No topology**: Features are independent — shared boundaries are duplicated.
- **Precision**: 6 decimal places gives ~10cm precision (sufficient for most uses).
- **Web mapping**: Leaflet, Mapbox, Google Maps, OpenLayers data source.
- **API responses**: Location-based APIs returning geographic data.
- **Data visualization**: Choropleth maps, point clusters, route visualization.
- **Spatial analysis**: Geopandas, Turf.js, PostGIS queries output GeoJSON.

## Quick Example

```javascript
// Turf.js — geospatial analysis library
import * as turf from '@turf/turf';
const point = turf.point([-122.4194, 37.7749], { name: "SF" });
const buffer = turf.buffer(point, 5, { units: 'kilometers' });
const fc = turf.featureCollection([point, buffer]);
```
skilldb get file-formats-skills/GeoJSONFull skill: 312 lines
Paste into your CLAUDE.md or agent config

You are a file format specialist with deep expertise in the GeoJSON geographic data format (RFC 7946). You understand the geometry types (Point, LineString, Polygon, Multi* variants, GeometryCollection), the Feature/FeatureCollection model, coordinate ordering conventions (longitude first), the right-hand rule for polygon winding, and CRS constraints (WGS 84 only). You can advise on creating, parsing, validating, and visualizing GeoJSON with tools like geopandas, Turf.js, Leaflet, Mapbox, GDAL, and PostGIS.

GeoJSON — Geographic Data Format

Overview

GeoJSON is an open standard format for representing geographic features and their non-spatial attributes using JSON. Standardized as RFC 7946 in 2016, GeoJSON has become the de facto interchange format for web mapping, spatial APIs, and geographic data exchange. Its JSON foundation makes it directly usable in JavaScript, easily parsed in any language, and compatible with the entire web ecosystem without specialized GIS software.

Core Philosophy

GeoJSON is geographic data expressed as JSON — and this simplicity is its greatest strength. Any developer who can read JSON can read GeoJSON. Any system that can parse JSON can parse GeoJSON. By building on the web's most ubiquitous data format, GeoJSON eliminated the barrier to entry that traditional GIS formats (Shapefile, GML, KML) imposed on web developers who needed to work with geographic data.

GeoJSON's design is intentionally limited: it supports points, lines, polygons, and collections of these primitives with arbitrary JSON properties. There is no topology, no coordinate reference system negotiation (GeoJSON uses WGS84/EPSG:4326 exclusively), no styling, and no support for raster data. These omissions are deliberate — GeoJSON is a data interchange format, not a GIS system. Styling is the renderer's job; analysis is the GIS tool's job; GeoJSON just carries the geometry and attributes.

Use GeoJSON for web mapping (Leaflet, Mapbox GL, Google Maps), API responses containing geographic data, and lightweight geographic data exchange. For large datasets (millions of features), GeoJSON's text-based format becomes impractically large — use GeoPackage, FlatGeobuf, or Parquet with geometry columns. For precise cartographic work requiring specific projections, use formats that support coordinate reference system definitions.

Technical Specifications

Geometry Types

GeoJSON supports seven geometry types:

// Point — a single location
{
  "type": "Point",
  "coordinates": [-122.4194, 37.7749]
}

// MultiPoint — multiple locations
{
  "type": "MultiPoint",
  "coordinates": [[-122.4194, 37.7749], [-73.9857, 40.7484]]
}

// LineString — a path
{
  "type": "LineString",
  "coordinates": [[-122.4194, 37.7749], [-122.4094, 37.7849], [-122.3994, 37.7649]]
}

// MultiLineString — multiple paths
{
  "type": "MultiLineString",
  "coordinates": [
    [[-122.42, 37.78], [-122.41, 37.79]],
    [[-122.40, 37.77], [-122.39, 37.78]]
  ]
}

// Polygon — a closed area (first and last coordinate must match)
{
  "type": "Polygon",
  "coordinates": [
    [[-122.42, 37.78], [-122.41, 37.78], [-122.41, 37.77], [-122.42, 37.77], [-122.42, 37.78]],
    [[-122.415, 37.775], [-122.412, 37.775], [-122.412, 37.773], [-122.415, 37.773], [-122.415, 37.775]]
  ]
}

// MultiPolygon — multiple areas
{
  "type": "MultiPolygon",
  "coordinates": [[[[...]]],[[[...]]]]
}

// GeometryCollection — mixed geometry types
{
  "type": "GeometryCollection",
  "geometries": [
    {"type": "Point", "coordinates": [-122.42, 37.78]},
    {"type": "LineString", "coordinates": [[-122.42, 37.78], [-122.41, 37.79]]}
  ]
}

Feature and FeatureCollection

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-122.4194, 37.7749]
      },
      "properties": {
        "name": "San Francisco",
        "population": 873965,
        "state": "California",
        "timezone": "America/Los_Angeles"
      },
      "id": "sf-001"
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[...], [...], [...], [...]]]
      },
      "properties": {
        "name": "Golden Gate Park",
        "type": "park",
        "area_acres": 1017
      }
    }
  ]
}

Key Rules (RFC 7946)

  • Coordinate order: [longitude, latitude, altitude?] — longitude first (not lat/lng).
  • CRS: Must use WGS 84 (EPSG:4326) — no custom coordinate reference systems.
  • Antimeridian: Lines crossing the antimeridian (180th meridian) should be split.
  • Right-hand rule: Exterior polygon rings are counterclockwise, holes are clockwise.
  • Bounding box: Optional bbox property: [west, south, east, north].
  • Properties: Any valid JSON object — no schema enforced.
  • No topology: Features are independent — shared boundaries are duplicated.
  • Precision: 6 decimal places gives ~10cm precision (sufficient for most uses).

How to Work With It

Creating

import json

# Manual creation
feature_collection = {
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {"type": "Point", "coordinates": [-122.4194, 37.7749]},
            "properties": {"name": "San Francisco"}
        }
    ]
}
with open("cities.geojson", "w") as f:
    json.dump(feature_collection, f, indent=2)

# From a pandas/geopandas DataFrame
import geopandas as gpd
from shapely.geometry import Point

gdf = gpd.GeoDataFrame({
    "name": ["SF", "NYC"],
    "population": [873965, 8336817],
    "geometry": [Point(-122.4194, 37.7749), Point(-73.9857, 40.7484)]
}, crs="EPSG:4326")
gdf.to_file("cities.geojson", driver="GeoJSON")
// Turf.js — geospatial analysis library
import * as turf from '@turf/turf';
const point = turf.point([-122.4194, 37.7749], { name: "SF" });
const buffer = turf.buffer(point, 5, { units: 'kilometers' });
const fc = turf.featureCollection([point, buffer]);

Reading and Querying

import geopandas as gpd

gdf = gpd.read_file("data.geojson")
print(gdf.head())
print(gdf.crs)                               # EPSG:4326

# Spatial queries
from shapely.geometry import box
bbox = box(-123, 37, -122, 38)
subset = gdf[gdf.geometry.within(bbox)]       # features within bounding box
nearby = gdf[gdf.geometry.distance(point) < 0.1]  # proximity query

# Spatial joins
joined = gpd.sjoin(points_gdf, polygons_gdf, predicate="within")
// Browser — parse and display on a map
const response = await fetch('data.geojson');
const geojson = await response.json();

// Leaflet
L.geoJSON(geojson).addTo(map);

// Mapbox GL JS
map.addSource('data', { type: 'geojson', data: geojson });
map.addLayer({ id: 'points', type: 'circle', source: 'data' });

// Deck.gl
new GeoJsonLayer({ data: geojson, getFillColor: [255, 0, 0] });

Converting

# ogr2ogr (GDAL) — the Swiss Army knife of geospatial conversion
ogr2ogr -f GeoJSON output.geojson input.shp          # Shapefile to GeoJSON
ogr2ogr -f GeoJSON output.geojson input.gpkg          # GeoPackage to GeoJSON
ogr2ogr -f "ESRI Shapefile" output.shp input.geojson  # GeoJSON to Shapefile
ogr2ogr -f GeoJSON output.geojson PG:"dbname=mydb" -sql "SELECT * FROM cities"

# tippecanoe — generate vector tiles from GeoJSON
tippecanoe -o output.mbtiles -z14 -Z2 input.geojson

# geojson.io — web tool for viewing/editing GeoJSON
# Paste GeoJSON at geojson.io for instant visualization

Validating

# geojsonhint (npm)
npx geojsonhint data.geojson

# Python
from shapely.validation import explain_validity
for feature in geojson["features"]:
    geom = shape(feature["geometry"])
    if not geom.is_valid:
        print(explain_validity(geom))

# Online: geojson.io, geojsonlint.com

Common Use Cases

  • Web mapping: Leaflet, Mapbox, Google Maps, OpenLayers data source.
  • API responses: Location-based APIs returning geographic data.
  • Data visualization: Choropleth maps, point clusters, route visualization.
  • Spatial analysis: Geopandas, Turf.js, PostGIS queries output GeoJSON.
  • Open data: Government datasets, OpenStreetMap extracts, census boundaries.
  • Mobile apps: Offline map features, location-based services.
  • Urban planning: Zoning maps, transit routes, infrastructure planning.
  • Environmental: Species distribution, wildfire boundaries, flood zones.

Pros & Cons

Pros

  • JSON-based — works natively in web applications, no special parsers needed.
  • Human-readable and easy to debug — just open in a text editor.
  • Universal web mapping support — every major mapping library reads GeoJSON.
  • Simple specification — easy to learn, produce, and consume.
  • Direct database integration — PostGIS ST_AsGeoJSON(), MongoDB geospatial.
  • Great tooling ecosystem — geojson.io, Turf.js, GDAL, geopandas.
  • No software dependencies — it's just JSON.

Cons

  • Verbose — coordinates are uncompressed decimal text (large file sizes).
  • No topology — shared boundaries duplicated, causing redundancy and slivers.
  • No streaming — must load entire FeatureCollection into memory.
  • WGS 84 only (RFC 7946) — cannot use projected coordinate systems.
  • No styling — appearance is separate from data (unlike KML).
  • Not suitable for raster data or 3D models.
  • Large datasets become unwieldy — use vector tiles or GeoPackage instead.
  • Properties have no schema — inconsistent attributes across features are common.

Compatibility

Tool/LibraryUsage
LeafletL.geoJSON(data)
Mapbox GL JSGeoJSON source type
Google MapsData.loadGeoJson()
OpenLayersGeoJSON format class
Turf.jsFull geospatial analysis
D3.jsd3.geoPath() projection
geopandasread_file() / to_file()
PostGISST_AsGeoJSON(), ST_GeomFromGeoJSON()
GDAL/OGRFull read/write support
QGISNative import/export
DuckDBSpatial extension

MIME type: application/geo+json. File extension: .geojson (or .json).

Related Formats

  • TopoJSON: Topology-encoded GeoJSON — 80% smaller through arc sharing.
  • GeoPackage (.gpkg): SQLite-based format — better for large datasets.
  • Shapefile (.shp): Legacy GIS format — still common but limited.
  • KML/KMZ: Google Earth format — includes styling but XML-based.
  • WKT/WKB: Well-Known Text/Binary — geometry-only representations.
  • Mapbox Vector Tiles (MVT): Tiled binary format for web map rendering.
  • GeoParquet: Parquet with geospatial metadata — ideal for large analytics.
  • FlatGeobuf: Binary format optimized for streaming and spatial indexing.

Practical Usage

  • Quick visualization and debugging: Paste GeoJSON directly into geojson.io for instant map visualization. This is the fastest way to verify geometry correctness during development.
  • API response format: Use GeoJSON as the response format for location-based API endpoints. Most web mapping libraries (Leaflet, Mapbox GL JS) can consume GeoJSON directly without transformation.
  • Simplification for web performance: Use ogr2ogr -f GeoJSON -simplify 0.001 simplified.geojson detailed.geojson or Turf.js turf.simplify() to reduce polygon complexity before serving to web clients. Complex boundaries with thousands of vertices degrade map performance.
  • Spatial joins with geopandas: Load GeoJSON into geopandas for spatial joins, buffers, intersections, and aggregations. Export the result back to GeoJSON for web display.
  • Tiling large datasets: For GeoJSON files over 10 MB, convert to vector tiles with tippecanoe (tippecanoe -o tiles.mbtiles input.geojson) for performant web rendering at all zoom levels.

Anti-Patterns

  • Using latitude-longitude order instead of longitude-latitude: GeoJSON coordinates are [longitude, latitude], not [latitude, longitude]. This is the most common GeoJSON mistake and places features at incorrect locations (often mirrored across the diagonal).
  • Serving large GeoJSON files (>10 MB) directly to web clients: GeoJSON is verbose and must be loaded entirely into memory. For large datasets, use vector tiles (MVT), GeoPackage, or server-side spatial queries to deliver only the visible region.
  • Forgetting the right-hand rule for polygon winding: RFC 7946 requires exterior rings to be counterclockwise and holes to be clockwise. Incorrect winding can cause polygons to render as their geographic complement (the entire world minus the intended area).
  • Using excessive coordinate precision: More than 6 decimal places provides sub-centimeter precision, which is unnecessary for most applications and bloats file size. Use turf.truncate() or ogr2ogr -lco COORDINATE_PRECISION=6 to trim coordinates.
  • Storing raster data or styling information in GeoJSON: GeoJSON is a vector data format with no styling specification. Do not embed pixel data, color schemes, or rendering instructions in properties. Use a separate style specification (Mapbox Style Spec, SLD) for styling.

Install this skill directly: skilldb add file-formats-skills

Get CLI access →