Memisahkan Linestrings pada dateline dengan OpenLayers

9

Beberapa tahun yang lalu saya memposting Garis Tanggal Internasional dan @ jdeolive menyarankan saya membagi fitur di dateLine. Jadi saya mencoba.

Ketika saya mencoba untuk membagi trek satelit saya dengan splitWith pada dateline saya kembali null. Saya tahu saya membelah dengan benar karena ketika saya berpisah di jalur Greenwich saya mendapatkan hasil yang diharapkan.

Adakah yang tahu bagaimana saya bisa membagi Linestring secara terprogram sesuai dengan garis tanggal dengan OpenLayers? Saya mencari kode contoh jika Anda memilikinya.

Saya sudah mencoba wrapDateLinetetapi sepertinya tidak berfungsi pada layer vektor meskipun layer vektor saya seperti ini:

vectorLayer = new OpenLayers.Layer.Vector("GroundTracks", {
    renderers: ['Canvas', 'VML'],
    wrapDateLine: true}); // <-- shoud be wraping.

masukkan deskripsi gambar di sini

Ini kode saya:

var features = [];
var format = new OpenLayers.Format.WKT({
    'internalProjection': map.baseLayer.projection,
    'externalProjection': prjGeographic
});
var satTrack = format.read("LINESTRING (95.538611 13.286511, 94.730711 16.908947, 93.901095 20.528750, 93.043594 24.145177, 92.150978 27.757436, 91.214579 31.364666, 90.223791 34.965899, 89.165364 38.560019, 88.022401 42.145679, 86.772901 45.721205, 85.387568 49.284424, 83.826433 52.832413, 82.033480 56.361087, 79.927797 59.864504, 77.388419 63.333664, 74.227306 66.754285, 70.139140 70.102478, 64.605267 73.335774, 56.712904 76.373458, 44.881134 79.052803, 26.939886 81.047314, 02.704174 81.839241, -21.686285 81.101751, -39.887660 79.141947, -51.906937 76.480894, -59.912477 73.452897, -65.514482 70.225089, -69.645366 66.880243, -72.834535 63.461797, -75.393132 59.994131, -77.512464 56.491789, -79.315407 52.963919, -80.884039 49.416549, -82.275114 45.853820, -83.529088 42.278691, -84.675583 38.693355, -85.736827 35.099503, -86.729876 31.498490, -87.668095 27.891443, -88.562176 24.279331, -89.420849 20.663020, -90.251389 17.043303, -91.059999 13.420926, -91.852092 09.796602, -92.632515 06.171020, -93.405728 02.544857, -94.175960 -01.081217, -94.947343 -04.706542, -95.724045 -08.330456, -96.510402 -11.952298, -97.311065 -15.571400, -98.131162 -19.187081, -98.976502 -22.798638, -99.853829 -26.405335, -100.771148 -30.006378, -101.738172 -33.600889, -102.766925 -37.187866, -103.872602 -40.766117, -105.074803 -44.334175, -106.399366 -47.890158, -107.881153 -51.431559, -109.568417 -54.954914, -111.529886 -58.455253, -113.866668 -61.925160, -116.733085 -65.353081, -120.374635 -68.720132, -125.199754 -71.993642, -131.916790 -75.113368, -141.772276 -77.960803, -156.750096 -80.294831, -178.475596 -81.673196, 156.248392 -81.611421, 135.042323 -80.136505, 120.556535 -77.748172, 111.014840 -74.872356, 104.485504 -71.737081, 99.775637 -68.454400, 96.208126 -65.081545, 93.391438 -61.649716, 91.089380 -58.177038, 89.152970 -54.674643, 87.484294 -51.149703, 86.016609 -47.607042, 84.702947 -44.050030, 83.509299 -40.481112, 82.410411 -36.902133, 81.387093 -33.314533, 80.424442 -29.719485, 79.510644 -26.117981, 78.636145 -22.510889, 77.793053 -18.898997, 76.974710 -15.283040, 76.175371 -11.663718, 75.389950 -08.041709, 74.613831 -04.417680, 73.842693 -00.792294, 73.072378 02.833789, 72.298749 06.459907, 71.517566 10.085391, 70.724342 13.709564, 69.914194 17.331733, 69.081655 20.951185, 68.220447 24.567170, 67.323194 28.178891, 66.381031 31.785476, 65.383084 35.385943, 64.315735 38.979152, 63.161579 42.563725, 61.897893 46.137940, 60.494337 49.699551, 58.909396 53.245525, 57.084691 56.771602, 54.935577 60.271560, 52.334964 63.735923, 49.084320 67.149569, 44.859585 70.487030, 39.107498 73.702694, 30.852243 76.709182, 18.420695 79.329532, -00.339911 81.212453, -25.028018 81.831766)");

