การแยก Linestrings บน dateline ด้วย OpenLayers


9

สองสามปีที่ผ่านมาฉันโพสต์บรรทัดวันที่สากลล้อมรอบและ @jdeolive แนะนำให้ฉันแยกคุณสมบัติที่ dateLine ดังนั้นฉันจึงพยายาม

เมื่อฉันพยายามที่จะแยกการติดตามดาวเทียมของฉันกับsplitWithnullในวันเดือนปีที่ผมได้รับกลับมา ฉันรู้ว่าฉันแยกอย่างถูกต้องเพราะเมื่อฉันแยกสายกรีนวิชฉันจะได้รับผลลัพธ์ที่คาดหวัง

มีใครรู้บ้างว่าฉันสามารถแยกLinestringอย่างเป็นโปรแกรมตามบรรทัดวันที่ด้วย OpenLayers ได้อย่างไร ฉันค้นหาโค้ดตัวอย่างถ้าคุณมี

ฉันลองแล้วwrapDateLineแต่ดูเหมือนว่ามันจะไม่ทำงานบนเลเยอร์เวกเตอร์แม้ว่าเลเยอร์เวกเตอร์ของฉันจะเป็นแบบนี้:

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

ป้อนคำอธิบายรูปภาพที่นี่

นี่คือรหัสของฉัน:

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

คำถามของฉันไม่ซ้ำกับคำถามนี้เพราะพวกเขาต้องการทราบวิธีการทำใน ogr2ogr

ปรับปรุง:

นี่คือสิ่งที่ผมทำงานชุดข้อมูลทั่วไปที่มีลักษณะเหมือน (ติดตามดาวเทียมตลอด 24 ชั่วโมง): ผู้ WKT LineString สามารถพบได้ที่นี่

ป้อนคำอธิบายรูปภาพที่นี่


คุณใช้ openlayers รุ่นใดอยู่
Plux

@Plux 2.13.1 (ล่าสุด)
CaptDragon

1
ตาม API ของพวกเขา wrapDateLine ควรใช้กับเลเยอร์ฐานเท่านั้นดังนั้นจึงไม่น่าแปลกใจที่มันจะไม่ทำงานบนเลเยอร์เวกเตอร์ อย่างไรก็ตามฉันไม่รู้ว่าจะทำให้มันทำงานบนเลเยอร์เวกเตอร์ได้อย่างไร ฉันมีปัญหาที่คล้ายกันตัวเองกับ multipolygons ที่ข้ามวันที่
Plux

1
@Plux ลองดูโซลูชัน
CaptDragon

ทางออกที่ดี น่าเสียดายที่ไม่สามารถใช้กับปัญหาของฉันได้ฉันเชื่อว่าปัญหาของฉันอยู่ที่ geoserver-side มากกว่าและรูปหลายเหลี่ยมของฉันมีพิกัดที่อยู่ใน "ด้านอื่น ๆ " ของ dateline เช่น -180.00000000000003 90.00000190734869 ซึ่งสร้างปัญหาบางอย่างใน geoserver ที่ฉันเชื่อ ฉันไม่มีปัญหาในการแสดงสิ่งนี้บนแผนที่ของฉัน (ฉันมี wms ที่ทำสิ่งนี้) แต่ฉันต้องการใช้สิ่งเหล่านี้เป็นตัวกรองใน wfs-query ไปยัง geoserver :)
Plux

คำตอบ:


2

ปัญหาคือคุณลักษณะของคุณไม่ข้ามบรรทัดวันที่จากมุมมองของ OpenLayers ดังนั้นเส้นแบ่งของคุณจึงไม่ตัดกับคุณลักษณะของคุณ ตัวอย่างจากข้อมูลของคุณ:

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

คุณไปจาก -178 ถึง 156 และสิ่งนี้จะไม่ข้ามบรรทัดวันที่จากมุมมองของ OpenLayers แทนที่จะแยกในบรรทัดวันที่คุณควรแยกค่า X ขั้นต่ำของคุณ

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

