Неопределенные данные с использованием D3-тайла в React/Dash

Я пытаюсь продублировать этот пример карты d3 в React, чтобы использовать его в качестве компонента в Plotly Dash. Однако есть проблема (я думаю, с D3-плиткой), которая приводит к неопределенным строкам в URL-адресах opnestreemap. Это препятствует тому, чтобы код захватил фактические изображения для плиток, и приводит к следующему изображению:

Ошибки, возникающие при увеличении масштаба, выглядят так:

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

Вот полный код MyMap.react.js. Кажется, что ошибка происходит из-за того, что переменная плиток не заполнена данными, но я не уверен, какова причина этого. Любая помощь будет принята с благодарностью!

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import * as d3 from 'd3';
import {mesh} from 'topojson-client';
import * as d3Tile from 'd3-tile';

function createGeoMap(divId) {

    var pi = Math.PI,
        tau = 2 * pi;

    var width = Math.max(960, window.innerWidth),
        height = Math.max(500, window.innerHeight);

    // Initialize the projection to fit the world in a 1×1 square centered at the origin.
    var projection = d3.geoMercator()
        .scale(1 / tau)
        .translate([0, 0]);

    var path = d3.geoPath()
        .projection(projection);

    var tile = d3Tile.tile()
        .size([width, height]);

    var zoom = d3.zoom()
        .scaleExtent([1 << 11, 1 << 14])
        .on('zoom', zoomed);

    var svg = d3.select('#' + divId).append('svg')
        .attr('width', width)
        .attr('height', height);

    var raster = svg.append('g');

    var vector = svg.append('path');

    d3.json('https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/us.json', function(error, us) {
      if (error) throw error;

      vector
          .datum(mesh(us, us.objects.states));

      // Compute the projected initial center.
      var center = projection([-98.5, 39.5]);

      // Apply a zoom transform equivalent to projection.{scale,translate,center}.
      svg
          .call(zoom)
          .call(zoom.transform, d3.zoomIdentity
              .translate(width / 2, height / 2)
              .scale(1 << 12)
              .translate(-center[0], -center[1]));
    });

    function zoomed() {
      var transform = d3.event.transform;

      var tiles = tile
          .scale(transform.k)
          .translate([transform.x, transform.y])
          ();

      projection
          .scale(transform.k / tau)
          .translate([transform.x, transform.y]);

      vector
          .attr('d', path);

      var image = raster
          .attr('transform', stringify(tiles.scale, tiles.translate))
        .selectAll('image')
        .data(tiles, function(d) { return d; });

      image.exit().remove();

      image.enter().append('image')
          .attr('xlink:href', function(d) { return 'http://' + 'abc'[d[1] % 3] + '.tile.openstreetmap.org/' + d[2] + '/' + d[0] + '/' + d[1] + '.png'; })
          .attr('x', function(d) { return d[0] * 256; })
          .attr('y', function(d) { return d[1] * 256; })
          .attr('width', 256)
          .attr('height', 256);
    }

    function stringify(scale, translate) {
      var k = scale / 256, r = scale % 1 ? Number : Math.round;
      return 'translate(' + r(translate[0] * scale) + ',' + r(translate[1] * scale) + ') scale(' + k + ')';
    }
}

export default class MyMap extends Component {

    constructor() {
        super();
        this.plot = this.plot.bind(this);
    }

    plot(props) {
        createGeoMap(props.id);
    }

    componentDidMount() {
        this.plot(this.props);
    }

    shouldComponentUpdate() {
        return false;
    }

    componentWillReceiveProps(newProps) {
        if(newProps.id !== this.props.id) {
            this.plot(newProps);
        }
    }

    render() {
        const {id} = this.props;
        return (
            <div id={id} />
        );
    }
}

MyMap.propTypes = {
    /**
     * The ID used to identify this compnent in Dash callbacks
     */
    id: PropTypes.string,

};

1 ответ

Решение

вопрос

Похоже, что документация и связанные примеры для плитки d3 используют v0.0.3; однако использование 0.0.4 нарушает документацию и примеры (см. этот отчет о проблеме). Поскольку создается впечатление, что вы используете один из примеров, на которые документация ссылается как шаблон, ваш код будет поврежден при использовании новейшей версии d3.tile.

Вы можете увидеть симптом этого перерыва, посмотрев запрашиваемые листы, как вы заметили, вы запрашиваете изображение примерно так для каждой плитки:

"http://undefined.tile.openstreetmap.org/undefined/undefined/undefined.png"

Изменения в v0.0.4

В v0.0.3 массивом, представляющим значения x,y,z для каждой плитки, был создан d3.tile: [1,2,3]

В v0.0.4 объект со свойствами для значений x,y,z создается с помощью d3.tile: {x:1,y:2,z:3}

исправлять

Таким образом, вы можете изменить:

  return 'http://' + 'abc'[d[1] % 3] + '.tile.openstreetmap.org/' + d[2] + '/' + d[0] + '/' + d[1] + '.png' 

к

return 'http://' + 'abc'[d.y % 3] + '.tile.openstreetmap.org/' + d.z + '/' + d.x + '/' + d.y + '.png'; })

И, конечно, атрибуты x, y каждой плитки должны быть установлены в d.x * 256 а также d.y * 256 а не d[0] * 256 а также d[1] * 256,

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