Применить d3 геопроекцию к изображению

У меня есть изображение, о котором я знаю нижнюю левую и верхнюю правую координаты проекции меркатора (широта / долгота).

изображение текущих погодных условий в Германии

Я добавил его в Google Maps как google.maps.GroundOverlay, который дает мне правильный результат

Я добавил несколько цветовых маркеров в верхний левый (зеленый), верхний правый (красный), нижний правый (голубой) и нижний левый (красный) углы изображения.

Это все хорошо, за исключением того, что я хочу перейти с Google Maps на d3 с топойсоном, потому что d3 намного легче и функции Google Map не требуются.

Я создал карту на основе d3, включая маркеры углов изображения, вот результат:

Если я наложу два окна (браузер, который содержит карту Google Maps и браузер, который содержит карту d3), то я смогу очень точно перекрыть границы страны и угловые точки.

Вот анимированный GIF-файл, который имеет браузер d3-карты поверх браузера карт Google Maps, а прозрачность окна браузера d3-карты изменяется с помощью инструмента. Он изменяется от полностью непрозрачного до полностью прозрачного, затем снова до полностью непрозрачного. Изображение, которое показывает названия стран, является Google Maps.

Как видите, точки и границы стран очень хорошо выровнены, а радарные изображения - нет. Перемещение изображения d3 вниз немного облегчает проблему, но не является правильным решением. Я знаю, что показанный в Google Maps.

Что я делаю: на карте d3 я создаю проекцию

projection = d3.geo.mercator().scale(scale).translate([width / 2, width / 2]).center(center);

Затем создаем ограничивающий прямоугольник из представленного изображения с широкими углами.

bb = {
  east:  16.0,
  west:   4.0,
  north: 56.0,
  south: 46.0,
}

И спроецируйте их из латунного пространства в координаты x,y холста.

p_ur = projection([bb.east, bb.north]).map(function(x) { return x; })
p_ll = projection([bb.west, bb.south]).map(function(x) { return x; })
p_ul = projection([bb.west, bb.north]).map(function(x) { return x; })
p_lr = projection([bb.east, bb.south]).map(function(x) { return x; })

Когда я рисую круги, позиции очень точно совпадают с позициями на Картах Google. Это означает, что проекция работает.

circle = svg.append("svg:circle").attr('cx',p_ll[0]).attr('cy',p_ll[1]).attr('r', 2).attr("class", "ll")
circle = svg.append("svg:circle").attr('cx',p_ur[0]).attr('cy',p_ur[1]).attr('r', 2).attr("class", "ur")
circle = svg.append("svg:circle").attr('cx',p_ul[0]).attr('cy',p_ul[1]).attr('r', 2).attr("class", "ul")
circle = svg.append("svg:circle").attr('cx',p_lr[0]).attr('cy',p_lr[1]).attr('r', 2).attr("class", "lr")

Прежде чем нарисовать круги, я рисую границы страны с топойсоном.

path = d3.geo.path().projection(projection);
countries = svg.append("g");
// ...fetching the data from the web...
countries.selectAll('.country')
         .data(topojson.feature(data, data.objects.europe).features)
         .enter()
         .append('path')
         .attr('class', 'country')
         .attr('d', path);

И тут возникает проблема: я применяю изображение очень "дешево". Я проецирую левый верхний лонглонг в пространство холста x,y и использую его в качестве источника x,y для svg:image, Эти точки уже были рассчитаны для цветных кругов выше, поэтому я снова использую их здесь.

image = svg.append("svg:image")
         .attr('x', p_ul[0])
         .attr('y', p_ul[1])
         .attr('width',  (p_lr[0] - p_ul[0]))
         .attr('height', (p_lr[1] - p_ul[1]))
         .attr('preserveAspectRatio', 'none')
         .attr("xlink:href","http://.../current.png")

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

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

Но я понятия не имею, как это сделать. Есть идеи? Есть ли простой оверлейный плагин для d3.geo, родственный google.maps.GroundOverlay?

1 ответ

Карты Google используют проекцию WEB MERCATOR, которая немного отличается от проекции истинного меркатора (используется D3). (Поднято из Википедии: Web Mercator - это небольшой вариант проекции Mercator, который используется в основном в веб-картографических программах. В нем используются те же формулы, что и в стандартном Mercator, что и для небольших карт. Однако в Web Mercator используются сферические формулы во всех масштабах, тогда как в крупномасштабных картах Меркатора обычно используется эллипсоидальная форма проекции. Несоответствие незаметно в глобальном масштабе, но заставляет карты локальных областей слегка отклоняться от истинных эллипсоидальных карт Меркатора в том же масштабе. более выражен дальше от экватора и может достигать 35 км на земле.

В то время как формулы Веб-Меркатора предназначены для сферической формы Меркатора, географические координаты должны быть в эллипсоидальной системе координат WGS 84. Это несоответствие приводит к тому, что проекция слегка неконформна.

В SO- обсуждении был задан вопрос, почему изменяется масштабирование, когда в D3 включены растровые изображения. Опять же, это было связано с различиями между D3 и Mercator. Если ваше растровое изображение идеально выровнено на картах Google... оно НИКОГДА не должно выравниваться в d3 из-за тонких различий между веб-меркатором и меркатором, и для его работы я думаю, что растровое изображение должно быть (перепроецировано), и это причина, по которой вы видите его смещение на d3 против карт Google. (редактирование, чтобы заметить, что я использовал не то слово. Не масштабировано, скорее ПРОПРОЕКТировано...)

Надеюсь это немного поможет.

На самом деле здесь можно найти красивое программное обеспечение для перепроектирования, которое также имеет открытый исходный код: (часть проекта GDAL). (надеюсь, это вам немного поможет.)

Другие вопросы по тегам