ฉันได้สร้างตัวอย่างที่นี่เพื่อแยกแทร็กดาวเทียมของคุณออกเป็นสองคุณลักษณะ: http://jsfiddle.net/6XJ5A/

ตอนนี้เพื่อใช้ WKT ที่มีหลายบรรทัดในการอัปเดตของคุณแทนที่จะใช้เป็นเส้นตรงคุณจะต้องผ่านชุดข้อมูลทั้งหมดและสร้างสายแยกของคุณพร้อมพิกัดทั้งหมดที่ข้ามเส้นข้อมูล โดยการสร้างเส้นเล็ก ๆ ภายในเส้นหลายเส้นคุณสามารถแบ่งพิกัดทั้งหมดที่ควรข้ามเส้นบอกแนว นี่คือตัวอย่างที่อัปเดต: http://jsfiddle.net/Jc274/

และรหัส:

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

สิ่งนี้จะส่งคืนคุณเป็นเส้นแบ่งในทุกจุดที่ "ตัด" เส้นข้อมูล

โปรดทราบว่าฉันยังวนรอบพิกัดเพื่อลบเส้นที่ผ่านแผนที่เพื่อเชื่อมต่อ 2 พิกัด:

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

อัปเดต: ฉันอัปเดตตัวอย่างแรกเพื่อเพิ่มเฉพาะบรรทัดที่แบ่งเท่านั้น ฉันยังปรับปรุงคำอธิบายตาม วิธีนี้ไม่ได้เป็นการพิสูจน์ด้วยแทร็กดาวเทียม 24 ชั่วโมงที่คุณให้ แต่ฉันกำลังทำอยู่

อัปเดต 2: ฉันอัปเดตตัวอย่างที่สอง ด้วยการใช้ Multiline ในการแยกและวนลูปผ่านผลลัพธ์เพื่อลบพิกัดพิเศษที่เพิ่มโดยการแบ่งเราได้รับชุดของคุณสมบัติที่ไม่เคยข้ามไปในวันที่


+1 สำหรับความพยายาม แต่ตัวอย่างแผนที่ของคุณดูเหมือนจะไม่สามารถแก้ปัญหาได้ แทร็กดาวเทียมบนแผนที่ตัวอย่างของคุณยังคงข้ามโลกทั้งโลกแทนที่จะไปตามเส้นทางธรรมชาติของแทร็กดาวเทียม ฉันต้องคิดถึงอะไรบางอย่าง?
CaptDragon

คุณอาจเข้าใจผิดปัญหาเพราะ..., -178.475596 -81.673196, 156.248392 -81.611421,...ข้ามเส้นข้อมูลอย่างแน่นอน ดูที่นี่
CaptDragon

ให้ฉันอัปเดตรหัสและคำอธิบายของฉัน ฉันรู้ว่ามันควรข้ามบรรทัดข้อมูล แต่ OpenLayers ไม่สนับสนุนสิ่งนี้ จากมุมมองของ OL มันไม่ได้ข้ามเส้นข้อมูล
Julien-Samuel Lacroix

ถูกตัอง. นั่นแหละปัญหา. ฉันจะต้องหลอก OpenLayers และแยกแถวเพื่อให้มันไปจนถึงขอบแล้วดำเนินการต่อในอีกด้านที่มันควรจะ
CaptDragon

2

ฉันพบวิธีแก้ปัญหาที่ยอดเยี่ยมเกี่ยวกับ Github ของ @Dane มันเรียกว่าArc.jsและใช้สำหรับการคำนวณเส้นทางวงเวียนใหญ่ ไม่เพียงแค่นั้นมันจะแบ่งบรรทัดบน dateline และให้คุณสอง linestrings ที่ตรงกับที่ dateline ซึ่ง OpenLayers สามารถแมปได้อย่างง่ายดาย ฉันหวังว่าเขาจะมาเพื่อรับรางวัล

นี่คือผลลัพธ์ของฉัน:

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่


1