var featGreenwichLine = format.read("LINESTRING(0 -89, 0 89)");
var featDateLine = format.read("LINESTRING(180 -89, 180 89)");

features.push(featGreenwichLine);
features.push(featDateLine);
features.push(satTrack);

var resultsGreenwich = satTrack.geometry.splitWith(featGreenwichLine.geometry);
var resultsDateLine = satTrack.geometry.splitWith(featDateLine.geometry);

console.log(resultsGreenwich); //<--RETURNS EXPECTED RESULTS.
console.log(resultsDateLine);//<--RETURNS NULL.

vectorLayer.addFeatures(features);

Pertanyaan saya bukan duplikat dari pertanyaan ini karena mereka ingin tahu bagaimana melakukannya di ogr2ogr

Memperbarui:

Inilah tampilan dataset yang biasa saya gunakan (trek satelit 24 jam): Linestring wkt dapat ditemukan di SINI .

masukkan deskripsi gambar di sini

CaptDragon
sumber
Versi openlayer apa yang Anda gunakan?
Plux
@Plux 2.13.1 (Terakhir)
CaptDragon
1
Menurut API mereka, wrapDateLine hanya boleh digunakan pada lapisan dasar, jadi tidak heran itu tidak bekerja pada lapisan vektor. Namun, saya tidak tahu bagaimana cara membuatnya bekerja pada layer vektor. Saya mengalami masalah serupa dengan multipoligon yang melintasi garis data.
Plux
1
@Lux periksa solusi
CaptDragon
Solusi yang bagus. Sayangnya, ini tidak berlaku untuk masalah saya, saya percaya masalah saya lebih pada sisi geoserver, dan poligon saya berisi koordinat yang ada di "sisi lain" dateline, seperti -180.0000000000003 90.00000190734869 yang menciptakan beberapa masalah dalam geoserver yang saya percaya. Saya tidak memiliki masalah menampilkan ini di peta saya (saya punya wms yang melakukan ini), tapi saya ingin menggunakan ini sebagai filterbox pada permintaan-wfs ke geoserver :)
Plux

Jawaban:

2

Masalahnya adalah bahwa fitur Anda tidak melewati garis tanggal dari perspektif OpenLayers, sehingga garis split Anda tidak memotong fitur Anda. Contoh dari data Anda:

..., -178.475596 -81.673196, 156.248392 -81.611421,...

Anda beralih dari -178 ke 156, dan ini tidak melewati garis tanggal dari perspektif OpenLayers. Alih-alih membelah pada garis tanggal, Anda harus membagi nilai X minimum Anda.

// Build the splitting line based on the min and max coordinates of the vector to split
var minX = 999999999;
var minY = -20037508.34 // minimum value of the spherical mercator projection
var maxY = 20037508.34  // maximum value of the spherical mercator projection
//Extract the minimum X from the data as bounds seems to be rounded.
for(var i=0; i<satTrack.geometry.components.length; i++) {
    if(satTrack.geometry.components[i].x < minX)
        minX = satTrack.geometry.components[i].x;
}
var pointList = [
    new OpenLayers.Geometry.Point(minX, minY),
    new OpenLayers.Geometry.Point(minX, maxY)
];
var featDateLine = new OpenLayers.Feature.Vector(
    new OpenLayers.Geometry.LineString(pointList)
);

