我对D3来说还很陌生,所以很抱歉,如果这与其他帖子无关。我正在尝试绘制一个带有用户通过单击小节图选择的点的地图。目前,我有一张世界地图,这些点根据单击的条进入和退出。我想知道如何还包含一个缩放效果,该效果会裁剪地球,使其仅包括所选条形的点区域。例如:第一个栏应显示美利坚合众国的地图。
var data = [{
"name": "Apples",
"value": 20,
"latlong": [
{"latitude": 32.043478, "longitude": -110.7851017},
{"latitude": 40.49, "longitude": -106.83},
{"latitude": 39.1960652, "longitude": -120.2384172},
{"latitude": 36.137076, "longitude": -81.183722},
{"latitude": 35.1380976, "longitude": -90.0611644},
{"latitude": 33.76875, "longitude": -84.376217},
{"latitude": 32.867153, "longitude": -79.9678813},
{"latitude": 39.61078, "longitude": -78.79830099999},
{"latitude": 40.8925, "longitude": -89.5074},
{"latitude": 44.1862, "longitude": -85.8031},
{"latitude": 35.48759154, "longitude": -86.10236359},
{"latitude": 37.9342807, "longitude": -107.8073787999},
{"latitude": 41.3530864, "longitude": -75.6848074},
{"latitude": 38.423137, "longitude": -80.021118},
{"latitude": 43.5121, "longitude": -72.4021},
{"latitude": 48.4070083, "longitude": -114.2827366}
]
},
{
"name": "Oranges",
"value": 26,
"latlong": [
{"latitude": -36.8506158, "longitude": 174.7678785},
{"latitude": -27.4510454, "longitude": 153.0319808},
{"latitude": -33.867111, "longitude": 151.217941},
{"latitude": -34.8450381, "longitude": 138.4985548},
{"latitude": -37.7928386, "longitude": 144.9051327},
{"latitude": -32.0582947, "longitude": 115.7460244},
{"latitude": 29.934926599999, "longitude": -90.0615085},
{"latitude": -34.4829472, "longitude": -58.518548},
{"latitude": -33.460464, "longitude": -70.656868},
{"latitude": 4.8007362, "longitude": -74.0373992},
{"latitude": 4.9375556, "longitude": -73.9649426},
{"latitude": -23.701185, "longitude": -46.7001431},
{"latitude": 33.678023, "longitude": -116.23754},
{"latitude": 51.8451208, "longitude": 5.6872547},
{"latitude": 40.3688321, "longitude": -3.6866294},
{"latitude": 40.4817271, "longitude": -3.6330802},
{"latitude": 40.4642, "longitude": -3.6131},
{"latitude": 52.327353537, "longitude": 1.67658117421},
]
}
];
//set up svg using margin conventions - we'll need plenty of room on the left for labels
var margin = {
top: 15,
right: 25,
bottom: 15,
left: 60
};
var width = 400 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("#graphic").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scale.linear()
.range([0, width])
.domain([0, d3.max(data, function (d) {
return d.value;
})]);
var y = d3.scale.ordinal()
.rangeRoundBands([height, 0], .1)
.domain(data.map(function (d) {
return d.name;
}));
//make y axis to show bar names
var yAxis = d3.svg.axis()
.scale(y)
//no tick marks
.tickSize(0)
.orient("left");
var gy = svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var bars = svg.selectAll(".bar")
.data(data)
.enter()
.append("g")
//append rects
bars.append("rect")
.attr("class", "bar")
.attr("y", function (d) {
return y(d.name);
})
.attr("height", y.rangeBand())
.attr("x", 0)
.attr("width", function (d) {
return x(d.value);
})
.on("click", function (d) {
// d3.select("#chart circle")
// .attr("fill", function () { return "rgb(0, 0, " + Math.round(d.key * 10) + ")"; });
d3.selectAll('rect').style('fill', '#5f89ad');
d3.select(this).style("fill", "#012B4E");
var circle = svg_map.select("g").selectAll("circle")
.data(d.latlong);
circle.exit().remove();//remove unneeded circles
circle.enter().append("circle")
.attr("r",0);//create any new circles needed
//update all circles to new positions
circle.transition()
.duration(500)
.attr("cx", function(d){ return projection([d.longitude, d.latitude])[0] })
.attr("cy", function(d){ return projection([d.longitude, d.latitude])[1] })
.attr("r", 7)
.attr("class", "circle")
.style("fill", "#012B4E")
.attr("stroke", "#012B4E")
.style("opacity", 0.7)
.attr("r", 4)
})
/////////////////////// WORLD MAP ////////////////////////////
var width = window.innerWidth,
height = window.innerHeight,
centered,
clicked_point;
var projection = d3.geoMercator()
// .translate([width / 2.2, height / 1.5]);
var plane_path = d3.geoPath()
.projection(projection);
var svg_map = d3.select("#graphic").append("svg")
.attr("width", 900)
.attr("height", 550)
.attr("class", "map");
var g = svg_map.append("g");
var path = d3.geoPath()
.projection(projection);
// load and display the World
d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error, topology) {
g.selectAll("path")
.data(topojson.feature(topology, topology.objects.countries)
.features)
.enter()
.append("path")
.attr("d", path)
;
});
path {
stroke: #2296F3;
stroke-width: 0.25px;
fill: #f8f8ff;
}
body {
font-family: "Arial", sans-serif;
}
.bar {
fill: #5f89ad;
}
.axis {
font-size: 13px;
}
.axis path,
.axis line {
fill: none;
display: none;
}
.label {
font-size: 13px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Simple Bar chart</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
</head>
<body>
<div id="graphic"></div>
</body>
</html>
答案 0 :(得分:3)
此功能基本上可以完成您想要的操作。用例略有不同,因为用新数据重绘地图后会调用该用例,但结果是相同的。选择的数据出现在地图上,并且地图被缩放到显示在其上的点的程度。
recenter() {
console.log('recentering')
try {
// get a handle to the transform object, and reset it to the
// zoom identity (resets to zoom out all the way)
let ztrans = this.zoom.transform
let t = this.d3.zoomIdentity
this.svg.transition().duration(DURATION*2).call(ztrans,t)
// get the object and measurements to determine the virtual
// "bounding" rectangle around the directional extremes of sites
// i.e., north (topmost), east (rightmost), etc
let circles = this.g.selectAll('a > circle')
let data = circles.data()
let long = data.map(o => parseFloat(o.Longitude))
let lat = data.map(o => parseFloat(o.Latitude))
let rightmost = this.d3.max(long)
let leftmost = this.d3.min(long)
let topmost = this.d3.max(lat)
let bottommost = this.d3.min(lat)
// convert the lat/long to points in the projection
let lt = this.projection([leftmost,topmost])
let rb = this.projection([rightmost,bottommost])
// calc the gaps (east - west, south - north) in pixels
let g = rb[0]-lt[0]
let gh = rb[1]-lt[1]
// get the dimensions of the panel in which the map sits
let w = this.svg.node().parentElement.getBoundingClientRect().width
let h = this.svg.node().parentElement.getBoundingClientRect().height
// the goal here is to move the halfway point between leftmost and rightmost
// on the projection sites to the halfway point of the panel in which the
// svg element resides, and same for 'y'
// the scale is the value 90% of the scale factor of either east-west to width
// or south-north to height, whichever is greater
let neoScale = 0.9 / Math.max(g/w, gh/h)
// now recalculate what will be the difference between the current
// center and the center of the scaled virtual rectangle
// this finds the difference between the centers
// the new center of the scaled rectangle is the average of the left and right
// or top and bottom points
let neoX = w/2 - (neoScale * ((lt[0]+rb[0])/2))
let neoY = h/2 - (neoScale * ((lt[1]+rb[1])/2))
// TRANSLATE FIRST! then scale.
t = this.d3.zoomIdentity.translate(neoX,neoY).scale(neoScale)
this.svg.transition().duration(DURATION*2).call(ztrans, t)
}
catch(e)
{
console.log(e)
}
更新
我派遣了操作人员的小提琴,并创建了working fiddle,并进行了以下更改:
recenter
函数最初是在d3 v4中编写的。运营商的摆弄是在v3中。新小提琴使用的是v4。recenter
函数是从另一个项目中复制并“按原样”粘贴的。在小提琴中对其进行了修改,以说明局部变量的名称和范围。此外,点不会在第一次单击条时呈现,但会在随后的单击中显示。