¿Cómo saber si una ruta GeoJSON se cruza con otra característica en Leaflet?

8

Tengo una aplicación donde el usuario dibuja una ruta (una serie de líneas rectas conectadas) y esta ruta no puede intersecar ninguna característica en una capa GeoJSON en particular.

Necesito verificar que ningún punto a lo largo de estas líneas se cruza con la capa GeoJSON, no solo los puntos finales.

¿Cómo puedo realizar esta verificación?

Lava Caliente
fuente
Podría ser factible
ghybs
¿Algo en particular que debería mirar en Turf.js?
LavaHot
No creo que turf.js haga esto. Es posible que pueda adaptar algún otro código de detección de intersección para sus propósitos. Por ejemplo, esto , que está diseñado para operar en cadenas de líneas GeoJSON, podría ayudarlo a obtener la mayor parte del camino, pero si necesita que funcione con capas de polígonos, deberá adaptarlo para aceptar la entrada de polígonos o extraer los polígonos como cadenas de líneas de su capa GeoJSON primero.
nathansnider
2
¡Impresionante trabajo! :-) ¿Pensé que turf.intersect haría el trabajo? (basándose en su jsfiddle: fiddle.jshell.net/tyt4oeux/1 ) Pero tal vez pasé por alto la pregunta.
ghybs
Ah-ha, pero por supuesto que funciona! Acabo de tomar los documentos de API en su palabra que turf.intersect necesitaba un polígono como entrada. Supongo que nunca está de más intentarlo. Dado que turf.intersect tiene la ventaja de ser más simple y detectar cuándo una línea está completamente dentro de un polígono, creo que ese es claramente el camino a seguir aquí.
nathansnider

Respuestas:

4

Puede probar la biblioteca Turf y un método como intersect: http://turfjs.org/docs/#intersect

Aquí está el ejemplo de código de esa biblioteca:

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
fuente
Debería agregar un ejemplo de cómo hacer esto: los enlaces se pudren con el tiempo.
alphabetasoup
Si ese enlace se pudre con el tiempo, entonces toda mi respuesta se anulará. Todo el ejemplo se basa en la existencia de la biblioteca Turf, y si esa no estará allí ... Sin embargo, copié ese ejemplo en mi respuesta.
Adrian Ber
44
El enlace se pudrió, aquí está el nuevo turfjs.org/docs/#intersect
Calvein
Enlace podrido de nuevo (o error); sin barra, solo: turfjs.org/docs#intersect
Hendy
1

EDITAR: Vea el violín de ghybs del comentario anterior para obtener una solución más simple y mejor usando turf.js. La respuesta original sigue:


Aquí hay una versión modificada de la rutina de intersección de la biblioteca geojson-js-utils que toma las cadenas de líneas GeoJSON como entrada y produce puntos GeoJSON de su intersección como salida:

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

Las modificaciones fueron necesarias porque la función original era calcular las intersecciones solo de latitud y longitud, como si fueran solo coordenadas en un plano, produciendo resultados inexactos (especialmente en latitudes altas o largas distancias). El uso L.Projectionde convertir a un sistema de coordenadas proyectadas conforme (o, en este caso, casi conforme ) durante el cálculo corrige esto.

Se podría modificar aún más para aceptar objetos de geometría de folleto en lugar de solo LineStrings, pero en su lugar utilicé esta función bastante difícil de manejar para crear LineStrings para pasar a la función de intersección:

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

y esta función para tomar objetos Leaflet, convertirlos en LineStrings y verificar intersecciones:

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

Aquí hay un ejemplo de violín que usa esto con Leaflet.draw:

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

Cuando termine de dibujar un objeto, colocará marcadores en el mapa en los puntos donde el objeto dibujado se cruza con la geometría base. No puede verificar las intersecciones mientras todavía se está dibujando un camino, porque Leaflet.draw no nos da ningún controlador de eventos para usar mientras el dibujo aún está en progreso. Sin embargo, se comprobará tan pronto como se complete un evento de sorteo.

También tenga en cuenta que esto no detectará las intersecciones para las rutas que se encuentran completamente dentro de los polígonos con los que se están comprobando. Podrías hacer esas comprobaciones usando turf.js (probablemente combinando turf.explode con turf.within ).

nathansnider
fuente