问题描述
我正在尝试将this globe的大小设置为200 x 200px。
我了解到该投影的当前尺寸为960 x 500像素。
改变SVG的大小不会缩小地球范围。我无法理解原因。
var width = 200;
var height = 200;
和
const width = 200;
const height = 200;
和
const svg = d3.select('svg')
.attr('width',200).attr('height',200);
我该如何最好地解决这个问题?我在做什么错了?
我的代码:
<!DOCTYPE html>
<html>
<body>
<svg></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script>
const width = 960;
const height = 500;
const config = {
speed: 0.005,verticalTilt: -20,horizontalTilt: 0
}
let locations = [];
const svg = d3.select('svg')
.attr('width',width).attr('height',height);
const markerGroup = svg.append('g');
const projection = d3.geoOrthographic();
const initialScale = projection.scale();
const path = d3.geoPath().projection(projection);
const center = [width/2,height/2];
drawGlobe();
drawGraticule();
enableRotation();
function drawGlobe() {
d3.queue()
.defer(d3.json,'world-110m.json')
.defer(d3.json,'locations.json')
.await((error,worldData,locationData) => {
svg.selectAll(".segment")
.data(topojson.feature(worldData,worldData.objects.countries).features)
.enter().append("path")
.attr("class","segment")
.attr("d",path)
.style("stroke","silver")
.style("stroke-width","1px")
.style("fill",(d,i) => 'silver')
.style("opacity",".5");
locations = locationData;
drawMarkers();
});
}
function drawGraticule() {
const graticule = d3.geoGraticule()
.step([10,10]);
svg.append("path")
.datum(graticule)
.attr("class","graticule")
.attr("d",path)
.style("fill","#fff")
.style("stroke","#ececec");
}
function enableRotation() {
d3.timer(function (elapsed) {
projection.rotate([config.speed * elapsed - 120,config.verticalTilt,config.horizontalTilt]);
svg.selectAll("path").attr("d",path);
drawMarkers();
});
}
function drawMarkers() {
const markers = markerGroup.selectAll('circle')
.data(locations);
markers
.enter()
.append('circle')
.merge(markers)
.attr('cx',d => projection([d.longitude,d.latitude])[0])
.attr('cy',d.latitude])[1])
.attr('fill',d => {
const coordinate = [d.longitude,d.latitude];
gdistance = d3.geodistance(coordinate,projection.invert(center));
return gdistance > 1.57 ? 'none' : 'tomato';
})
.attr('r',7);
markerGroup.each(function () {
this.parentNode.appendChild(this);
});
}
</script>
</body>
</html>
解决方法
Projection.scale()
投影的比例决定了投影世界的大小。一般来说,d3投影具有默认的比例值,该比例值将填充960x500 SVG /画布。使用d3.geoOrthographic生成的地图没有长边,因此为500x500像素。默认比例值是:249.5-宽度/高度的一半(允许笔触宽度)。此比例因子在宽度和高度上都是线性的:将其加倍,并将其加倍(世界投影尺寸的四倍)。因此,如果您想要200x200像素的世界,则需要:99.5作为比例值。
这是d3.geoOrthographic的默认设置,其他比例尺具有其他比例尺默认值。例如,对于墨卡托来说,它是480 /π:在960像素宽度上的经度为2π。
Projection.translate()
但是,如果您更改200x200像素世界的比例,则默认投影转换会出现问题。默认情况下,此值设置为[250,480]-[500,960]的一半,即SVG /画布的默认D3预期大小。此坐标是投影地理中心(默认为0°N,0°W)的投影位置。您需要将此值更改为[100,100]:SVG /画布的中心。
解决方案
const projection = d3.geoOrthographic()
.scale(99.5)
.translate([100,100]);
自动解决方案
有一种更简单的方法,但是了解机械原理可能会有用。
projection.fitSize()
/ .fitExtent()
既可以设置比例尺,也可以根据指定的宽度/高度/范围自动平移。对于您来说,这很容易手动解决,但是您也可以使用:
d3.geoOrthographic()
.fitSize([width,height],geoJsonObject)
或
d3.geoOrthographic()
.fitExtent([[left,top],[right,bottom]],geojsonObject)
在使用topojson时:topojson.feature
返回一个geojson对象(具有包含各个要素的features属性-无法将要素数组传递给fitSize或fitExtent)。