J King
J King

Reputation: 4434

leaflet js fill color error when hole overlaps boundary of polygon

I have a polygon defined in a leaflet js map (over a bing map).
This polygon has two holes (yellow polygons) cut out of it. The SECOND hole polygon overlaps the outer boundary of the primary polygon.

enter image description here

PROBLEM:

The part of the SECOND polygon that extends past the outer edge of the primary polygon gets the fill color of the primary polygon.

IN this case, the primary polygon is red and the whole being removed is yellow

I have circled the issue in the attached image.

The following is a fiddle where I have recreated the issue.

QUESTION

Can someone help me figure out how to remove the part of the second yellow shape from the red polygon without this overlap color issue?

For clarity, here is all the code from the fiddle:

// Create the map
var map = L.map('map').setView([53.631611, -113.323975], 9);

// Set up the OSM layer
L.tileLayer(
    'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 
    {maxZoom: 18}).addTo(map);

//Create Polygon Orange
var polyOrange = new L.Polygon([
            [[53.569268602609704,-113.71411800384523], [53.51214299292513,-113.71411800384523], [53.51214299292513,-113.68802547454835], [53.403391858715274,-113.6907720565796], [53.40461992848445,-113.64957332611085], [53.3960226956682,-113.64614009857179], [53.39684155458476,-113.34332942962648], [53.4471711023092,-113.35744857788087], [53.4471711023092,-113.24483871459962], [53.57212285981298,-113.24445247650148], [53.58149977709897,-113.29389095306398], [53.58435320870081,-113.36118221282959], [53.62102302920731,-113.3447027206421], [53.646672964306994,-113.36942195892335], [53.64952199454264,-113.57116699218751], [53.669866612978275,-113.6345958709717], [53.65603334084723,-113.70188713073732], [53.57620003591595,-113.68678092956544]] 

], {color:'orange', opacity:1, fillOpacity:0.05});

//Create polygon YELLOW
var polyYellow = new L.Polygon([
    [ 
        [[53.716006, -113.252565],[53.715496, -113.228546],[53.717052, -113.215209],[53.734927, -113.197527],[53.753505, -113.172980],[53.768016, -113.168688],[53.774205, -113.157874],[53.774284, -113.148883],[53.715867, -113.149098],[53.715934, -113.172036],[53.686769, -113.172207],[53.686935, -113.196896],[53.367132, -113.197204],[53.366818, -113.221196],[53.337761, -113.220792],[53.337882, -113.801601],[53.367033, -113.807995],[53.367197, -113.836702],[53.716273, -113.836434]], 
        [[53.569268602609704,-113.71411800384523], [53.51214299292513,-113.71411800384523], [53.51214299292513,-113.68802547454835], [53.403391858715274,-113.6907720565796], [53.40461992848445,-113.64957332611085], [53.3960226956682,-113.64614009857179], [53.39684155458476,-113.34332942962648], [53.4471711023092,-113.35744857788087], [53.4471711023092,-113.24483871459962], [53.57212285981298,-113.24445247650148], [53.58149977709897,-113.29389095306398], [53.58435320870081,-113.36118221282959], [53.62102302920731,-113.3447027206421], [53.646672964306994,-113.36942195892335], [53.64952199454264,-113.57116699218751], [53.669866612978275,-113.6345958709717], [53.65603334084723,-113.70188713073732], [53.57620003591595,-113.68678092956544]] 
    ],
    [ 
        [[53.555985, -114.002024],[53.555804, -114.038727],[53.541397, -114.038512],[53.541372, -114.050958],[53.512009, -114.050958],[53.512111, -114.026410],[53.505169, -114.026410],[53.505373, -113.971564],[53.512034, -113.971650],[53.512034, -113.959076],[53.520338, -113.959179],[53.519726, -113.885364],[53.541153, -113.885364],[53.541101, -113.845993],[53.571438, -113.846851],[53.570012, -113.885364],[53.571082, -113.916435],[53.569349, -113.959007],[53.555451, -113.959383]]
    ]
], {color:'yellow', opacity:1, fillOpacity:0.05});

