Как сделать плавный переход для репроекции карты в d3 js

Я хотел бы сделать плавный переход между alber/orthographic в мини-приложении, которое я создаю, как в этом примере:

http://mbostock.github.io/d3/talk/20111018/#27

Однако кажется, что этот плавный переход нарушен в v3 с довольно прерывистым переходом путей карты:

https://www.evernote.com/shard/s236/sh/46b002bd-9c5b-4e9b-87ef-270c303eb677/2eaeebb267a3fc59df5a8447bbbcc58b/res/37917835-5aad-4509-b534-31a3e3034762/Worst_Tornado_Outbreaks_of_All_Time-20130611-074050.jpg/jpg?resizeSmall&width=832

Код довольно прост, я инициализирую карту как albers, затем запускаю ortho() для ее обновления.

function ortho() {
  var self = this, 
    h = 1000,
    w = document.width;

  this.projection = d3.geo.orthographic()
    .scale(500)
    .translate([ (w - 300) / 2, h / 2])
    .clipAngle(90)
    .rotate([90, 0, 0])
    .precision(.1);

  this.path = d3.geo.path()
    .projection(this.projection);

  //update path WITH transition
  d3.selectAll('path')
    .transition()
    .duration(900)
    .attr('d', app.path);

}

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


person benheb    schedule 11.06.2013    source источник


Ответы (1)


Если вы интерполируете путь с помощью простого интерполятора строк D3 (d3.interpolateString), , то количество координат в начальном пути и количество координат в конечном пути должны точно совпадать, в том числе в том же порядке. Но это почти никогда не происходит из-за отсечения, обрезки и изменение выборки. Возможна интерполяция формы (используя несколько стратегий), но трудно решаемая задача в общем случае. См. это объяснение (часть Переходы пути), почему простой интерполяции недостаточно.

Вместо интерполяции пути вы хотите интерполировать проекцию. Интерполяция проекции не требует точного соответствия между координатами и, следовательно, позволяет избежать артефактов интерполяции. См. эти примеры для демонстрации:

Как показано в первом примере, вот реализация, которую вы можете использовать:

function interpolatedProjection(a, b) {
  var projection = d3.geo.projection(raw).scale(1),
      center = projection.center,
      translate = projection.translate,
      α;

  function raw(λ, φ) {
    var pa = a([λ *= 180 / Math.PI, φ *= 180 / Math.PI]), pb = b([λ, φ]);
    return [(1 - α) * pa[0] + α * pb[0], (α - 1) * pa[1] - α * pb[1]];
  }

  projection.alpha = function(_) {
    if (!arguments.length) return α;
    α = +_;
    var ca = a.center(), cb = b.center(),
        ta = a.translate(), tb = b.translate();
    center([(1 - α) * ca[0] + α * cb[0], (1 - α) * ca[1] + α * cb[1]]);
    translate([(1 - α) * ta[0] + α * tb[0], (1 - α) * ta[1] + α * tb[1]]);
    return projection;
  };

  delete projection.scale;
  delete projection.translate;
  delete projection.center;
  return projection.alpha(0);
}

Создайте интерполированную проекцию, используя две проекции a и b, а затем установите для интерполированной альфы значение от 0 (для a) до 1 (для b).

person mbostock    schedule 11.06.2013
comment
Можно ли интерполировать clipAngle таким образом? Ни один из примеров не демонстрирует это — я пытаюсь перейти от орфографического/90 к равнопрямоугольному/180, и есть много странных артефактов. - person Casey; 17.06.2014