Learn

GeoJSON examples

The fastest way to understand GeoJSON is to read it. All examples below are valid RFC 7946 and copy-paste ready.

TL;DR
  • Every GeoJSON object needs a type field — coordinates are always [longitude, latitude]
  • Rings in Polygons must close: the first and last position must be identical
  • FeatureCollection is the standard top-level envelope; most libraries and databases expect it
  • Free datasets: geojson-maps.ash.ms (countries), country-json on npm, @geo-maps/countries-land on npm

Minimal valid GeoJSON

The smallest document that passes any GeoJSON validator is an empty FeatureCollection:

{
  "type": "FeatureCollection",
  "features": []
}

Two fields. Both required. features can be empty — an empty array is valid GeoJSON. This is useful as an initial state before features are added.

Point

A Point is a single location represented as [longitude, latitude]. Here's a Feature wrapping the Eiffel Tower:

{
  "type": "Feature",
  "id": "eiffel-tower",
  "geometry": {
    "type": "Point",
    "coordinates": [2.2945, 48.8584]
  },
  "properties": {
    "name": "Eiffel Tower",
    "category": "landmark",
    "altitude_m": 330
  }
}

Note the coordinate order: [longitude, latitude], not [latitude, longitude]. The Eiffel Tower is at longitude 2.2945°E, latitude 48.8584°N. Swapping the order places it in the Atlantic Ocean.

The id field is optional on a Feature but useful for referencing specific features in event handlers and filter expressions.

LineString

A LineString is an ordered sequence of positions forming a path. This example traces a three-stop route through central London — from King's Cross to Trafalgar Square to Waterloo Station:

{
  "type": "Feature",
  "geometry": {
    "type": "LineString",
    "coordinates": [
      [-0.1240, 51.5308],
      [-0.1281, 51.5080],
      [-0.1143, 51.5036]
    ]
  },
  "properties": {
    "name": "King's Cross → Trafalgar Square → Waterloo",
    "distance_km": 4.1
  }
}

Each position in the coordinates array is [longitude, latitude]. A LineString requires at least two positions. There is no maximum.

Polygon

Simple polygon

A Polygon is a closed shape. The coordinates array holds one or more rings. The outer boundary is the first ring. Every ring must close — the last position must be identical to the first.

Here's a park boundary in Hyde Park, London:

{
  "type": "Feature",
  "geometry": {
    "type": "Polygon",
    "coordinates": [
      [
        [-0.1750, 51.5030],
        [-0.1580, 51.5030],
        [-0.1580, 51.5130],
        [-0.1750, 51.5130],
        [-0.1750, 51.5030]
      ]
    ]
  },
  "properties": {
    "name": "Hyde Park (simplified)",
    "area_ha": 142
  }
}

The outer ring uses counter-clockwise winding order, as required by RFC 7946. Five positions: four corners plus the closing repeat.

Polygon with a hole

To cut a hole in a polygon, add a second ring to coordinates. The outer boundary is the first ring (counter-clockwise); the hole is the second ring (clockwise):

{
  "type": "Feature",
  "geometry": {
    "type": "Polygon",
    "coordinates": [
      [
        [-0.1750, 51.5030],
        [-0.1580, 51.5030],
        [-0.1580, 51.5130],
        [-0.1750, 51.5130],
        [-0.1750, 51.5030]
      ],
      [
        [-0.1700, 51.5060],
        [-0.1640, 51.5060],
        [-0.1640, 51.5100],
        [-0.1700, 51.5100],
        [-0.1700, 51.5060]
      ]
    ]
  },
  "properties": {
    "name": "Hyde Park with building footprint removed",
    "area_ha": 139
  }
}

The second ring — the hole — is wound clockwise. It must be fully contained within the outer ring.

MultiPolygon

A MultiPolygon represents a single geographic feature composed of multiple disconnected polygons. The canonical case: a country with islands.

This example uses simplified boundaries for Denmark's mainland and the island of Bornholm:

{
  "type": "Feature",
  "geometry": {
    "type": "MultiPolygon",
    "coordinates": [
      [
        [
          [8.0, 54.8],
          [12.7, 54.8],
          [12.7, 57.8],
          [8.0, 57.8],
          [8.0, 54.8]
        ]
      ],
      [
        [
          [14.7, 55.0],
          [15.2, 55.0],
          [15.2, 55.3],
          [14.7, 55.3],
          [14.7, 55.0]
        ]
      ]
    ]
  },
  "properties": {
    "name": "Denmark (simplified)",
    "iso_a2": "DK"
  }
}

MultiPolygon has one extra level of nesting compared to Polygon. Polygon.coordinates is [rings]. MultiPolygon.coordinates is [[rings], [rings], ...] — an array of polygon coordinate arrays.

FeatureCollection with mixed types

