Розщеплення рядків на дательні з OpenLayers


9

Пару років тому я опублікував обговорення міжнародної лінії зустрічі і @jdeolive запропонував мені розділити функції на dateLine. Тому я спробував.

Коли я намагаюся розділити свою супутникову доріжку на splitWith на дательні, я повертаюся назад null. Я знаю, що я розбиваюсь правильно, бо коли я розбиваюсь по лінії Грінвіч, я отримую очікувані результати.

Хтось знає, як я можу правильно розділити 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 години): Linestring wkt можна знайти ТУТ .

введіть тут опис зображення


Яку версію openlayers ви використовуєте?
Plux

@Plux 2.13.1 (останній)
CaptDragon

1
Відповідно до їх API, wrapDateLine слід використовувати лише на базовому шарі, тому не дивно, що він не працює на векторному шарі. Однак я не маю уявлення, як змусити це працювати на векторному шарі. У мене є подібне питання з багатополігонами, які перетинають дателіну.
Plux

1
@Plux перевіри моє рішення
CaptDragon

Приємне рішення. Це, на жаль, не стосується моєї проблеми, я вважаю, що моя проблема більше стосується геосервера, і мій багатокутник містить координати, які знаходяться на "іншій стороні" дателіни, як, наприклад, -180.00000000000003 90.00000190734869, що створює деякі проблеми в геосервері, я вважаю. У мене не виникає проблем із відображенням цього на моїй карті (у мене є WMS, який це робить), але я хочу використовувати їх як фільтри для WPS-запиту до геосервера :)
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)
);

Тут я створив приклад, який успішно розділив ваш супутниковий трек на 2 функції: 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: я оновив другий приклад. Використовуючи багаторядкову лінію для розбиття та циклічного перегляду результату для видалення додаткових координат, доданих розщепленням, ми отримуємо набір функцій, які ніколи не переходять через дателіну.


+1 для зусиль, але приклади вашої карти, мабуть, не вирішують проблему. Доріжки супутника на вашій прикладі карти все ще перетинають всю земну кулю, а не йдуть природним шляхом супутникової доріжки. Я, мабуть, щось бракує?
CaptDragon

Можливо, ви нерозумієте проблему, оскільки ..., -178.475596 -81.673196, 156.248392 -81.611421,...абсолютно перетинає дателіну. Дивіться тут
CaptDragon

Дозвольте мені оновити код та пояснення. Я знаю, що він повинен перетинати лінію передачі даних, але OpenLayers це не підтримує. З точки зору OL, це не перетинає дателіну.
Жюльєн-Самуель Лакруа

Це вірно. У цьому проблема. Мені потрібно обдурити OpenLayers і розділити лінію так, щоб вона підходила прямо до краю, а потім продовжувалася з іншого боку, де це належить.
CaptDragon

2

Я знайшов чудове рішення на github @Dane. Він називається Arc.js і призначений для обчислення маршрутів Великого кола. Мало того, вона також розділить лінію на дателіні і надасть вам два рядки, що зустрічаються в дательні, які OpenLayers можуть легко відобразити. Я сподіваюся, що він висуває претензії на винагороду.

Ось мої результати:

введіть тут опис зображення введіть тут опис зображення


1

Функція splitWith не знає про тривимірну форму Землі. Він працює лише у двовимірному світі. У вашому випадку всі ваші LINESTRINGкоординати X знаходяться в межах від -180 до 180. Отже, з двовимірної точки зору OpenLayers, рядковий рядок ніколи насправді не перетинає вашу розділену геометрію (лінія дати), і це говорить вам, повертаючись null.

Я вважаю, що вам потрібно буде написати якийсь спеціальний код, щоб зробити розділення. Я уявляю алгоритм, який перетинає вершини, будуючи рядки вихідного рядка таким чином:

  • Для кожної сусідньої пари вершин вирішують, чи відрізок між ними перетинає лінію дати.
  • Якщо цього немає, збережіть цей сегмент таким, який він є, і додайте його до "поточного" рядка вихідного рядка.
  • Якщо він робить , розділити відрізок на дві частини. Додайте одну частину до рядка рядків "поточний", запустити новий рядок рядка "поточний" та додайте іншу частину до цієї нової.

Одне розумне евристичне визначення того, чи пара вершин перетинає лінію дати, - це перевірити, чи різниця між координатами X більше 180 градусів. (Хоча це може помилитися, наприклад, у полярних регіонах. Можливо, вам пощастить не мати справді високих широт.)

Операція розбиття відрізка на дві частини може бути такою ж простою, як і лінійна інтерполяція (якщо ви не надто дбаєте про точність доріжки). Коли ви виявите, що сегмент перетинає рядок дати, ви робите копію другої вершини і переміщуєте її координату X (додаючи або віднімаючи 360 градусів), потім інтерполюємо координату Y.

EDIT : Ось 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.