问题
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