указать точку в многоугольнике с помощью leaflet-pip и d3.geoContains - PullRequest
2 голосов
/ 22 января 2020

Я озадачен тем, что я делаю здесь неправильно. У меня есть объект geo Json, и я пытаюсь найти объект, который содержит данную точку. Когда я использую d3.geoContains, я получил ложные результаты, ie те многоугольники, которые не содержат точку.

Сейчас я использую point in polygon из https://mapbox.github.io/leaflet-pip/, который, кажется, работает нормально, однако я не знаю, чего мне не хватает с d3.geoContains. Согласно документам он должен возвращать true, если объект содержит точку.

В приложении приведен простой случай, когда у меня есть 4 квадрата, два в нижнем ряду, один рядом с другим, и два других в верхнем ряду. Я проверяю, чтобы найти многоугольник, содержащий точку [0,7, 0,7], и pip возвращает правильный ответ, справа вверху, но d3.geoContains возвращает остальные три квадрата.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <!--d3 -->
    <script src='https://d3js.org/d3.v4.min.js'></script>

    <!-- leaflet -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css"/>
    <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js"></script>

    <script src="https://unpkg.com/leaflet-pip@1.1.0/leaflet-pip.js"></script>

</head>
<body>


<script>
    var my_squares = {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "properties": {"id": "bottom_left"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.0, 0.0], [0.5, 0.0], [0.5, 0.5], [0.0, 0.5], [0.0, 0.0]]],
                }
            },
            {
                "type": "Feature",
                "properties":  {"id": "bottom_right"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.5, 0.0], [1.0, 0.0], [1.0, 0.5], [0.5, 0.5], [0.5, 0.0]]],
                }
            },
            {
                "type": "Feature",
                "properties": {"id": "top_right"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.5, 0.5], [1.0, 0.5], [1.0, 1.0], [0.5, 1.0], [0.5, 0.5]]],
                }
            },
            {
                "type": "Feature",
                "properties": {"id": "top_left"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.0, 0.5], [0.5, 0.5], [0.5, 1.0], [0.0, 1.0], [0.0, 0.5]]],
                }
            },
        ]
    }

    var my_point = [0.7, 0.7];
    console.log('Point is: ' + my_point)
    var geo_out = my_squares.features.filter(function(d) {return d3.geoContains(d, my_point)});
    console.log('From d3.geoContains');
    geo_out.forEach(function(d){console.log(d.properties.id)});

    console.log('')

    var polygons = L.geoJson(my_squares, {});
    var res = leafletPip.pointInLayer(my_point, polygons)[0].feature.properties.id;
    console.log('From pip');
    console.log(res)

</script>

</body>
</html>

Ответы [ 2 ]

2 голосов
/ 22 января 2020

Ваши координаты расположены в направлении против часовой стрелки.

Поскольку исходный Geo JSON 1.0 spe c ничего не сказал по порядку намотки, поэтому d3-geo сделано до своего собственного правила :

Сферические многоугольники также требуют соглашения о порядке намотки, чтобы определить, какая сторона многоугольника находится внутри: внешнее кольцо для многоугольников, меньших, чем полусфера, должно быть по часовой стрелке, в то время как внешнее кольцо для многоугольников, больших чем полусфера, должно быть против часовой стрелки.

Следовательно, с точки зрения d3-geo, ваши многоугольники являются дырками; функции охватывают все везде, кроме тех отверстий. Вот наглядный пример того, что происходит .


К сожалению, Geo JSON spe c в RFC7946 позже определил это и указал с точностью до наоборот :

Линейное кольцо ДОЛЖНО следовать правилу правой руки относительно области, которую оно ограничивает, то есть внешние кольца против часовой стрелки, а отверстия по часовой стрелке.

Согласно краткому прочтению набора тестов , кажется, что листовка-пипс принимает этот порядок намотки, поэтому она понимает ваши полигоны.


Если мы расположите кольца по часовой стрелке, ответы будут top_right:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <!--d3 -->
    <script src='https://d3js.org/d3.v4.min.js'></script>

    <!-- leaflet -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css"/>
    <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js"></script>

    <script src="https://unpkg.com/leaflet-pip@1.1.0/leaflet-pip.js"></script>

</head>
<body>


<script>
    var my_squares = {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "properties": {"id": "bottom_left"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.0, 0.0], [0.0, 0.5], [0.5, 0.5], [0.5, 0.0], [0.0, 0.0]]],
                }
            },
            {
                "type": "Feature",
                "properties":  {"id": "bottom_right"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.5, 0.0], [0.5, 0.5], [1.0, 0.5], [1.0, 0.0], [0.5, 0.0]]],
                }
            },
            {
                "type": "Feature",
                "properties": {"id": "top_right"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.5, 0.5], [0.5, 1.0], [1.0, 1.0], [1.0, 0.5], [0.5, 0.5]]],
                }
            },
            {
                "type": "Feature",
                "properties": {"id": "top_left"},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [[[0.0, 0.5], [0.0, 1.0], [0.5, 1.0], [0.5, 0.5], [0.0, 0.5]]],
                }
            },
        ]
    }

    var my_point = [0.7, 0.7];
    console.log('Point is: ' + my_point)
    var geo_out = my_squares.features.filter(function(d) {return d3.geoContains(d, my_point)});
    console.log('From d3.geoContains');
    geo_out.forEach(function(d){console.log(d.properties.id)});

    console.log('')

    var polygons = L.geoJson(my_squares, {});
    var res = leafletPip.pointInLayer(my_point, polygons)[0].feature.properties.id;
    console.log('From pip');
    console.log(res)

</script>

</body>
</html>

(Я был удивлен, что ответ Пипса не изменился. Возможно, он просто предполагал, что вы имели в виду внешнее кольцо в любом случае, или, возможно, он не поддерживает многоугольники с отверстиями в первое место.)

1 голос
/ 22 января 2020

D3, включая d3.geoContains, использует сферическую геометрию. Это в отличие от почти любого другого инструмента веб-картографирования, в котором точки широты и долготы считаются декартовыми.

Это означает, что каждое соединение между двумя точками в географическом объекте следует за большим круговым расстоянием (поэтому рисовать ограничивающие рамки сложнее, см. здесь , в то время как другие библиотеки могут усложнить его Нарисуйте большие дуги окружности, см. здесь ).

Используя сферическую геометрию, антимеридиан не вызывает столько проблем, но, наоборот, имеет значение порядок намотки: вы рисуете везде кроме области интереса или самой области интереса.

Это относится к d3.geoContains: Ваши полигоны намотаны в обратном порядке, как и должно быть. Вместо того, чтобы ограничивать маленькую коробку, они связывают остальной мир - вот почему вы получите противоположный результат, как и ожидалось.

У вас есть два варианта: перемотать полигоны, например, с помощью turf. js:

var fixed = features.map(function(feature) {
    return turf.rewind(feature,{reverse:true});
})

Или, если все ваши полигоны последовательно намотаны в обратном направлении Вы можете просто инвертировать результат d3-geoContains, хотя это выглядит гораздо менее идеальным решением.

...