Разделение строк на дате с помощью OpenLayers

9

Пару лет назад я опубликовал обзор «Международной линии даты» и @jdeolive предложил разделить функции на линии даты. Итак, я попробовал.

Когда я пытаюсь разделить свою спутниковую дорожку с помощью 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 можно найти ЗДЕСЬ .

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

CaptDragon
источник
Какую версию openlayers вы используете?
Plux
@Plux 2.13.1 (Lastest)
CaptDragon
1
Согласно их API, wrapDateLine следует использовать только на базовом уровне, поэтому неудивительно, что он не работает на векторном уровне. Однако я понятия не имею, как заставить его работать на векторном слое. У меня похожая проблема с мультиполигонами, которые пересекают дату.
Plux
1
@Plux, проверьте мое решение
CaptDragon
Хорошее решение. К сожалению, это не применимо к моей проблеме, я считаю, что моя проблема больше связана со стороной геосервера, и мой многоугольник содержит координаты, которые находятся на «другой стороне» линии даты, например -180.00000000000003 90.00000190734869, что, как я полагаю, создает некоторые проблемы в геосервере. У меня нет проблем с отображением этого на моей карте (у меня есть wms, который это делает), но я хочу использовать их в качестве наборов фильтров в wfs-запросе к геосерверу :)
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 могут легко отобразить. Я надеюсь, что он выходит вперед, чтобы получить награду.

Вот мои результаты:

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

CaptDragon
источник
1

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

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

  • Для каждой смежной пары вершин решите, пересекает ли отрезок между ними линию даты.
  • Если это не так, оставьте этот сегмент таким, какой он есть, и добавьте его в «текущую» строку выходной строки.
  • Если это так , разделите сегмент на две части. Добавьте одну часть в строку строки «current», начните новую строку строки «current» и добавьте другую часть к этой новой строке.

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

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

РЕДАКТИРОВАТЬ : Вот JSFiddle, который демонстрирует вышеупомянутый алгоритм на ваших данных: http://jsfiddle.net/85vjS/

CSD
источник
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, который не «вышел за границы» на дату. После этого вы сможете без проблем разделить ваши данные.

radouxju
источник
Я уже попробовал оба LINESTRING(179.99 -89, 179.99 89)и LINESTRING(-179.99 -89, -179.99 89)безрезультатно. Что касается CRS, к сожалению, это не сработает для моей цели, потому что я картирую спутниковые треки, которые много раз путешествуют по миру. Таким образом, все CRS где-то разделены, и у меня будет одна и та же проблема, где бы я ни разбил ее. Спасибо за ваш вклад.
CaptDragon