Saya telah membuat contoh di sini yang berhasil membagi trek satelit Anda menjadi 2 fitur: http://jsfiddle.net/6XJ5A/

Sekarang untuk menggunakan WKT dengan banyak garis dalam pembaruan Anda, alih-alih menggunakan garis lurus, Anda harus menelusuri seluruh dataset dan membangun garis pisah Anda dengan semua koordinat yang melintasi garis data. Dengan membangun garis kecil di dalam sebuah multiline, Anda dapat membagi semua koordinat yang harus melintasi dateline. Inilah contoh yang diperbarui: http://jsfiddle.net/Jc274/

Dan kodenya:

// Build the splitting line based on the min and max coordinates of the vector to split
var pointList = [];
var lastPoint = satTrack.geometry.components[0];
//Extract the minimum X from the data as bounds seems to be rounded.
for (var i = 1; i < satTrack.geometry.components.length; i++) {
    if (Math.abs(satTrack.geometry.components[i].x - lastPoint.x) > 10000000) {
        pointList.push(satTrack.geometry.components[i]);
    }
    lastPoint = satTrack.geometry.components[i];
}

var lineList = [];
for(var i=0; i<pointList.length; i++) {
    lineList.push(new OpenLayers.Geometry.LineString([
        new OpenLayers.Geometry.Point(pointList[i].x, pointList[i].y-0.00001), 
        new OpenLayers.Geometry.Point(pointList[i].x, pointList[i].y+0.00001)
    ]));
}

var featDateLine = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.MultiLineString(lineList), null, split_style);

Ini akan mengembalikan Anda garis terpecah pada semua titik yang "memotong" garis data

Perhatikan bahwa saya juga memutari koordinat untuk menghapus garis yang melintasi peta untuk menghubungkan 2 koordinat:

for (var i = 0; i < resultsDateLine.length; i++) {
    // Remove the first (or last) point of the line, the one that cross the dateline
    if (Math.abs(resultsDateLine[i].components[0].x - resultsDateLine[i].components[1].x) > 10000000) {
        resultsDateLine[i].removeComponent(resultsDateLine[i].components[0]);
    }
    if (Math.abs(resultsDateLine[i].components[resultsDateLine[i].components.length - 1].x - resultsDateLine[i].components[resultsDateLine[i].components.length - 2].x) > 10000000) {
        resultsDateLine[i].removeComponent(resultsDateLine[i].components[resultsDateLine[i].components.length - 1]);
    }
    features.push(new OpenLayers.Feature.Vector(resultsDateLine[i], null, style_array[i]));
}

Memperbarui: Saya memperbarui contoh pertama untuk hanya menambahkan baris yang terpecah. Saya juga memperbarui penjelasannya. Pendekatan ini bukan bukti peluru dengan trek satelit 24 jam yang Anda berikan, tapi saya sedang mengusahakannya.

Pembaruan 2: Saya memperbarui contoh kedua. Dengan menggunakan multiline untuk membagi dan mengulangi hasil untuk menghapus koordinat tambahan yang ditambahkan oleh split, kami mendapatkan serangkaian fitur yang tidak pernah melintasi dateline.

Julien-Samuel Lacroix
sumber
Memberi +1 untuk upaya, tetapi contoh peta Anda sepertinya tidak menyelesaikan masalah. Trek satelit pada peta contoh Anda masih melintasi seluruh bola dunia alih-alih mengikuti jalur alami trek satelit. Saya harus kehilangan sesuatu?
CaptDragon
Anda mungkin salah paham masalah karena ..., -178.475596 -81.673196, 156.248392 -81.611421,...benar-benar melewati batas waktu. Lihat di sini
CaptDragon
Biarkan saya memperbarui kode dan penjelasan saya. Saya tahu ini harus melewati garis data, tetapi OpenLayers tidak mendukung ini. Dari sudut pandang OL, itu tidak melewati garis data.
Julien-Samuel Lacroix
Betul. Itulah masalahnya. Saya perlu mengelabui OpenLayers dan membagi garis sehingga naik ke tepi kemudian melanjutkan di sisi lain di mana seharusnya.
CaptDragon
2

