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

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

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

Кажется, что этот плавный переход нарушен в 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);

}

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

1 ответ

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

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

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

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

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