// CREATE POLYGONE RED
var polyRed = new L.Polygon([
    [ 
        [[53.773877,-114.001813],[53.774105,-113.998508],[53.774105,-113.917720],[53.803400,-113.917398],[53.803575,-113.661065],[53.812242,-113.660379],[53.818221,-113.652482],[53.818221,-113.620124],[53.803575,-113.620124],[53.803499,-113.347294],[53.818120,-113.347380],[53.818145,-113.345062],[53.830178,-113.334934],[53.835015,-113.335750],[53.842232,-113.324506],[53.840485,-113.321759],[53.845245,-113.314550],[53.845448,-113.309958],[53.847321,-113.309271],[53.847296,-113.305537],[53.845827,-113.306310],[53.845827,-113.304164],[53.844030,-113.306267],[53.842283,-113.310601],[53.825365,-113.310601],[53.825340,-113.322317],[53.803524,-113.322961],[53.803244,-113.124330],[53.774265,-113.124223],[53.715852,-113.124309],[53.715846,-113.098581],[53.571181,-113.098811],[53.366714,-113.098865],[53.366702,-113.074291],[53.279625,-113.074530],[53.279664,-113.098670],[53.279606,-113.220582],[53.250595,-113.220603],[53.250582,-113.488503],[53.250531,-113.499918],[53.235790,-113.499918],[53.235892,-113.561716],[53.250531,-113.561716],[53.250556,-113.598323],[53.257720,-113.598452],[53.257771,-113.610425],[53.265139,-113.610468],[53.265062,-113.927419],[53.366600,-113.927333],[53.366715,-114.001904]]
    ],
    [ 
        [[53.716006, -113.252565],[53.715496, -113.228546],[53.717052, -113.215209],[53.734927, -113.197527],[53.753505, -113.172980],[53.768016, -113.168688],[53.774205, -113.157874],[53.774284, -113.148883],[53.715867, -113.149098],[53.715934, -113.172036],[53.686769, -113.172207],[53.686935, -113.196896],[53.367132, -113.197204],[53.366818, -113.221196],[53.337761, -113.220792],[53.337882, -113.801601],[53.367033, -113.807995],[53.367197, -113.836702],[53.716273, -113.836434]],
        [[53.555985, -114.002024],[53.555804, -114.038727],[53.541397, -114.038512],[53.541372, -114.050958],[53.512009, -114.050958],[53.512111, -114.026410],[53.505169, -114.026410],[53.505373, -113.971564],[53.512034, -113.971650],[53.512034, -113.959076],[53.520338, -113.959179],[53.519726, -113.885364],[53.541153, -113.885364],[53.541101, -113.845993],[53.571438, -113.846851],[53.570012, -113.885364],[53.571082, -113.916435],[53.569349, -113.959007],[53.555451, -113.959383]]
    ]
], {color:'red', opacity:1, fillOpacity:0.03});

//Add polygons to map
polyRed.addTo(map); 
polyYellow.addTo(map); 
polyOrange.addTo(map);

and you just need thin on the html page:

and link to the leaflet.js and .css cdn

Upvotes: 2

Views: 1672

Answers (1)

ghybs
ghybs

Reputation: 53185

Your outer ring (i.e. first polygon array in your RED multipolygon definition) should bound your geometry, including the hole.

In your current definition, the 2nd hole (i.e. 2nd ring of the 2nd polygon in your RED multipolygon definition) extends more West than the outer ring (some coordinates have lower longitude than the lowest one of the outer ring).

This inconsistency makes the rendering struggle in correctly representing your multipolygon with holes, resulting in a part of your hole being painted with fill color.

A "simple" solution is to manually modify your outer ring in order to include entirely your hole:

// Create the map
var map = L.map('map').setView([53.54, -113.9], 10);

// Set up the OSM layer
L.tileLayer(
  'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 18
  }).addTo(map);

//Create polygon YELLOW
var polyYellow = new L.Polygon([
  [
    [
      [53.716006, -113.252565],
      [53.715496, -113.228546],
      [53.717052, -113.215209],
      [53.734927, -113.197527],
      [53.753505, -113.172980],
      [53.768016, -113.168688],
      [53.774205, -113.157874],
      [53.774284, -113.148883],
      [53.715867, -113.149098],
      [53.715934, -113.172036],
      [53.686769, -113.172207],
      [53.686935, -113.196896],
      [53.367132, -113.197204],
      [53.366818, -113.221196],
      [53.337761, -113.220792],
      [53.337882, -113.801601],
      [53.367033, -113.807995],
      [53.367197, -113.836702],
      [53.716273, -113.836434]
    ],
    [
      [53.569268602609704, -113.71411800384523],
      [53.51214299292513, -113.71411800384523],
      [53.51214299292513, -113.68802547454835],
      [53.403391858715274, -113.6907720565796],
      [53.40461992848445, -113.64957332611085],
      [53.3960226956682, -113.64614009857179],
      [53.39684155458476, -113.34332942962648],
      [53.4471711023092, -113.35744857788087],
      [53.4471711023092, -113.24483871459962],
      [53.57212285981298, -113.24445247650148],
      [53.58149977709897, -113.29389095306398],
      [53.58435320870081, -113.36118221282959],
      [53.62102302920731, -113.3447027206421],
      [53.646672964306994, -113.36942195892335],
      [53.64952199454264, -113.57116699218751],
      [53.669866612978275, -113.6345958709717],
      [53.65603334084723, -113.70188713073732],
      [53.57620003591595, -113.68678092956544]
    ]
  ],
  [
    [
      [53.555985, -114.002024],
      [53.555804, -114.038727],
      [53.541397, -114.038512],
      [53.541372, -114.050958],
      [53.512009, -114.050958],
      [53.512111, -114.026410],
      [53.505169, -114.026410],
      [53.505373, -113.971564],
      [53.512034, -113.971650],
      [53.512034, -113.959076],
      [53.520338, -113.959179],
      [53.519726, -113.885364],
      [53.541153, -113.885364],
      [53.541101, -113.845993],
      [53.571438, -113.846851],
      [53.570012, -113.885364],
      [53.571082, -113.916435],
      [53.569349, -113.959007],
      [53.555451, -113.959383]
    ]
  ]
], {
  color: 'yellow',
  opacity: 1,
  fillOpacity: 0.5
});