Saya menemukan solusi hebat di github @Dane. Ini disebut Arc.js dan ini untuk menghitung rute lingkaran besar. Tidak hanya itu, ia juga akan membagi garis pada garis data dan memberi Anda dua linestrings yang bertemu pada garis data, yang dapat dengan mudah dipetakan oleh OpenLayers. Saya harap dia datang untuk mengklaim hadiah itu.

Inilah hasil saya:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

CaptDragon
sumber
1

Fungsi splitWith tidak tahu tentang bentuk 3 dimensi bumi. Ini hanya beroperasi di dunia 2 dimensi. Dalam kasus Anda, semua LINESTRINGkoordinat X Anda berada di antara -180 dan 180. Jadi dari perspektif dua dimensi OpenLayers, string garis tidak pernah benar-benar melewati geometri perpecahan Anda (garis tanggal) dan ia memberi tahu Anda demikian dengan kembali null.

Saya percaya bahwa Anda harus menulis beberapa kode khusus untuk melakukan pemisahan. Saya membayangkan algoritme yang melingkari simpul Anda, membangun string garis keluaran seperti ini:

  • Untuk setiap pasangan simpul yang berdekatan, putuskan apakah segmen di antara mereka melintasi garis tanggal.
  • Jika tidak, pertahankan segmen seperti itu dan tambahkan ke string "saat ini".
  • Jika tidak , membagi segmen menjadi dua bagian. Tambahkan satu bagian ke string baris "saat ini", mulai string baris "saat ini" yang baru, dan tambahkan bagian lain ke yang baru ini.

Satu heuristik yang masuk akal untuk menentukan apakah sepasang simpul melewati garis tanggal adalah untuk melihat apakah perbedaan antara koordinat X lebih dari 180 derajat. (Meskipun ini bisa salah, misalnya, di daerah kutub. Mungkin Anda cukup beruntung untuk tidak memiliki garis lintang yang sangat tinggi.)

Operasi pemisahan segmen menjadi dua bagian bisa sesederhana interpolasi linier (jika Anda tidak terlalu peduli dengan akurasi lintasan). Ketika Anda mendeteksi bahwa segmen melintasi garis tanggal Anda membuat salinan dari simpul kedua dan memindahkan koordinat X-nya (dengan menambahkan atau mengurangi 360 derajat) kemudian interpolasi koordinat Y.

EDIT : Ini JSFiddle yang menunjukkan algoritma di atas pada data Anda: http://jsfiddle.net/85vjS/

csd
sumber
0

Jika bekerja dengan Greenwich, ini karena Anda berada di dalam batas CRS Anda. Jadi saya pertama-tama akan menyarankan solusi yang sama seperti di pos yang Anda tunjuk:

var featDateLine = format.read("LINESTRING(179.99 -89, 179.99 89)");

dan mungkin

var featDateLine = format.read("LINESTRING(-179.99 -89, -179.99 89)");

untuk sisi lain.

Solusi lain adalah bekerja di CRS yang tidak "tidak terikat" di dateline. Anda kemudian dapat membagi data Anda tanpa masalah.

radouxju
sumber
Saya sudah mencoba keduanya LINESTRING(179.99 -89, 179.99 89)dan LINESTRING(-179.99 -89, -179.99 89)tidak berhasil. Mengenai CRS, sayangnya, ini tidak akan berfungsi untuk tujuan saya karena saya memetakan trek satelit yang berkeliling dunia berkali-kali. Jadi semua CRS terpecah di suatu tempat dan saya akan memiliki masalah yang sama di mana pun saya membaginya. Terima kasih atas masukan Anda.
CaptDragon