OpenLayers ile dateline Linestringleri bölme


9

Birkaç yıl önce Uluslararası Tarih Satırı etrafına sarıldım ve @jdeolive , dateLine'daki özellikleri bölmemi önerdi . Ben de denedim.

Uydu parçamı splitWith ile dateline ayırmaya çalıştığımda geri dönüyorum null. Doğru ayrıldığımı biliyorum çünkü Greenwich hattına ayrıldığımda beklenen sonuçları elde ediyorum.

Herkes OpenLayers ile tarih çizgisi boyunca bir Linestring programlı olarak düzgün bir şekilde nasıl bölebilirim biliyor mu? Varsa örnek kod ararım.

Denedim wrapDateLineama vektör katmanım böyle olmasına rağmen vektör katmanları üzerinde çalışmıyor gibi görünüyor:

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

resim açıklamasını buraya girin

İşte benim kod:

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

Sorum bu sorunun bir kopyası değil çünkü ogr2ogr içinde nasıl yapılacağını bilmek istiyorlar

Güncelleme:

Çalıştığım tipik bir veri kümesi şöyledir (24 saat uydu izi): Linestring wkt BURAYA bulunabilir .

resim açıklamasını buraya girin


Hangi açıcı sürümünü kullanıyorsunuz?
Ocak'ta Plux

@Plux 2.13.1 (En Son)
CaptDragon

1
API'larına göre, wrapDateLine yalnızca taban katmanında kullanılmalıdır, bu nedenle vektör katmanında çalışmadığı şaşırtıcı değildir. Ancak, vektör katmanında nasıl çalışacağını bilmiyorum. Ben datelinden geçen çokgenlerle benzer bir sorun yaşıyorum.
Plux

1
Benim kontrol @Plux çözüm
CaptDragon

Güzel çözüm. Maalesef sorunum için geçerli değil, sorunumun geoserver tarafında daha fazla olduğuna inanıyorum ve çokgenim, -180.0000000000000003 90.00000190734869 datelinin "diğer tarafında" olan koordinatlara sahip olduğuna inanıyorum. Haritamda bunu gösteren sorunlar yaşamıyorum (bunu yapan bir wms var), ama bu geoserver için bir wfs sorgusunda filtre kutuları olarak kullanmak istiyorum :)
Plux

Yanıtlar:


2

Sorun, özelliğinizin tarih çizgisini OpenLayers perspektifinden geçmemesidir, bu nedenle bölünmüş çizginiz özelliğinizle kesişmez. Verilerinizden bir örnek:

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

-178'den 156'ya gidiyorsunuz ve bu, tarih satırını OpenLayers perspektifinden geçmiyor. Tarih satırına bölmek yerine, minimum X değerinize bölmelisiniz.

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

Burada uydu kanalınızı 2 özelliğe başarıyla ayıran bir örnek oluşturdum: http://jsfiddle.net/6XJ5A/

Şimdi WKT'yi güncellemenizde birden çok satırla kullanmak için, düz bir satır kullanmak yerine, tüm veri kümesini gözden geçirmeli ve bölünmüş çizginizi dateline doğru giden tüm koordinatlarla oluşturmalısınız. Çok satırlı bir çizgide küçük bir çizgi oluşturarak, dateline geçmesi gereken tüm koordinatları bölebilirsiniz. Güncellenmiş örnek: http://jsfiddle.net/Jc274/

Ve kod:

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

Bu size datelini "kesen" tüm noktalarda bölünmüş bir çizgi döndürür

Ben de 2 koordinatları bağlamak için harita üzerinden gider çizgiyi kaldırmak için koordinatlar arasında döngü unutmayın:

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

Güncelleme: Sadece bölünmüş satırı eklemek için ilk örneği güncelledim. Ayrıca açıklamayı buna göre güncelledim. Bu yaklaşım, sağladığınız 24 saatlik uydu kanalı ile kurşun geçirmez değil, ancak üzerinde çalışıyorum.

Güncelleme 2: İkinci örneği güncelledim. Bölmek için bir çok satır kullanarak ve bölme tarafından eklenen ekstra koordinatları kaldırmak için sonuçta döngü yaparak, asla dateline geçmeyen bir dizi özellik elde ederiz.


Çaba için + 1'leyin, ancak harita örnekleriniz sorunu çözmüyor gibi görünüyor. Örnek haritanızdaki uydu kanalları, uydu kanalının doğal yolunu izlemek yerine tüm dünyayı geçer. Bir şey mi kaçırmalıyım?
CaptDragon

