Search code examples
leafletstylespolygonoverlap

Leaflet - Change and revert polygon feature style


I make a little app using leaflet to display information about polygons. Here's how the code works :

  • A crosshair is displayed in the center of the screen on top of the map
  • When map pans, crosshair remains in the center on the screen
  • When crosshair overlaps a polygon, polygon information is displyed in a div
  • the purpose of this system is to help polygon selecting on mobile for very small polygons

I came up with the following app : https://www.laurentgontier.com/Crosshair/

I need to be able to change polygon style when it is overlaped by crosshair and revert to default style when crosshair leaves polygon.

so far, I wasn't able to achieve this.

Here's the part of code :

//Geojson layer display     
var geojsonLayer = new L.GeoJSON.AJAX("map.geojson",{       
onEachFeature: onEachFeature
});       
geojsonLayer.addTo(map);

//Crosshair display
var crosshairIcon = L.icon({
    iconUrl: 'Cible.png',
    iconSize:     [30, 30], // size of the icon
    iconAnchor:   [15, 15], // point of the icon which will correspond to marker's location
});
crosshair = new L.marker(map.getCenter(), {icon: crosshairIcon, clickable:false});
crosshair.addTo(map);

// Move the crosshair to the center of the map when the user pans
map.on('move', function(e) {
crosshair.setLatLng(map.getCenter());
});

// Move the crosshair to the center of the map when the user pans
map.on('moveend', function(e) {
var hasHit = false;

var newCenter = map.getCenter();

geoJsonLayers.forEach(function(layer){
var currLayerFeature = layer.feature;
var layerPlace = currLayerFeature.properties.Place;

var layerCoordinates = currLayerFeature.geometry.coordinates[0]

var xp = [], yp =[];
for(var i = 0; i<layerCoordinates.length; i++){
xp.push(layerCoordinates[i][0]); yp.push(layerCoordinates[i][1]);
}

if(checkPointForHit(xp, yp , newCenter.lng, newCenter.lat)){
displayPlace.innerHTML = layerPlace; hasHit = true}

})
if(!hasHit)displayPlace.innerHTML = 'Not overlaping polygon'
});


function checkPointForHit(xp/*array of xpointsOfPolygon*/, yp /*array of ypointsOfPolygon*/, x, y) { 
var i, j, c = 0, npol = xp.length; 
for (i = 0, j = npol-1; i < npol; j = i++) { 
if ((((yp[i] <= y) && (y < yp[j])) || 
((yp[j] <= y) && (y < yp[i]))) && 
(x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i])) { 
c =!c; 
} 
} 
return c; 
} 

Any idea about that ?


Solution

  • Use the turfjs library and then use booleanContains:

    map.on('moveend', function(e) {
        var hasHit = false;
    
        geojsonLayer.resetStyle()
    
        geoJsonLayers.forEach(function(layer){
            var layerPlace = layer.feature.properties.Place;
    
            if(turf.booleanContains(layer.toGeoJSON(),crosshair.toGeoJSON())){
                displayPlace.innerHTML = layerPlace; 
                hasHit = true;
                layer.setStyle({color: 'red'});
            }
    
        })
        if(!hasHit){
            displayPlace.innerHTML = 'Not overlaping polygon';
        }
    });
    

    When you change the event from moveend to move the color is updated while moveing

    Add DOM Script

    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = 'https://cdn.jsdelivr.net/npm/@turf/turf@5.1.6/turf.min.js';    
    
    document.getElementsByTagName('head')[0].appendChild(script);
    
    
    map.on('move moveend', function(e) {
        var hasHit = false;
    
        var newCenter = map.getCenter();
        geojsonLayer.resetStyle()
    
        geoJsonLayers.forEach(function(layer){
            var layerPlace = layer.feature.properties.Place;
    
    
            if(turf.booleanContains(layer.toGeoJSON(),crosshair.toGeoJSON())){
                displayPlace.innerHTML = layerPlace; 
                hasHit = true;
                layer.setStyle({color: 'red'});
            }
    
        })
        if(!hasHit){
            displayPlace.innerHTML = 'Not overlaping polygon';
        }
    });