Cell & Shape

GeoJSON guide

Why is my GeoJSON polygon invalid?

If a tool just told you “Polygons and MultiPolygons should follow the right-hand rule,” your ring is wound the wrong way — it’s not corrupt, just clockwise where RFC 7946 wants counterclockwise. This page explains the rule, shows how the shoelace formula detects it, how to rewind a ring, and the other reasons a polygon gets flagged invalid — each with the exact message our validator prints. Everything runs in your browser; nothing you paste is uploaded.

What the right-hand rule actually says

RFC 7946, the GeoJSON standard, pins down winding order for polygons:

The mnemonic is the “right-hand rule”: walk the boundary in the order the points are listed, and the polygon’s interior is always on your left. The spec chose a fixed convention because a bare list of coordinates is otherwise ambiguous on a sphere. The same four corners, read counterclockwise, describe a one-degree square; read clockwise, a spherical-geometry engine can legitimately interpret them as the entire Earth minus that square. Winding order is the only thing that disambiguates the small inside from the enormous outside.

One nuance worth knowing: RFC 7946 phrases this as a SHOULD, not a MUST. Conforming parsers are required to accept rings in either direction, and producers should emit the right-hand order. That’s why a “wrong” winding is a warning here, not a hard error — but as the next section shows, plenty of downstream systems treat it as fatal.

Why most renderers tolerate it — but some pipelines break

Draw a clockwise polygon in Leaflet, MapLibre’s default fill, or almost any GeoJSON preview and it looks perfect: those renderers treat a polygon as a flat filled shape and don’t care which way you walked the boundary. That tolerance is exactly why bad winding survives so long undetected — until it reaches a system that reads winding as meaning:

Vector-tile encoders
Mapbox Vector Tile geometry uses fill winding to distinguish exterior boundaries from holes. Feed a tile encoder a clockwise exterior ring and the fill can invert — the inside renders empty and the outside fills — or the feature drops entirely.
Spherical-geometry libraries
Turf’s boolean operations (intersect, union, difference) and Google’s S2 region coverer compute on the sphere, where a ring’s direction sets which side is interior. A clockwise exterior becomes “all of the globe except this patch,” so an area query returns the complement of what you meant.
Geography-typed DB columns
PostGIS geography and SQL Server geography validate winding on insert. Depending on the engine and version they either reject the row outright or silently store the complementary polygon — a bug you won’t notice until a containment query returns the whole world.
Strict RFC 7946 validators
Linters and schema checkers built to the spec’s producer guidance emit the very string that brought you here: “Polygons and MultiPolygons should follow the right-hand rule.”

How detection works: the shoelace formula

Winding direction falls straight out of a ring’s signed area, computed with the shoelace (surveyor’s) formula. For a ring of points (x₀,y₀), (x₁,y₁), … — remember GeoJSON order is [longitude, latitude], so x = lon and y = lat:

2A = Σ (xᵢ · yᵢ₊₁ − xᵢ₊₁ · yᵢ)

A positive signed area means counterclockwise; negative means clockwise. Here’s the correct (CCW) square worked out term by term — the exterior ring is [100,0] → [101,0] → [101,1] → [100,1] → [100,0]:

Edge (xᵢ,yᵢ) → (xᵢ₊₁,yᵢ₊₁) xᵢ · yᵢ₊₁ xᵢ₊₁ · yᵢ Term
(100,0) → (101,0)100·0 = 0101·0 = 00
(101,0) → (101,1)101·1 = 101101·0 = 0+101
(101,1) → (100,1)101·1 = 101100·1 = 100+1
(100,1) → (100,0)100·0 = 0100·1 = 100−100
Sum = 2A+2

So 2A = +2, A = +1. The sign is positive → counterclockwise → a valid RFC 7946 exterior ring. Reverse the point order and every term flips sign, giving 2A = −2, A = −1clockwise → the winding-order warning. That single sign is the whole test.

How to fix it: rewind

The fix is simply to reverse the ring’s point order when the sign is wrong — that’s what “rewind” means. Below is the very same square in both directions. The signed area confirms the label, so you can trust which is which:

Correct · counterclockwise signed area = +1
{
  "type": "Polygon",
  "coordinates": [
    [
      [100.0, 0.0],
      [101.0, 0.0],
      [101.0, 1.0],
      [100.0, 1.0],
      [100.0, 0.0]
    ]
  ]
}
Wrong · clockwise signed area = −1
{
  "type": "Polygon",
  "coordinates": [
    [
      [100.0, 0.0],
      [100.0, 1.0],
      [101.0, 1.0],
      [101.0, 0.0],
      [100.0, 0.0]
    ]
  ]
}