splitWithฟังก์ชั่นไม่ทราบเกี่ยวกับรูปทรง 3 มิติของโลก มันทำงานในโลก 2 มิติเท่านั้น ในกรณีของคุณทั้งหมดของคุณLINESTRINGพิกัด X อยู่ระหว่าง -180 และ 180 ดังนั้นจากมุมมองสองมิติ OpenLayers' สตริงสายไม่จริงไม่ข้ามแยกรูปทรงเรขาคณิตของคุณ (สายวัน) nullและมันจะบอกคุณได้โดยการกลับมา

ฉันเชื่อว่าคุณจะต้องเขียนโค้ดที่กำหนดเองเพื่อทำการแยก ฉันจินตนาการถึงอัลกอริทึมที่วนรอบจุดยอดของคุณสร้างสายอักขระเอาท์พุทแบบนี้

  • สำหรับจุดยอดคู่ที่อยู่ติดกันแต่ละคู่จะตัดสินว่าส่วนที่อยู่ระหว่างพวกเขาข้ามเส้นวันที่หรือไม่
  • หากไม่เป็นเช่นนั้นให้รักษาเซ็กเมนต์ดังกล่าวและเพิ่มเข้าไปในสตริงบรรทัดเอาต์พุต "ปัจจุบัน"
  • ถ้าเป็นเช่นนั้นแบ่งส่วนออกเป็นสองส่วน เพิ่มส่วนหนึ่งลงในสตริง "ปัจจุบัน" เริ่มต้นใหม่ "ปัจจุบัน" สตริงสตริงและเพิ่มส่วนอื่น ๆ ไปยังใหม่นี้

หนึ่งฮิวริสติกที่สมเหตุสมผลสำหรับการพิจารณาว่าคู่ของจุดยอดตัดข้ามเส้นวันที่เพื่อดูว่าความแตกต่างระหว่างพิกัด X นั้นมากกว่า 180 องศาหรือไม่ (แม้ว่าสิ่งนี้อาจทำให้เกิดความผิดพลาดได้ตัวอย่างเช่นในเขตขั้วโลกบางทีคุณอาจโชคดีพอที่ไม่มีละติจูดสูงมาก)

การทำงานของการแบ่งเซกเมนต์ออกเป็นสองส่วนอาจเป็นเรื่องง่ายเหมือนกับการสอดแทรกเชิงเส้น (ถ้าคุณไม่สนใจเกี่ยวกับความแม่นยำของแทร็กมากเกินไป) เมื่อคุณตรวจพบว่าส่วนข้ามเส้นวันที่ที่คุณทำสำเนาของจุดสุดยอดที่สองและย้ายพิกัด X ของมัน (โดยการเพิ่มหรือลบ 360 องศา) จากนั้นสอดแทรกพิกัด Y

แก้ไข : นี่คือ JSFiddle ที่แสดงให้เห็นถึงอัลกอริทึมข้างต้นในข้อมูลของคุณ: http://jsfiddle.net/85vjS/


0

ถ้ามันใช้ได้กับกรีนนิชนั่นเป็นเพราะคุณอยู่ในขอบเขตของ CRS ของคุณ ดังนั้นฉันจะแนะนำวิธีแก้ปัญหาแบบเดียวกับในโพสต์ที่คุณกำลังชี้ไปที่:

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

และอาจจะ

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

สำหรับด้านอื่น ๆ

อีกวิธีหนึ่งคือการทำงานใน CRS ที่ไม่ได้ "ออกจากขอบเขต" ที่วันที่ จากนั้นคุณควรจะสามารถแยกข้อมูลของคุณได้โดยไม่มีปัญหา


ฉันลองทั้งสองอย่างแล้วLINESTRING(179.99 -89, 179.99 89)และLINESTRING(-179.99 -89, -179.99 89)ไม่เป็นประโยชน์ เกี่ยวกับ CRS แต่น่าเสียดายที่นี่ไม่สามารถใช้งานได้เพราะฉันทำแผนที่แทร็กดาวเทียมที่เดินทางรอบโลกหลายครั้ง ดังนั้น CRS ทั้งหมดจะถูกแยกบางแห่งและฉันจะมีปัญหาเดียวกันทุกที่ที่ฉันแยกมัน ขอบคุณสำหรับข้อมูลของคุณ
CaptDragon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.