A Leaflet plugin that makes vector layers render correctly and continuously across the antimeridian (the ±180° longitude line).
Leaflet's tile layer already repeats the world seamlessly when you pan left or right. Vector layers do not. A route crossing the Pacific disappears at ±180°, or draws a line straight across the entire map in the wrong direction.
Other approaches split the feature into two pieces at the antimeridian. This breaks the geometry, the popup, any downstream GIS processing, and looks wrong at the seam.
Leaflet.WorldWrap never splits anything.
- Coordinates are normalised so consecutive vertices never jump more than 180° of longitude, eliminating the "wrong-way" line across the map.
- A shadow clone is maintained for every tracked layer in each world copy that is currently visible on screen (plus a one-world buffer on each side so clones exist before they scroll into view).
- As you pan, stale shadows are removed and new ones are created in
real time on Leaflet's
moveevent — not just after panning stops — so there is no visible pop-in during fast panning.
Open demo.html in a browser. Pan east or west, zoom in and out, and watch
the features appear seamlessly in every world copy.
Script tag:
<script src="leaflet.worldwrap.js"></script>Pass worldWrap: true when creating your map. Every layer you add after that
is handled automatically — no changes to your layer code required.
var map = L.map('map', {
worldWrap: true,
center: [10, 180],
zoom: 3
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
// These all just work — no special handling needed:
L.polyline([[35, 139], [40, -150], [37, -122]]).addTo(map);
L.polygon([[-12, 176], [-12, -179], [-22, -179], [-22, 176]]).addTo(map);
L.circle([0, 180], { radius: 800000 }).addTo(map);
L.marker([21, -157]).addTo(map);
L.geoJSON(myGeoJsonData).addTo(map);| Option | Type | Default | Description |
|---|---|---|---|
worldWrap |
boolean |
false |
Enable world-wrapping for this map. |
| Type | Notes |
|---|---|
L.Marker |
Shadow markers update in real time during drag. |
L.Circle |
Shadow placed at shifted centre; radius unchanged. |
L.CircleMarker |
Pixel-radius circle; only centre is shifted. |
L.Polyline |
Coordinates normalised to prevent wrong-way lines. |
L.Polygon |
Includes L.Rectangle. Rings are each normalised. |
L.GeoJSON |
Each child feature is tracked individually. |
L.FeatureGroup |
Each child layer is tracked individually. |
L.LayerGroup |
Recurses into children; watches for future adds/removes. |
The map viewport spans some range of longitude, e.g. 90°W to 270°E when looking at the Pacific. Leaflet divides the longitude line into 360°-wide strips called world copies. World 0 is −180° to +180°. World −1 is −540° to −180°. World +1 is +180° to +540°, and so on.
// World index from a longitude:
Math.floor((lng + 180) / 360)On every move event the plugin computes which world indices are visible,
adds a shadow shifted by k × 360° for each visible world k ≠ 0, and
removes any shadow for a world that is no longer visible.
-
Editing shadows: Shadow copies are independent Leaflet layers. Editing one with Leaflet.draw or Leaflet.transform will not propagate back to the original or to other shadows.
-
Custom layer types: Only the built-in Leaflet vector types listed above are cloned. Custom layer subclasses that do not extend one of those types will be ignored.
-
Performance with large layer counts: Each tracked layer gets one full Leaflet clone per visible world copy (typically 2–4 at normal zoom levels). For small to medium datasets this is negligible. At large scales — thousands of features, or a dense GeoJSON layer — the overhead of maintaining and rendering that many extra SVG/Canvas elements can become noticeable. If you hit performance problems, consider clustering small features with Leaflet.markercluster, simplifying geometry before adding it to the map, or only adding the plugin to maps where the antimeridian region is actually relevant.
Tested against Leaflet 1.9.x. Should work with any Leaflet 1.x build. No external dependencies beyond Leaflet itself.
ES5-compatible; no transpilation required.
Bug reports and pull requests are welcome on GitHub.
MIT © Eric Dalnas