Search code examples
leafletgeojson

Deduplicate polygon state/country shared boundaries


I am using GEOJSON to draw a world map with leaflet and having dashed line to draw boundaries, like in below image.

enter image description here

The problem I am having is that the line is not shared by two states if two states share boundaries, rather two lines are drawn each for different state. Due to this when zoomed enough lines looks weird as they get overlapped on each other. As show below. enter image description here

var GeoJsonLayer = L.geoJson();
var myStyle = {
                "color": "#fff",
                "weight": 0.8,
                "opacity": 1,
                "fillOpacity": "1",
                "fillColor": "#243E54"
            };
// here data.world contains GEOJson to draw whole map
            if (data.world) {
                GeoJsonLayer.options.className = "geojsonlayerpolygon";
                GeoJsonLayer.on("dblclick", function (ev) {
                    map.fireEvent("dblclick", ev);
                });
                GeoJsonLayer.addData(data.world);
                GeoJsonLayer.setStyle(myStyle);
            }

For making it dashed line i am using below CSS

.geojsonlayerpolygon{
   stroke-dasharray: 6 4;
 }

GEOJson I am using is https://jsonblob.com/826f1a94-c1a3-11e9-a004-354d7c27cab2

How can i make sure that boundaries, when shared, have only one line?


Solution

  • Sounds like a job for TopoJSON. From its readme file:

    TopoJSON is an extension of GeoJSON that encodes topology. Rather than representing geometries discretely, geometries in TopoJSON files are stitched together from shared line segments called arcs. This technique is similar to Matt Bloch’s MapShaper and the Arc/Info Export format, .e00.

    TopoJSON eliminates redundancy, allowing related geometries to be stored efficiently in the same file. For example, the shared boundary between California and Nevada is represented only once, rather than being duplicated for both states. A single TopoJSON file can contain multiple feature collections without duplication, such as states and counties. Or, a TopoJSON file can efficiently represent both polygons (for fill) and boundaries (for stroke) as two feature collections that share the same arc mesh.

    So, start by loading the topojson library...

    <script src="https://unpkg.com/topojson@3"></script>
    

    ...then create a topology object for your GeoJSON data; note that TopoJSON expects an array of GeoJSON features, e.g. I'll load a GeoJSON FeatureCollection containing the Natural Earth country boundaries via fetch and create a topology:

    fetch("ne_110m_admin_0_countries.geojson")
    .then(res=>res.json())
    .then(json=>{
    
          var topo = topojson.topology([json]);
    
          /* ...more stuff here soon */
    
    });
    

    ...then add a L.GeoJSON layer with just the polygon fill, setting the stroke option to avoid drawing any lines on the contour of the polygons (remember that path styling options can be passed to GeoJSON constructors) ...

    var layerFill = L.geoJson(json, { stroke: false }).addTo(map);
    

    ...calculate the mesh of the topology, which will be a GeoJSON MultiLineString...

    topojson.mesh(topo);
    

    ...and create another L.GeoJSON instance to draw said MultiLineString. Style the lines as you like (including dashArray), e.g. ...

    var layerLines = L.geoJson(topojson.mesh(topo), { 
      fill: false, dashArray:[3, 6], weight: 2 
    }).addTo(map);
    

    The end result, which you can see here as a working example, contains no overlapping dashed lines, as expected:

    Screenshot of country boundaries topology


    Using TopoJSON is one possible approach. There are other possible approaches (e.g. pre-generating a MultiLineString GeoJSON file with just the boundaries, using a different tool, etc); but the idea of using topological rules on the dataset would be the same.