Undefined data using D3-tile within React/Dash

邮差的信 提交于 2019-12-23 22:04:20

问题


I'm trying to duplicate this d3 map example within React so that I can use it as a component in Plotly Dash. However, there is an issue (I think with D3-tile) that's resulting in undefined strings in the opnestreemap urls. This prevents the code from grabbing the actual images for the tiles, and results in the following image:

The errors produced as you zoom in look like this:

Here is the full MyMap.react.js code. It seems like the error is coming from the tiles variable not being filled with data, but I'm not sure what the cause of that would be. Any help would be greatly appreciated!

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:


Issue

It appears that the documentation and linked examples for d3 tile use v0.0.3; however, using 0.0.4 breaks the documentation and examples (see this issue report). Since it appears as though you are using one of the examples that the documentation links to as a template, your code will be broken when using the newest version of d3.tile.

You can see the symptom of this break by looking the tiles you are requesting, as you note, you are requesting an image at something like this for each tile:

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

Changes in v0.0.4

In v0.0.3, an array representing the x,y,z values for each tile was produced by d3.tile: [1,2,3]

In v0.0.4, an object with properties for the x,y,z values is produced by by d3.tile: {x:1,y:2,z:3}

Fix

So you can change:

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

To

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

And of course the x,y attributes of each tile needs to be set to d.x * 256 and d.y * 256 as well, rather than d[0] * 256 and d[1] * 256.



来源:https://stackoverflow.com/questions/48429562/undefined-data-using-d3-tile-within-react-dash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!