GeoJSON Debugging Guide

Fix common GeoJSON problems: points in the ocean reversed polygons invalid geometries and coordinate order issues.

Points Appear in the Wrong Location

Symptom

Your markers or points render on the map, but they appear thousands of kilometers from where they should be — often in the ocean, on the wrong continent, or mirrored across the equator. Every point is wrong by the same relative offset.

Cause: Coordinate Order Swap ([lat, lng] vs [lng, lat])

GeoJSON mandates [longitude, latitude] order per RFC 7946 Section 3.1.1. The confusion is endemic because Google Maps, Leaflet's L.latLng(), and most geocoding APIs return [latitude, longitude]. When you paste coordinates from one of these sources directly into a GeoJSON coordinates array without swapping, every point lands in the wrong location. The Golden Gate Bridge at the correct GeoJSON coordinates is [-122.4783, 37.8199]. Written in the wrong order as [37.8199, -122.4783], it places a marker in the Southern Indian Ocean.

The Problem in Code

json{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [37.8199, -122.4783]
      },
      "properties": { "name": "Golden Gate Bridge" }
    }
  ]
}

The Fix

Swap every coordinate pair so longitude comes first. Use our Coordinate Flip Tool to reverse all coordinate pairs in an entire GeoJSON file in one operation — it handles Point, LineString, Polygon, and all Multi- variants. After flipping, the corrected feature reads:

json{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [-122.4783, 37.8199]
  },
  "properties": { "name": "Golden Gate Bridge" }
}

To prevent this in code, always be explicit about which variable is longitude and which is latitude: const coordinates = [longitude, latitude]; // GeoJSON order. If you use the Google Maps Geocoding API, destructure carefully: const { lat, lng } = result.geometry.location; const coords = [lng, lat];

Polygon Appears Inside-Out or Inverted

Symptom

The polygon renders, but the entire map is shaded except for the area where your polygon should be — as if the fill covers the whole world with a "hole" cut out where your shape is. Or the polygon clips everything outside its boundary instead of shading inside it.

Cause: Incorrect Winding Order (RFC 7946 Right-Hand Rule)

RFC 7946 Section 3.1.6 mandates the right-hand rule: exterior polygon rings must wind counter-clockwise (CCW) and interior rings (holes) must wind clockwise (CW), when coordinates are in geographic (lon/lat) space. The original 2008 GeoJSON spec did not specify winding order, so a significant amount of legacy GeoJSON data has clockwise exterior rings. When a renderer that enforces RFC 7946 (like Mapbox GL JS) encounters a clockwise exterior ring, it treats the exterior of the polygon as the interior, producing the inside-out visual effect. A square around the Empire State Building in the wrong winding order:

The Problem in Code

json{
  "type": "Polygon",
  "coordinates": [[
    [-73.9807, 40.7534],
    [-73.9807, 40.7434],
    [-73.9907, 40.7434],
    [-73.9907, 40.7534],
    [-73.9807, 40.7534]
  ]]
}

The ring above winds clockwise (right, down, left, up). RFC 7946 requires it to wind counter-clockwise for an exterior ring.

The Fix

Reverse the coordinate array for any exterior ring that winds clockwise, and for any hole that winds counter-clockwise. Use our Rewind Tool, which applies RFC 7946-compliant winding to all rings in a FeatureCollection automatically. The corrected exterior ring:

json{
  "type": "Polygon",
  "coordinates": [[
    [-73.9807, 40.7534],
    [-73.9907, 40.7534],
    [-73.9907, 40.7434],
    [-73.9807, 40.7434],
    [-73.9807, 40.7534]
  ]]
}

To determine winding order programmatically, compute the signed area of a ring using the shoelace formula: if the result is positive the ring is counter-clockwise; if negative it is clockwise. Turf.js's turf.booleanClockwise() function does this in one call.

"Invalid GeoJSON" Errors

Symptom

A library, API, or validator rejects your GeoJSON with an error like "Invalid GeoJSON", "Parse error", "Unexpected type", or "Ring is not closed". The file looks correct to the naked eye.

Cause 1: Unclosed Polygon Rings

RFC 7946 requires the first and last positions of every polygon ring to be identical. A triangle needs 4 coordinate pairs — the three vertices plus a fourth that repeats the first. This is the most common validation failure.

