Como saber se um caminho GeoJSON cruza com outro recurso no Leaflet?

8

Eu tenho um aplicativo em que o usuário desenha um caminho (uma série de linhas retas conectadas) e esse caminho pode não interceptar nenhum recurso em uma camada GeoJSON específica.

Preciso verificar se nenhum ponto ao longo dessas linhas cruza a camada GeoJSON, não apenas os pontos finais.

Como posso executar esta verificação?

LavaHot
fonte
Poderia ser capaz de fazer com Turf.js
ghybs
Alguma coisa em particular que eu deveria procurar no Turf.js?
LavaHot
Eu não acho que o turf.js faça isso. Você pode adaptar outro código de detecção de interseção para seus propósitos. Por exemplo, isso , projetado para operar em cadeias de linhas GeoJSON, pode ajudá-lo na maior parte do caminho, mas se você precisar trabalhar com camadas de polígonos, será necessário adaptá-lo para aceitar a entrada de polígonos ou extrair os polígonos como cadeias de linha da camada GeoJSON primeiro.
Nathansnider 23/11/2015
2
Uau trabalho impressionante! :-) Eu pensaria que o turf.intersect faria o trabalho? (baseando-se no seu jsfiddle: fiddle.jshell.net/tyt4oeux/1 ) Mas talvez eu tenha ignorado a questão.
ghybs
Ah-ha, mas é claro que funciona! Eu apenas peguei os documentos da API com a palavra que turf.intersect precisava de um polígono como entrada. Nunca é demais tentar, eu acho. Como o turf.intersect tem a vantagem de ser mais simples e de detectar quando uma linha está inteiramente dentro de um polígono, esse é claramente o caminho a seguir aqui, eu acho.
Nathansnider

Respostas:

4

Você pode tentar a biblioteca Turf e um método como intersect: http://turfjs.org/docs/#intersect

Aqui está o exemplo de código dessa 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
fonte
Você deve adicionar um exemplo de como fazer isso: os links apodrecem ao longo do tempo.
alphabetasoup
Se esse link apodrecer com o tempo, toda a minha resposta será anulada. O exemplo inteiro é baseado na existência da biblioteca Turf, e se essa não estiver lá ... No entanto, copiei esse exemplo para a minha resposta.
Adrian Ber
4
O link apodrecido, aqui está o novo turfjs.org/docs/#intersect
Calvein
Rot apodrecer novamente (ou erro); Não há barra, apenas: turfjs.org/docs#intersect
Hendy
1

EDIT: Veja o violino de ghybs no comentário acima para obter uma solução mais simples e melhor usando turf.js. A resposta original segue:


Aqui está uma versão modificada da rotina de interseção da biblioteca geojson-js-utils que recebe as cadeias de linhas GeoJSON como entrada e produz pontos de GeoJSON de sua interseção como saída:

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

As modificações foram necessárias porque a função original era calcular interseções apenas a partir da latitude e longitude, como se fossem apenas coordenadas em um plano, produzindo resultados imprecisos (especialmente em altas latitudes ou longas distâncias). Usar L.Projectionpara converter em um sistema de coordenadas projetado conforme (ou, neste caso, quase conforme ) durante o cálculo corrige isso.

Pode-se modificá-lo ainda mais para aceitar objetos de geometria Leaflet em vez de apenas LineStrings, mas usei essa função bastante pesada para criar LineStrings a serem passados ​​para a função de interseção:

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

e esta função para pegar objetos Leaflet, convertê-los em LineStrings e verificar interseções:

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

Aqui está um exemplo de violino usando isso com o Leaflet.draw:

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

Quando você terminar de desenhar um objeto, ele colocará marcadores no mapa nos pontos em que o objeto desenhado cruza com a geometria base. Ele não pode verificar interseções enquanto um caminho ainda está sendo desenhado, porque Leaflet.draw não nos fornece manipuladores de eventos para usar enquanto o desenho ainda está em andamento. Ele verificará assim que um evento de empate for concluído.

Observe também que isso não detectará interseções de caminhos que estejam inteiramente dentro de polígonos com os quais estão sendo verificados. Você pode fazer essas verificações usando turf.js (provavelmente combinando turf.explode com turf.within ).

nathansnider
fonte