// CREATE POLYGONE RED
var polyRed = new L.Polygon([
  // First polygon.
  [
    // First ring = outer ring, must bound the surface.
    [
      [53.773877, -114.001813],
      [53.774105, -113.998508],
      [53.774105, -113.917720],
      [53.803400, -113.917398],
      [53.803575, -113.661065],
      [53.812242, -113.660379],
      [53.818221, -113.652482],
      [53.818221, -113.620124],
      [53.803575, -113.620124],
      [53.803499, -113.347294],
      [53.818120, -113.347380],
      [53.818145, -113.345062],
      [53.830178, -113.334934],
      [53.835015, -113.335750],
      [53.842232, -113.324506],
      [53.840485, -113.321759],
      [53.845245, -113.314550],
      [53.845448, -113.309958],
      [53.847321, -113.309271],
      [53.847296, -113.305537],
      [53.845827, -113.306310],
      [53.845827, -113.304164],
      [53.844030, -113.306267],
      [53.842283, -113.310601],
      [53.825365, -113.310601],
      [53.825340, -113.322317],
      [53.803524, -113.322961],
      [53.803244, -113.124330],
      [53.774265, -113.124223],
      [53.715852, -113.124309],
      [53.715846, -113.098581],
      [53.571181, -113.098811],
      [53.366714, -113.098865],
      [53.366702, -113.074291],
      [53.279625, -113.074530],
      [53.279664, -113.098670],
      [53.279606, -113.220582],
      [53.250595, -113.220603],
      [53.250582, -113.488503],
      [53.250531, -113.499918],
      [53.235790, -113.499918],
      [53.235892, -113.561716],
      [53.250531, -113.561716],
      [53.250556, -113.598323],
      [53.257720, -113.598452],
      [53.257771, -113.610425],
      [53.265139, -113.610468],
      [53.265062, -113.927419],
      [53.366600, -113.927333],
      [53.366715, -114.001904],
      // up to here, -114.001904 is the West-most longitude
      [53.505169, -114.001904], // Intersection point, manually built, but you can see a small gap.
      // Reversed order
      [53.505169, -114.026410], // More West than -114.001904
      [53.512111, -114.026410], // More West than -114.001904
      [53.512009, -114.050958], // More West than -114.001904
      [53.541372, -114.050958], // More West than -114.001904
      [53.541397, -114.038512], // More West than -114.001904
      [53.555804, -114.038727], // More West than -114.001904
      [53.555985, -114.002024], // More West than -114.001904
      [53.555985, -114.001813] // Intersection point, manually built
    ]
    // No other ring, therefore it should have no hole, but see 2nd polygon below.
  ],
  // Second polygon, incorrectly rendered by Leaflet as a hole.
  [
    // First ring = outer ring, but incorrectly rendered by Leaflet as 1st hole of 1st polygon.
    [
      [53.716006, -113.252565],
      [53.715496, -113.228546],
      [53.717052, -113.215209],
      [53.734927, -113.197527],
      [53.753505, -113.172980],
      [53.768016, -113.168688],
      [53.774205, -113.157874],
      [53.774284, -113.148883],
      [53.715867, -113.149098],
      [53.715934, -113.172036],
      [53.686769, -113.172207],
      [53.686935, -113.196896],
      [53.367132, -113.197204],
      [53.366818, -113.221196],
      [53.337761, -113.220792],
      [53.337882, -113.801601],
      [53.367033, -113.807995],
      [53.367197, -113.836702],
      [53.716273, -113.836434]
    ],
    // Second ring = hole.
    [
      [53.555985, -114.002024], // More West than -114.001904
      [53.555804, -114.038727], // More West than -114.001904
      [53.541397, -114.038512], // More West than -114.001904
      [53.541372, -114.050958], // More West than -114.001904
      [53.512009, -114.050958], // More West than -114.001904
      [53.512111, -114.026410], // More West than -114.001904
      [53.505169, -114.026410], // More West than -114.001904
      [53.505373, -113.971564],
      [53.512034, -113.971650],
      [53.512034, -113.959076],
      [53.520338, -113.959179],
      [53.519726, -113.885364],
      [53.541153, -113.885364],
      [53.541101, -113.845993],
      [53.571438, -113.846851],
      [53.570012, -113.885364],
      [53.571082, -113.916435],
      [53.569349, -113.959007],
      [53.555451, -113.959383]
    ]
  ]
], {
  color: 'red',
  opacity: 1,
  fillOpacity: 0.3
});

