Làm cách nào để biết đường dẫn GeoJSON có giao nhau với một tính năng khác trong Leaflet không?


8

Tôi có một ứng dụng trong đó người dùng vẽ một đường dẫn (một loạt các đường thẳng được kết nối) và đường dẫn này có thể không giao nhau với bất kỳ tính năng nào trong một lớp GeoJSON cụ thể.

Tôi cần kiểm tra xem không có điểm nào dọc theo các đường này giao nhau với lớp GeoJSON, không chỉ các điểm cuối.

Làm thế nào tôi có thể thực hiện kiểm tra này?


Nó có thể thực hiện được với Turf.js
ghybs

Bất cứ điều gì đặc biệt tôi nên xem trong Turf.js?
LavaHot

Tôi không nghĩ turf.js làm điều này. Bạn có thể điều chỉnh một số mã phát hiện giao lộ khác cho mục đích của mình. Chẳng hạn, cái này , được thiết kế để hoạt động trên các dòng của GeoJSON, có thể giúp bạn thực hiện mọi cách, nhưng nếu bạn cần nó để làm việc với các lớp đa giác, bạn cần phải điều chỉnh nó để chấp nhận đầu vào đa giác hoặc trích xuất đa giác như linestrings từ lớp GeoJSON của bạn đầu tiên.
nathansnider

2
Wow công việc ấn tượng! :-) Tôi có thể nghĩ turf.intersect sẽ thực hiện công việc? (xây dựng trên jsfiddle của bạn: fiddle.jshell.net/tyt4oeux/1 ) Nhưng có lẽ tôi đã bỏ qua câu hỏi.
ghybs

Ah-ha, nhưng tất nhiên là có tác dụng! Tôi chỉ lấy các tài liệu API theo từ của họ rằng turf.intersect cần một đa giác làm đầu vào. Nó không bao giờ đau để thử, tôi đoán. Vì turf.intersect có lợi thế là đơn giản hơn cũng như phát hiện khi một dòng hoàn toàn nằm trong một đa giác, nên đó rõ ràng là cách để đi đến đây, tôi nghĩ vậy.
nathansnider

Câu trả lời:


4

Bạn có thể thử thư viện Turf và một phương thức như giao nhau: http://turfjs.org/docs/#intersect

Đây là ví dụ mã từ thư viện đó:

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

Bạn nên thêm một ví dụ về cách làm điều này: liên kết bị thối theo thời gian.
alphabetasoup

Nếu liên kết đó sẽ bị thối theo thời gian, thì toàn bộ câu trả lời của tôi sẽ trở nên vô hiệu. Toàn bộ ví dụ dựa trên sự tồn tại của thư viện Turf, và nếu cái đó sẽ không ở đó ... Tuy nhiên tôi đã sao chép ví dụ đó vào câu trả lời của mình.
Adrian Ber

4
Liên kết bị mục nát, đây là một cái mới turfjs.org/docs/#intersect
Calvein

Liên kết thối lần nữa (hoặc nhầm lẫn); không gạch chéo, chỉ: turfjs.org/docs#intersect
Hendy

1

EDIT: Xem fiddle của ghybs từ nhận xét ở trên để biết giải pháp đơn giản và tốt hơn bằng cách sử dụng turf.js. Câu trả lời gốc sau:


Dưới đây là phiên bản sửa đổi của thói quen giao cắt từ thư viện Geojson-js-utils , lấy các dòng của GeoJSON làm đầu vào và tạo các điểm GeoJSON của giao lộ làm đầu ra:

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

Việc sửa đổi là cần thiết bởi vì chức năng ban đầu là tính toán các giao điểm từ vĩ độ và kinh độ, như thể chúng chỉ là tọa độ trên một mặt phẳng, tạo ra kết quả không chính xác (đặc biệt là ở vĩ độ cao hoặc trên khoảng cách xa). Việc sử dụng L.Projectionđể chuyển đổi sang hệ tọa độ dự kiến phù hợp (hoặc, trong trường hợp này, gần như phù hợp ) trong quá trình tính toán sẽ khắc phục điều này.

Người ta có thể sửa đổi nó hơn nữa để chấp nhận các đối tượng hình học Leaflet thay vì chỉ LineStrings, nhưng thay vào đó tôi đã sử dụng hàm khá khó sử dụng này để tạo LineStrings được chuyển đến chức năng giao nhau:

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

và chức năng này để lấy các đối tượng Leaflet, chuyển đổi chúng thành LineStrings và kiểm tra các giao điểm:

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

Dưới đây là một ví dụ sử dụng điều này với Leaflet.draw:

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

Khi bạn vẽ xong một đối tượng, nó sẽ đặt các điểm đánh dấu trên bản đồ tại các điểm mà đối tượng được vẽ giao với hình dạng cơ sở. Nó không thể kiểm tra các giao lộ trong khi một đường dẫn vẫn đang được vẽ, bởi vì Leaflet.draw không cung cấp cho chúng tôi bất kỳ trình xử lý sự kiện nào để sử dụng trong khi bản vẽ vẫn đang được tiến hành. Nó sẽ kiểm tra ngay khi một sự kiện bốc thăm được hoàn thành.

Cũng lưu ý rằng điều này sẽ không phát hiện các giao lộ cho các đường dẫn nằm hoàn toàn trong đa giác mà chúng đang được kiểm tra. Bạn có thể thực hiện các kiểm tra đó bằng cách sử dụng turf.js (có thể kết hợp turf.explode với turf.within ).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.