我试图在 React 中复制这个 d3地图示例,以便我可以将它用作 Plotly Dash 中的组件。但是,有一个问题(我认为是 D3-tile)导致 opnestreemap url 中出现未定义的字符串。这可以防止代码抓取图块的实际图像,并产生以下图像:
放大时产生的错误如下所示:
这是完整的 MyMap.react.js 代码。似乎错误来自未填充数据的tiles变量,但我不确定原因是什么。任何帮助将不胜感激!
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,
};