Real datasets are usually FeatureCollections with multiple features of different types. Each Feature can have a different shape for properties — there is no enforced schema across features.

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "cafe-001",
      "geometry": {
        "type": "Point",
        "coordinates": [-0.1281, 51.5080]
      },
      "properties": {
        "name": "Flat White",
        "category": "cafe",
        "opens": "07:30"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [-0.1281, 51.5080],
          [-0.1320, 51.5100],
          [-0.1380, 51.5095]
        ]
      },
      "properties": {
        "name": "Cycle route to park",
        "surface": "tarmac",
        "distance_km": 0.8
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [-0.1420, 51.5070],
            [-0.1300, 51.5070],
            [-0.1300, 51.5120],
            [-0.1420, 51.5120],
            [-0.1420, 51.5070]
          ]
        ]
      },
      "properties": {
        "name": "Regent's Park",
        "area_ha": 166,
        "dogs_allowed": true
      }
    }
  ]
}

The three features share a name property by coincidence — there is no requirement for property keys to match across features. Each feature is independently valid GeoJSON. Leaflet, Mapbox GL JS, and PostGIS all accept mixed-type FeatureCollections without configuration.

Where to get free GeoJSON data

geojson-maps.ash.ms

Browser-based tool. Choose a region, pick a resolution (10m/50m/110m), click download. No account required. The 50m world file is approximately 4 MB — a reasonable starting point for most world maps. The 110m file is under 1 MB and suitable for thumbnails or performance-constrained contexts.

country-json on GitHub

github.com/samayo/country-json — per-country datasets in JSON with GeoJSON geometries included. MIT licensed. Covers 250+ countries with properties like area, population, capital city, and ISO codes. Install in Node.js: npm install country-json.

@geo-maps/countries-land

An npm package that exposes country land polygons as a ready-to-use FeatureCollection:

import countries from '@geo-maps/countries-land-10km'
// countries is a FeatureCollection — no fetch, no parsing
console.log(countries.features.length) // 177

Available at three resolutions: countries-land-1km (~23 MB), countries-land-10km (~4 MB), countries-land-110km (~800 KB). Use the 110km version in the browser unless you need country-level detail.

How to validate your GeoJSON

Spatibase GeoJSON Validator

Check your GeoJSON for structure and geometry problems with the Spatibase GeoJSON Validator, our free online tool.

geojson.io

Paste GeoJSON into geojson.io. Valid GeoJSON renders on the map immediately. Invalid GeoJSON shows an error banner in the bottom-left corner identifying the line and reason. The fastest way to catch a typo before sending to an API.

RFC 7946 manual checklist

For a quick manual review without a tool:

  1. Every object has a type field.
  2. Coordinate arrays have the right depth: [lon, lat] for Point, [[lon, lat], ...] for LineString, [[[lon, lat], ...]] for Polygon.
  3. Every ring in a Polygon closes — first position equals last position.
  4. Longitude is in the range −180 to 180; latitude is in −90 to 90.

geojsonhint

The geojsonhint npm package validates programmatically and returns structured errors:

import geojsonhint from '@mapbox/geojsonhint'

const errors = geojsonhint.hint(data)
if (errors.length > 0) {
  console.error(errors)
  // [{ message: 'Polygons and MultiPolygons should follow the right-hand rule', line: 12 }]
}

geojsonhint.hint() accepts a parsed object or a JSON string. It returns an array of { message, line } objects — empty array means valid.

Frequently asked questions

Where can I download free GeoJSON data?
geojson-maps.ash.ms provides world country boundaries at three resolution levels (10m, 50m, 110m) as downloadable GeoJSON files. The country-json GitHub repository has per-country datasets. The @geo-maps/countries-land npm package gives you a FeatureCollection of all country land polygons importable in Node.js.
What does a GeoJSON file look like?
A GeoJSON file is a plain JSON file. The top-level object has a 'type' of 'FeatureCollection' and a 'features' array. Each Feature has a 'type' of 'Feature', a 'geometry' object (with a type like 'Point' or 'Polygon' and a 'coordinates' array), and a 'properties' object for attributes.
What is a valid GeoJSON FeatureCollection?
A valid GeoJSON FeatureCollection must have a 'type' field equal to 'FeatureCollection' and a 'features' array. The array can be empty. Each element must be a valid Feature object. Coordinates must use [longitude, latitude] order in WGS 84.
How do I validate GeoJSON?
Paste the GeoJSON into the Spatibase GeoJSON Validator (spatibase.com/tools/geojson-validator) to check it against RFC 7946, or into geojson.io — invalid documents show an error. For programmatic validation, use the geojsonhint npm package: it returns an array of errors with line numbers. For a quick manual check: confirm 'type' is present on every object, rings close (first === last position), and coordinates are [longitude, latitude].
What is the file extension for GeoJSON?
The standard file extension is .geojson. The .json extension also works and is widely supported. When serving GeoJSON over HTTP, use the Content-Type header application/geo+json regardless of file extension.

If you want to draw GeoJSON shapes rather than write them by hand, Element Editor's playground lets you draw points, lines, and polygons on a map and download the result as a GeoJSON file.

Continue reading

Mike Acler

Mike Acler

Design engineer and creator of Element Editor, an open-source map drawing and GeoJSON editing library for React. Writes about web cartography and GIS tooling.