//Add polygons to map
polyRed.addTo(map);
polyYellow.addTo(map);
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-IkGU/uDhB9u9F8k+2OsA6XXoowIhOuQL1NTgNZHY1nkURnqEGlDZq3GsfmdJdKFe1k1zOc6YU2K7qY+hF9AodA==" crossorigin=""></script>

<div id="map" style="height: 200px"></div>

BTW, this highlight an inconsistency in Leaflet, that uses the 1st polygon of your multipolygon definition as an outer ring, and all rings of the 2nd polygon as holes of the 1st polygon. Whereas the docs example sticks to GeoJSON spec. This is probably related to Leaflet issue #3498.

A more robust solution would be to properly remove your holes from your outer ring, e.g. using Turf.js difference:

var outerRingLatLng = [
  [53.773877, -114.001813],
  [53.774105, -113.998508],
  [53.774105, -113.917720],
  [53.803400, -113.917398],
  [53.803575, -113.661065],
  [53.812242, -113.660379],
  [53.818221, -113.652482],
  [53.818221, -113.620124],
  [53.803575, -113.620124],
  [53.803499, -113.347294],
  [53.818120, -113.347380],
  [53.818145, -113.345062],
  [53.830178, -113.334934],
  [53.835015, -113.335750],
  [53.842232, -113.324506],
  [53.840485, -113.321759],
  [53.845245, -113.314550],
  [53.845448, -113.309958],
  [53.847321, -113.309271],
  [53.847296, -113.305537],
  [53.845827, -113.306310],
  [53.845827, -113.304164],
  [53.844030, -113.306267],
  [53.842283, -113.310601],
  [53.825365, -113.310601],
  [53.825340, -113.322317],
  [53.803524, -113.322961],
  [53.803244, -113.124330],
  [53.774265, -113.124223],
  [53.715852, -113.124309],
  [53.715846, -113.098581],
  [53.571181, -113.098811],
  [53.366714, -113.098865],
  [53.366702, -113.074291],
  [53.279625, -113.074530],
  [53.279664, -113.098670],
  [53.279606, -113.220582],
  [53.250595, -113.220603],
  [53.250582, -113.488503],
  [53.250531, -113.499918],
  [53.235790, -113.499918],
  [53.235892, -113.561716],
  [53.250531, -113.561716],
  [53.250556, -113.598323],
  [53.257720, -113.598452],
  [53.257771, -113.610425],
  [53.265139, -113.610468],
  [53.265062, -113.927419],
  [53.366600, -113.927333],
  [53.366715, -114.001904],
  [53.773877, -114.001813] // Finish by first position for GeoJSON compliancy.
];

var hole1LatLng = [
  [53.716006, -113.252565],
  [53.715496, -113.228546],
  [53.717052, -113.215209],
  [53.734927, -113.197527],
  [53.753505, -113.172980],
  [53.768016, -113.168688],
  [53.774205, -113.157874],
  [53.774284, -113.148883],
  [53.715867, -113.149098],
  [53.715934, -113.172036],
  [53.686769, -113.172207],
  [53.686935, -113.196896],
  [53.367132, -113.197204],
  [53.366818, -113.221196],
  [53.337761, -113.220792],
  [53.337882, -113.801601],
  [53.367033, -113.807995],
  [53.367197, -113.836702],
  [53.716273, -113.836434],
  [53.716006, -113.252565] // Finish by first position for GeoJSON compliancy.
];