Sorunu yanlış anlıyor olabilirsiniz çünkü ..., -178.475596 -81.673196, 156.248392 -81.611421,...kesinlikle datelini geçiyor. Buraya bakın
CaptDragon

Kodu ve açıklamamı güncelleyeyim. Veri hattını geçmesi gerektiğini biliyorum, ancak OpenLayers bunu desteklemiyor. OL açısından bakıldığında, datelini geçmiyor.
Julien-Samuel Lacroix

Doğru. İşte sorun bu. OpenLayers'ı kandırıp çizgiyi, kenara kadar gidecek ve sonra olması gereken diğer tarafta devam edecek şekilde bölmem gerekecek.
CaptDragon

2

@Dane'nin github'ında harika bir çözüm buldum. Buna Arc.js denir ve Büyük daire rotalarını hesaplamak içindir. Sadece bu da değil, aynı zamanda datelin üzerindeki çizgiyi böler ve size OpenLayers'ın kolayca eşleştirebileceği datelinde buluşan iki linestring sağlar. Umarım o ödül iddia ileri gelir.

İşte sonuçlarım:

resim açıklamasını buraya girin resim açıklamasını buraya girin


1

SplitWith görevi yer 3 boyutlu şekli hakkında bilmiyor. Sadece 2 boyutlu bir dünyada çalışır. Sizin durumunuzda, tüm LINESTRINGX koordinatlarınız -180 ile 180 arasındadır. Dolayısıyla, OpenLayers'ın iki boyutlu perspektifinden, çizgi dizesi aslında bölünmüş geometrinizi (tarih çizgisi) asla geçmez ve geri dönerek bunu size söyler null.

Bölme işlemini yapmak için bazı özel kodlar yazmak zorunda kalacağınıza inanıyorum. Köşelerinizin üzerinde döngüler, böyle çıkış satırı dizeleri bina bir algoritma hayal ediyorum:

  • Her bitişik köşe çifti için, aralarındaki parçanın tarih çizgisini geçip geçmediğine karar verin.
  • Yoksa, bu segmenti olduğu gibi tutun ve "geçerli" çıktı satırı dizesine ekleyin.
  • O takdirde yapar , iki bölüme segmenti bölün. Bir parçayı "geçerli" çizgi dizgisine ekleyin, yeni bir "geçerli" çizgi dizgisi başlatın ve diğer parçayı bu yenisine ekleyin.

Bir çift köşenin tarih çizgisini geçip geçmediğini belirlemek için makul bir buluşsal yöntem, X koordinatları arasındaki farkın 180 dereceden fazla olup olmadığını görmektir. (Bu, örneğin kutup bölgelerinde yanlış olabilir. Belki de gerçekten enlemlere sahip olmayacak kadar şanslısınız.)

Bir parçayı iki parçaya bölme işlemi doğrusal enterpolasyon kadar basit olabilir (eğer parçanın doğruluğu hakkında çok fazla umursamıyorsanız). Segmentin tarih çizgisini geçtiğini tespit ettiğinizde, ikinci tepe noktasının bir kopyasını oluşturur ve X koordinatını hareket ettirirsiniz (360 derece ekleyerek veya çıkararak) ve sonra Y koordinatını enterpole edersiniz.

DÜZENLEME : İşte verilerinizde yukarıdaki algoritmayı gösteren bir JSFiddle: http://jsfiddle.net/85vjS/


0

Greenwich ile çalışıyorsa, bunun nedeni CRS'nizin sınırları içindesinizdir. Bu yüzden ilk önce işaret ettiğiniz yazı ile aynı geçici çözümü öneririm:

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

ve belki

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

diğer taraf için.

Başka bir çözüm, datelinde "bağlı olmayan" bir CRS'de çalışmaktır. Böylece verilerinizi sorunsuz bir şekilde bölebilirsiniz.


Ben zaten her ikisini de denedim LINESTRING(179.99 -89, 179.99 89)ve LINESTRING(-179.99 -89, -179.99 89)boşuna. CRS ile ilgili olarak, maalesef, bu benim amacım için işe yaramaz çünkü dünya çapında birçok kez uydu kanallarını eşleştiriyorum. Bu yüzden tüm CRS'ler bir yere ayrılır ve böldüğüm her yerde aynı sorunu yaşayacağım. Girdiniz için teşekkürler.
CaptDragon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.