json// Invalid — ring not closed (3 positions)
{
  "type": "Polygon",
  "coordinates": [[
    [-73.9907, 40.7534],
    [-73.9807, 40.7434],
    [-73.9907, 40.7434]
  ]]
}

// Valid — ring closed (4 positions, first == last)
{
  "type": "Polygon",
  "coordinates": [[
    [-73.9907, 40.7534],
    [-73.9807, 40.7434],
    [-73.9907, 40.7434],
    [-73.9907, 40.7534]
  ]]
}

Cause 2: Wrong Type String Casing

GeoJSON type strings are case-sensitive and PascalCase. "point", "POINT", and "linestring" are all invalid. The 9 valid type values are: Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection, Feature, FeatureCollection.

Cause 3: Missing Required Members

A Feature must have both "geometry" and "properties" keys (each may be null, but neither may be absent). A FeatureCollection must have a "features" array (may be empty, but must be present). A Geometry must have both "type" and "coordinates" (except GeometryCollection, which uses "geometries" instead of "coordinates").

The Fix

Run your file through the GeoJSON Validator, which reports the exact member or ring causing the failure with line numbers. For programmatic validation in Node.js, the geojson-validation npm package (150k weekly downloads) checks all RFC 7946 constraints and returns structured error objects.

Features Don't Appear on the Map

Symptom

The GeoJSON loads without an error, the layer is added to the map, but no features are visible — the map is empty or unchanged. No console errors.

Cause 1: Coordinates Out of Range

If coordinates are in a projected CRS (like Web Mercator EPSG:3857), their values are in meters and will be in the millions — far outside the valid WGS 84 range of −180 to 180 (lon) and −90 to 90 (lat). The renderer places the features off-screen and they never appear in any normal map view. For example, the Empire State Building in EPSG:3857 is at approximately [-8232838, 4975221] — not valid GeoJSON coordinates. Reproject to WGS 84 using ogr2ogr -t_srs EPSG:4326 before using the file.

Cause 2: Empty Geometries

Converted or exported data sometimes contains features with empty coordinate arrays — "coordinates": [] — or geometry objects with zero-length arrays. These pass many basic JSON validity checks but render as nothing. Some Shapefile-to-GeoJSON converters produce empty geometries for null-shape records in the source file. Check by filtering features where feature.geometry?.coordinates?.length === 0.

Cause 3: bbox Mismatch Causing Auto-Zoom to Wrong Area