var hole2LatLng = [
  [53.555985, -114.002024],
  [53.555804, -114.038727],
  [53.541397, -114.038512],
  [53.541372, -114.050958],
  [53.512009, -114.050958],
  [53.512111, -114.026410],
  [53.505169, -114.026410],
  [53.505373, -113.971564],
  [53.512034, -113.971650],
  [53.512034, -113.959076],
  [53.520338, -113.959179],
  [53.519726, -113.885364],
  [53.541153, -113.885364],
  [53.541101, -113.845993],
  [53.571438, -113.846851],
  [53.570012, -113.885364],
  [53.571082, -113.916435],
  [53.569349, -113.959007],
  [53.555451, -113.959383],
  [53.555985, -114.002024] // Finish by first position for GeoJSON compliancy.
];

var outerRing = turf.polygon([latLngArrayToLngLat(outerRingLatLng)]);
var holes = turf.multiPolygon([
  [latLngArrayToLngLat(hole1LatLng)],
  [latLngArrayToLngLat(hole2LatLng)]
]);
var result = turf.difference(outerRing, holes);
var resultGeoJSON = turf.getGeom(result);

var map = L.map('map');

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

var geojsonLayer = L.geoJSON(resultGeoJSON, {
  style: {
    color: 'red',
    opacity: 1,
    fillOpacity: 0.3
  }
}).addTo(map);
map.fitBounds(geojsonLayer.getBounds());


function latLngArrayToLngLat(latLngArray) {
  return latLngArray.map(latLngToLngLat);
}

function latLngToLngLat(latLng) {
  return [latLng[1], latLng[0]];
}

//Create polygon YELLOW
var polyYellow = new L.Polygon([
  [
    [
      [53.716006, -113.252565],
      [53.715496, -113.228546],
      [53.717052, -113.215209],
      [53.734927, -113.197527],
      [53.753505, -113.172980],
      [53.768016, -113.168688],
      [53.774205, -113.157874],
      [53.774284, -113.148883],
      [53.715867, -113.149098],
      [53.715934, -113.172036],
      [53.686769, -113.172207],
      [53.686935, -113.196896],
      [53.367132, -113.197204],
      [53.366818, -113.221196],
      [53.337761, -113.220792],
      [53.337882, -113.801601],
      [53.367033, -113.807995],
      [53.367197, -113.836702],
      [53.716273, -113.836434]
    ],
    [
      [53.569268602609704, -113.71411800384523],
      [53.51214299292513, -113.71411800384523],
      [53.51214299292513, -113.68802547454835],
      [53.403391858715274, -113.6907720565796],
      [53.40461992848445, -113.64957332611085],
      [53.3960226956682, -113.64614009857179],
      [53.39684155458476, -113.34332942962648],
      [53.4471711023092, -113.35744857788087],
      [53.4471711023092, -113.24483871459962],
      [53.57212285981298, -113.24445247650148],
      [53.58149977709897, -113.29389095306398],
      [53.58435320870081, -113.36118221282959],
      [53.62102302920731, -113.3447027206421],
      [53.646672964306994, -113.36942195892335],
      [53.64952199454264, -113.57116699218751],
      [53.669866612978275, -113.6345958709717],
      [53.65603334084723, -113.70188713073732],
      [53.57620003591595, -113.68678092956544]
    ]
  ],
  [
    [
      [53.555985, -114.002024],
      [53.555804, -114.038727],
      [53.541397, -114.038512],
      [53.541372, -114.050958],
      [53.512009, -114.050958],
      [53.512111, -114.026410],
      [53.505169, -114.026410],
      [53.505373, -113.971564],
      [53.512034, -113.971650],
      [53.512034, -113.959076],
      [53.520338, -113.959179],
      [53.519726, -113.885364],
      [53.541153, -113.885364],
      [53.541101, -113.845993],
      [53.571438, -113.846851],
      [53.570012, -113.885364],
      [53.571082, -113.916435],
      [53.569349, -113.959007],
      [53.555451, -113.959383]
    ]
  ]
], {
  color: 'yellow',
  opacity: 1,
  fillOpacity: 0.5
});

//Add polygons to map
polyYellow.addTo(map);
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-IkGU/uDhB9u9F8k+2OsA6XXoowIhOuQL1NTgNZHY1nkURnqEGlDZq3GsfmdJdKFe1k1zOc6YU2K7qY+hF9AodA==" crossorigin=""></script>

<script src="https://unpkg.com/@turf/[email protected]/turf.js"></script>

<div id="map" style="height: 200px"></div>

Of course you could simply save the result of this refactoring as your new definition for your RED polygon, which now has only 1 hole.

Upvotes: 2

Related Questions