Wie kann man feststellen, ob sich ein GeoJSON-Pfad mit einer anderen Funktion in der Broschüre schneidet?

8

Ich habe eine Anwendung, in der der Benutzer einen Pfad (eine Reihe verbundener gerader Linien) zeichnet und dieser Pfad möglicherweise kein Merkmal in einer bestimmten GeoJSON-Ebene schneidet.

Ich muss überprüfen, ob kein Punkt entlang dieser Linien die GeoJSON-Ebene schneidet, nicht nur die Endpunkte.

Wie kann ich diese Prüfung durchführen?

LavaHot
quelle
Es könnte mit Turf.js
Ghybs
Was sollte ich mir in Turf.js besonders ansehen?
LavaHot
Ich glaube nicht, dass turf.js das tut. Möglicherweise können Sie einen anderen Kreuzungserkennungscode für Ihre Zwecke anpassen. Zum Beispiel diese , die auf GeoJSON Linienfolgen für den Betrieb konzipiert ist, können Sie die meisten der Art und Weise, aber wenn Sie es an die Arbeit mit Polygon - Layer benötigen, dann würden Sie müssen entweder anpassen Polygoneingabe oder extrahieren Sie die Polygone als akzeptieren , Linestrings von Ihrer GeoJSON-Ebene zuerst.
Nathansnider
2
Wow beeindruckende Arbeit! :-) Ich hätte gedacht, turf.intersect würde den Job machen? (Aufbauend auf Ihrer jsfiddle: fiddle.jshell.net/tyt4oeux/1 ) Aber vielleicht habe ich die Frage übersehen.
Ghybs
Ah-ha, aber das funktioniert natürlich! Ich habe gerade die API-Dokumente beim Wort genommen, dass turf.intersect ein Polygon als Eingabe benötigt. Es tut nie weh, es zu versuchen, denke ich. Da turf.intersect den Vorteil hat, einfacher zu sein und zu erkennen, wann sich eine Linie vollständig innerhalb eines Polygons befindet, ist dies meiner Meinung nach der richtige Weg.
Nathansnider

Antworten:

4

Sie können die Turf-Bibliothek und eine Methode wie intersect ausprobieren: http://turfjs.org/docs/#intersect

Hier ist das Codebeispiel aus dieser Bibliothek:

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
quelle
Sie sollten ein Beispiel dafür hinzufügen: Links verrotten im Laufe der Zeit.
Alphabetasoup
Wenn dieser Link im Laufe der Zeit verrottet, wird meine gesamte Antwort ungültig. Das gesamte Beispiel basiert auf der Existenz einer Turf-Bibliothek, und wenn diese nicht vorhanden sein wird ... Ich habe dieses Beispiel jedoch in meine Antwort kopiert.
Adrian Ber
4
Der Link ist verrottet, hier ist der neue turfjs.org/docs/#intersect
Calvein
Link erneut verrotten (oder Fehler); Kein Schrägstrich, nur: turfjs.org/docs#intersect
Hendy
1

BEARBEITEN : Eine einfachere und bessere Lösung mit turf.js finden Sie in Ghybs 'Geige aus dem obigen Kommentar. Die ursprüngliche Antwort folgt:


Hier ist eine modifizierte Version der Schnittroutine aus der Bibliothek geojson-js-utils , die GeoJSON-Linestrings als Eingabe verwendet und GeoJSON-Punkte ihrer Schnittmenge als Ausgabe erzeugt:

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;
}

Die Änderungen waren notwendig, da die ursprüngliche Funktion darin bestand, Schnittpunkte nur aus Breiten- und Längengraden zu berechnen, als wären sie nur Koordinaten in einer Ebene, was zu ungenauen Ergebnissen führte (insbesondere bei hohen Breiten oder über große Entfernungen). Die L.ProjectionKonvertierung in ein konformes (oder in diesem Fall nahezu konformes ) projiziertes Koordinatensystem während der Berechnung behebt dies.

Man könnte es weiter modifizieren, um Leaflet-Geometrieobjekte anstelle von nur LineStrings zu akzeptieren, aber stattdessen habe ich diese ziemlich unhandliche Funktion verwendet, um LineStrings zu erstellen, die an die Schnittpunktfunktion übergeben werden sollen:

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;
}

und diese Funktion, um Leaflet-Objekte zu nehmen, sie in LineStrings zu konvertieren und nach Schnittpunkten zu suchen:

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;
}

Hier ist ein Beispiel für eine Geige, die dies mit Leaflet.draw verwendet:

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

Wenn Sie mit dem Zeichnen eines Objekts fertig sind, werden Markierungen auf der Karte an den Punkten platziert, an denen sich das gezeichnete Objekt mit der Basisgeometrie schneidet. Es kann nicht nach Schnittpunkten suchen, während ein Pfad noch gezeichnet wird, da Leaflet.draw uns keine Ereignishandler zur Verfügung stellt, die verwendet werden können, während die Zeichnung noch ausgeführt wird. Es wird überprüft, sobald ein Ziehungsereignis abgeschlossen ist.

Beachten Sie auch, dass dadurch keine Schnittpunkte für Pfade erkannt werden, die vollständig innerhalb der Polygone liegen, gegen die sie geprüft werden. Sie können diese Überprüfungen mit turf.js durchführen (wahrscheinlich kombinieren Sie turf.explode mit turf.within ).

nathansnider
quelle