Comment savoir si un chemin GeoJSON croise une autre entité dans Leaflet?

8

J'ai une application où l'utilisateur dessine un chemin (une série de lignes droites connectées) et ce chemin ne peut croiser aucune entité dans une couche GeoJSON particulière.

Je dois vérifier qu'aucun point le long de ces lignes ne coupe la couche GeoJSON, pas seulement les extrémités.

Comment puis-je effectuer cette vérification?

LavaHot
la source
Cela pourrait être faisable
ghybs
Quelque chose en particulier que je devrais regarder dans Turf.js?
LavaHot
Je ne pense pas que turf.js le fasse. Vous pourrez peut-être adapter un autre code de détection d'intersection à vos besoins. Par exemple, ceci , qui est conçu pour fonctionner sur les chaînes de lignes GeoJSON, pourrait vous aider la plupart du temps, mais si vous en avez besoin pour travailler avec des couches de polygones, vous devrez soit l'adapter pour accepter l'entrée de polygone, soit extraire les polygones comme les lignes de ligne de votre couche GeoJSON en premier.
nathansnider
2
Wow travail impressionnant! :-) J'aurais pensé que turf.intersect ferait l'affaire? (en s'appuyant sur votre jsfiddle: fiddle.jshell.net/tyt4oeux/1 ) Mais j'ai peut-être oublié la question.
ghybs
Ah-ha, mais bien sûr ça marche! Je viens de prendre les docs API au mot que turf.intersect avait besoin d'un polygone en entrée. Cela ne fait jamais de mal d'essayer, je suppose. Étant donné que turf.intersect a l'avantage d'être plus simple et de détecter quand une ligne est entièrement dans un polygone, c'est clairement la voie à suivre ici, je pense.
nathansnider

Réponses:

4

Vous pouvez essayer la bibliothèque Turf et une méthode comme intersect: http://turfjs.org/docs/#intersect

Voici l'exemple de code de cette bibliothèque:

var poly1 = {
    "type": "Feature",
    "geometry": {
        "type": "Polygon",
        "coordinates": [[
            [-122.801742, 45.48565],
            [-122.801742, 45.60491],
            [-122.584762, 45.60491],
            [-122.584762, 45.48565],
            [-122.801742, 45.48565]
        ]]
    }
}
var poly2 = {
    "type": "Feature",
    "geometry": {
        "type": "Polygon",
        "coordinates": [[
            [-122.520217, 45.535693],
            [-122.64038, 45.553967],
            [-122.720031, 45.526554],
            [-122.669906, 45.507309],
            [-122.723464, 45.446643],
            [-122.532577, 45.408574],
            [-122.487258, 45.477466],
            [-122.520217, 45.535693]
         ]]
     }
}

var intersection = turf.intersect(poly1, poly2);
Adrian Ber
la source
Vous devez ajouter un exemple de la façon de procéder: les liens pourrissent avec le temps.
alphabetasoup
Si ce lien pourrit avec le temps, ma réponse entière sera annulée. L'exemple entier est basé sur l'existence de la bibliothèque Turf, et si celle-ci ne sera pas là ... Cependant j'ai copié cet exemple dans ma réponse.
Adrian Ber
4
Le lien pourri, voici le nouveau turfjs.org/docs/#intersect
Calvein
Lien pourri à nouveau (ou erreur); pas de slash, juste: turfjs.org/docs#intersect
Hendy
1

EDIT: Voir le violon de ghybs dans le commentaire ci-dessus pour une solution plus simple et meilleure en utilisant turf.js. La réponse originale suit:


Voici une version modifiée de la routine d'intersection de la bibliothèque geojson-js-utils qui prend les lignes de ligne GeoJSON en entrée et produit les points GeoJSON de leur intersection en sortie:

function lineStringsIntersect(l1, l2) {
    var intersects = [];
    for (var i = 0; i <= l1.coordinates.length - 2; ++i) {
        for (var j = 0; j <= l2.coordinates.length - 2; ++j) {
            var a1Latlon = L.latLng(l1.coordinates[i][1], l1.coordinates[i][0]),
                a2Latlon = L.latLng(l1.coordinates[i + 1][1], l1.coordinates[i + 1][0]),
                b1Latlon = L.latLng(l2.coordinates[j][1], l2.coordinates[j][0]),
                b2Latlon = L.latLng(l2.coordinates[j + 1][1], l2.coordinates[j + 1][0]),
                a1 = L.Projection.SphericalMercator.project(a1Latlon),
                a2 = L.Projection.SphericalMercator.project(a2Latlon),
                b1 = L.Projection.SphericalMercator.project(b1Latlon),
                b2 = L.Projection.SphericalMercator.project(b2Latlon),
                ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
                ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
                u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
            if (u_b != 0) {
                var ua = ua_t / u_b,
                    ub = ub_t / u_b;
                if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
                    var pt_x = a1.x + ua * (a2.x - a1.x),
                        pt_y = a1.y + ua * (a2.y - a1.y),
                        pt_xy = {"x": pt_x, "y": pt_y},
                        pt_latlon = L.Projection.SphericalMercator.unproject(pt_xy);
                    intersects.push({
                        'type': 'Point',
                            'coordinates': [pt_latlon.lng, pt_latlon.lat]
                    });
                }
            }
        }
    }
    if (intersects.length == 0) intersects = false;
    return intersects;
}