If your GeoJSON includes a "bbox" member that does not match the actual feature extents, map libraries that auto-zoom to the bbox (like Leaflet's fitBounds based on the bbox) will pan to an empty area. The features exist but are off-screen. Delete the stale bbox and let the library compute the extent from the actual coordinates, or recalculate it using Turf.js's turf.bbox().

Cause 4: Layer Ordering / Z-Index

In Mapbox GL JS, layers are drawn in the order they are added. If a filled polygon layer is added after point and line layers without an explicit beforeId parameter, the polygon fill will cover the other layers. Add polygon fill layers first, or use the beforeId option: map.addLayer(polygonLayer, 'point-layer-id') to insert it beneath the point layer.

The Fix

Validate coordinates are in the expected range using the GeoJSON Validator. For projection issues, use our Coordinate Precision Tool — coordinates in the millions are immediately obvious. For empty geometry issues, filter them before rendering: fc.features = fc.features.filter(f => f.geometry && f.geometry.coordinates.length > 0).

File Too Large to Load

Symptom

The browser tab freezes or crashes when loading the file. The map takes 10+ seconds to display. You see "Aw, Snap!" in Chrome or an out-of-memory error in the console. The file is over 5 MB.

Cause: GeoJSON Is a Text Format with No Compression or Indexing

GeoJSON stores every coordinate as a decimal string. A polygon with 1,000 vertices and 6-decimal-place coordinates uses roughly 20 bytes per coordinate pair — 20 KB for that single polygon. A FeatureCollection with 500 such polygons is 10 MB before any properties. Parsed into JavaScript objects, memory usage expands 4–6x: a 10 MB GeoJSON file may consume 60 MB of heap. Unlike FlatGeobuf or MBTiles, GeoJSON has no spatial index, so the entire file must be loaded before any feature can be accessed.

Fix 1: Reduce Coordinate Precision

Trimming from 15 decimal places (JavaScript's float64 output) to 6 reduces file size by 30–50% with no perceptible accuracy loss for any web mapping use case. Use our Coordinate Precision Tool to set a maximum decimal count across all coordinates in your file. The table below shows precision vs. ground distance:

textDecimal places | Ground distance (at equator)
5              | ~1.1 meters
6              | ~0.11 meters (consumer GPS accuracy)
7              | ~0.011 meters (survey-grade GPS)
8+             | Sub-millimeter — no practical value for web maps

Fix 2: Simplify Geometries

The Douglas-Peucker algorithm removes vertices that fall within a tolerance distance of the simplified line, preserving shape while dramatically reducing vertex count. A detailed coastline polygon with 50,000 vertices may simplify to 2,000 vertices at a 10-meter tolerance with no visible difference at zoom levels below 14. Use our GeoJSON Simplifier to apply Douglas-Peucker simplification with a configurable tolerance in degrees.

Fix 3: Minify the JSON

Pretty-printed GeoJSON with indentation and newlines is 20–40% larger than minified JSON. For a 5 MB pretty-printed file, minification alone saves 1–2 MB. Use our GeoJSON Minifier to strip all whitespace. Serve the result with gzip or Brotli compression from your server — GeoJSON compresses extremely well (typically 70–80% reduction) because of its repetitive structure.

Fix 4: Switch to a Binary Format for Large Datasets

For datasets over 10 MB that must be served to the browser, GeoJSON is the wrong tool. FlatGeobuf supports HTTP range requests with a spatial index, letting you load only features in the current viewport without downloading the full file. Mapbox Vector Tiles (MVT) serve pre-tiled, pre-simplified geometry at each zoom level. GeoParquet is ideal for server-side analytics — columnar storage enables reading only the columns you need. The command ogr2ogr -f FlatGeobuf output.fgb input.geojson converts in seconds.

Properties Missing After Conversion

Symptom

You convert a Shapefile, CSV, KML, or database export to GeoJSON and find that some or all feature properties are absent, truncated, or have wrong values. A field named population_density becomes population_d or disappears entirely.

Cause 1: Shapefile Field Name Truncation

The DBF format used by Shapefiles limits field names to 10 characters. If your source Shapefile has a field named population_density, it was already stored as population_d in the DBF. When ogr2ogr or any converter reads it, the truncated name is what ends up in GeoJSON — the original long name was never stored. There is no automatic way to recover the original name; you must rename properties manually after conversion or maintain a field name mapping.

Cause 2: Encoding Issues

Shapefiles commonly use Windows-1252 (Latin-1) or ISO-8859-1 encoding for text fields. GeoJSON must be UTF-8 per RFC 7946. If the converter doesn't explicitly transcode the encoding, non-ASCII characters (accented letters, CJK characters, special symbols) will be corrupted or produce mojibake. The fix with ogr2ogr: ogr2ogr -f GeoJSON -lco ENCODING=UTF-8 output.geojson input.shp. If you don't know the source encoding, try file -i input.dbf or open the DBF in a hex editor and look for byte values above 0x7F.

Cause 3: Nested JSON Properties Flattened or Dropped

Formats like CSV and some database exports represent all data in flat key-value pairs. If your GeoJSON has nested properties — e.g., "properties": {"address": {"city": "New York", "zip": "10001"}} — and you convert to Shapefile and back, the nested object is serialized to a string or dropped entirely, because DBF has no concept of nested structures. When converting GeoJSON to flat formats, flatten nested properties first. Tools like jq '[.features[].properties | flatten]' or the npm package flat can pre-process nested properties into dot-notation keys like address.city.

Cause 4: Type Coercion Errors

Shapefiles store numeric fields as fixed-width decimals with a defined precision. A GeoJSON property stored as the JavaScript number 1234567890123 (a 13-digit integer) may overflow a Shapefile's numeric field and be stored as null or clipped. Boolean values (true/false) in GeoJSON properties have no native Shapefile equivalent and are often coerced to integers (1/0) or strings ("T"/"F") depending on the converter.

The Fix

Inspect properties immediately after conversion before assuming they are correct. In the browser, use our GeoJSON Viewer to browse feature properties and confirm all expected fields are present with correct values. For bulk property inspection from the command line: ogr2ogr -f CSV /vsistdout/ input.geojson | head -5 prints the first 5 rows including headers, making truncation and missing fields immediately visible.