Programmatically, the incumbent tool is @mapbox/geojson-rewind: rewind(geojson) returns the geometry with RFC-correct winding. @turf/rewind does the same and takes a { reverse: true } option for the opposite convention. Both simply compute the shoelace sign per ring and flip the ones that are backwards. On this site you can skip the code entirely — paste the polygon into the inspector and click the one-click rewind fix next to the warning.

See it in the inspector

The polygon below is wound clockwise on purpose — the reversed version of the square above. Open it in the inspector and you’ll see the winding-order warning with a one-click rewind, or copy it and paste it yourself.

Open this broken polygon in the Inspector →

Deliberately clockwise
{
  "type": "Polygon",
  "coordinates": [
    [
      [100.0, 0.0],
      [100.0, 1.0],
      [101.0, 1.0],
      [101.0, 0.0],
      [100.0, 0.0]
    ]
  ]
}

Other reasons your polygon is invalid

Winding order is a warning, not the only thing that gets a polygon rejected. These are the other problems our validator catches. Each row shows a minimal broken ring, the stable issue code, and the exact message the validator prints — copied verbatim from its output, not paraphrased.

Unclosed ring ring-not-closed error
[[100,0],[101,0],[101,1],[100,1]]

The last point does not repeat the first, so the ring never closes.

Ring not closed — ring 1 (exterior) starts at (100, 0) but ends at (100, 1). Close it by repeating the first point at the end.

Self-intersection self-intersection error
[[100,0],[101,1],[101,0],[100,1],[100,0]]

A “bow-tie” ring whose edges cross — the exterior boundary is not simple.

Self-intersection — the polygon crosses itself at (100.5, 0.5). This is an invalid ring/line; most tools reject it.

Duplicate consecutive vertices duplicate-consecutive-points warning
[[100,0],[101,0],[101,1],[101,1],[100,1],[100,0]]

A point repeated back-to-back makes a zero-length edge — so the validator also reports a self-intersection at that vertex.

Duplicate points — ring 1 (exterior) repeats 1 consecutive point (e.g. (101, 1)). Remove the repeats.

Out-of-range coordinates latitude-out-of-range error
[[100,0],[101,0],[101,95],[100,95],[100,0]]

Latitude 95° is impossible (valid range is −90…90). Longitude has its own code, longitude-out-of-range.

2 coordinates have latitude outside [-90, 90].

The winding-order case shown earlier carries the code winding-order with the message: “Winding order — ring 1 (exterior) is clockwise, but RFC 7946 wants exterior rings counterclockwise. Most renderers tolerate it; rewind to comply.”

Sources and further reading

The winding rule is quoted from the GeoJSON standard; the other failure modes are checked against OGC simple-feature validity and this site's fixture-backed validator.

Frequently asked questions

Why does GeoJSON require counterclockwise rings?
RFC 7946 (the GeoJSON standard) fixes a winding convention so that a ring is unambiguous about which side is “inside.” Under the right-hand rule, an exterior ring is wound counterclockwise and interior rings (holes) are wound clockwise; walking the boundary in its listed order always keeps the polygon’s interior on your left. Without a fixed convention, the same list of points could mean either a small island or the entire planet minus that island — spherical geometry libraries genuinely interpret it both ways. Note the RFC softens this to a “SHOULD”: parsers must accept either winding, but producers should emit the right-hand order.
Do I have to fix the winding order?
It depends entirely on where the geometry is going. Most web renderers (Leaflet, MapLibre’s default fill, most GeoJSON viewers) treat a polygon as a flat shape and draw it correctly regardless of winding, so a clockwise ring looks fine. But four common pipelines do break: Mapbox/vector-tile encoders that rely on fill winding to decide inside-vs-outside, spherical booleans (Turf’s intersect/union, Google S2 coverings) that read a clockwise exterior as “everything except this area,” geography-typed database columns (PostGIS geography, SQL Server geography) that reject or invert wrong-wound rings, and strict RFC 7946 validators. If your data touches any of those, rewind it. If it only ever gets drawn on a 2-D web map, the warning is cosmetic.
How do I rewind a polygon programmatically?
The incumbent tool is @mapbox/geojson-rewind: `rewind(geojson)` returns the same geometry with RFC-7946-correct winding (exterior counterclockwise, holes clockwise). @turf/rewind does the same and accepts a `{ reverse: true }` option for the opposite convention. Both work by computing each ring’s signed area with the shoelace formula and reversing the point order when the sign is wrong — exactly what this page walks through. On this site you don’t need any of that: paste the polygon into the inspector and click the one-click “rewind” fix next to the winding-order warning.