Les modifications étaient nécessaires parce que la fonction d'origine calculait les intersections à partir de la latitude et de la longitude seules, comme si elles n'étaient que des coordonnées sur un plan, produisant des résultats inexacts (en particulier à des latitudes élevées ou sur de longues distances). L'utilisation L.Projectionde la conversion en système de coordonnées projeté conforme (ou, dans ce cas, presque conforme ) pendant le calcul corrige ce problème.

On pourrait le modifier davantage pour accepter les objets de géométrie Leaflet au lieu de simplement LineStrings, mais à la place, j'ai utilisé cette fonction plutôt lourde pour créer des LineStrings à passer à la fonction d'intersection:

function lineify(inputGeom) {
    var outputLines = {
        "type": "GeometryCollection",
            "geometries": []
    }
    switch (inputGeom.type) {
        case "GeometryCollection":
            for (var i in inputGeom.geometries) {
                var geomLines = lineify(inputGeom.geometries[i]);
                if (geomLines) {
                    for (var j in geomLines.geometries) {
                        outputLines.geometries.push(geomLines.geometries[j]);
                    }
                } else {
                    outputLines = false;
                }
            }
            break;
        case "Feature":
            var geomLines = lineify(inputGeom.geometry);
            if (geomLines) {
                for (var j in geomLines.geometries) {
                    outputLines.geometries.push(geomLines.geometries[j]);
                }
            } else {
                outputLines = false;
            }
            break;
        case "FeatureCollection":
            for (var i in inputGeom.features) {
                var geomLines = lineify(inputGeom.features[i].geometry);
                if (geomLines) {
                    for (var j in geomLines.geometries) {
                        outputLines.geometries.push(geomLines.geometries[j]);
                    }
                } else {
                    outputLines = false;
                }
            }
            break;
        case "LineString":
            outputLines.geometries.push(inputGeom);
            break;
        case "MultiLineString":
        case "Polygon":
            for (var i in inputGeom.coordinates) {
                outputLines.geometries.push({
                    "type": "LineString",
                        "coordinates": inputGeom.coordinates[i]
                });
            }
            break;
        case "MultiPolygon":
            for (var i in inputGeom.coordinates) {
                for (var j in inputGeom.coordinates[i]) {
                    outputLines.geometries.push({
                        "type": "LineString",
                            "coordinates": inputGeom.coordinates[i][j]
                    });
                }
            }
            break;
        default:
            outputLines = false;
    }
    return outputLines;
}

et cette fonction pour prendre des objets Leaflet, les convertir en LineStrings et vérifier les intersections:

function crossCheck(baseLayer, drawLayer) {
    var baseJson = baseLayer.toGeoJSON(),
        drawJson = drawLayer.toGeoJSON(),
        baseLines = lineify(baseJson),
        drawLines = lineify(drawJson),
        crossPoints = {
            type: "GeometryCollection",
            geometries: []
        };
    if (baseLines && drawLines) {
        for (var i in drawLines.geometries) {
            for (var j in baseLines.geometries) {
                var crossTest = lineStringsIntersect(drawLines.geometries[i], baseLines.geometries[j]);
                if (crossTest) {
                    for (var k in crossTest) {
                        crossPoints.geometries.push(crossTest[k]);
                    }
                }
            }
        }
    }
    return crossPoints;
}

Voici un exemple de violon utilisant ceci avec Leaflet.draw:

http://fiddle.jshell.net/nathansnider/egzxw86h/

Lorsque vous avez terminé de dessiner un objet, il place des marqueurs sur la carte aux points où l'objet dessiné croise la géométrie de base. Il ne peut pas vérifier les intersections alors qu'un chemin est toujours en cours de dessin, car Leaflet.draw ne nous donne aucun gestionnaire d'événements à utiliser pendant que le dessin est en cours. Il sera vérifié dès qu'un tirage au sort sera terminé.

Notez également que cela ne détectera pas les intersections pour les chemins qui se trouvent entièrement dans les polygones par rapport auxquels ils sont vérifiés. Vous pouvez faire ces vérifications en utilisant turf.js (en combinant probablement turf.explode avec turf.within ).

nathansnider
la source