如何调整 d3.js 球体的大小?
How to adjust the size of a d3.js globe?
我正在尝试将 this globe 的大小设置为 200 x 200 像素。
我了解到投影的当前大小为 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 => projection([d.longitude, 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/Canvas。使用 d3.geoOrthographic 生成的地图没有长边,因此这是 500x500 像素。默认比例值为:249.5 - width/height 的一半(允许笔划宽度)。这个比例因子在宽度和高度上都是线性的:加倍它和加倍两者(世界投影大小的四倍)。因此,如果您想要一个 200x200 像素的世界,您需要:99.5 作为您的比例值。
这是d3.geoOrthographic的默认值,其他比例有其他比例默认值。例如,对于墨卡托,它是 480/π:跨 960 像素宽度的经度 2π。
Projection.translate()
但是,如果您更改 200x200 像素世界的比例,您将遇到默认投影转换的问题。默认设置为 [250,480] - [500,960] 的一半,默认 D3 预期大小 SVG/Canvas。此坐标是投影的地理中心(默认为 0°N,0°W)投影到的位置。您需要将其更改为 [100,100] 的值:SVG/Canvas.
的中心
解决方案
const projection = d3.geoOrthographic()
.scale(99.5)
.translate([100,100]);
Automagic 解决方案
有一种更简单的方法,但了解其机制可能会有用。
projection.fitSize()
/.fitExtent()
均根据指定的 width/height / 范围自动设置比例和平移。在您的情况下,这很容易手动解决,但您也可以使用:
d3.geoOrthographic()
.fitSize([width,height],geoJsonObject)
或
d3.geoOrthographic()
.fitExtent([[left,top],[right,bottom]],geojsonObject)
当您使用 topojson 时:topojson.feature
returns geojson 对象(具有包含单个特征的特征 属性 - 特征数组不能传递给 fitSize 或fitExtent).
我正在尝试将 this globe 的大小设置为 200 x 200 像素。
我了解到投影的当前大小为 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 => projection([d.longitude, 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/Canvas。使用 d3.geoOrthographic 生成的地图没有长边,因此这是 500x500 像素。默认比例值为:249.5 - width/height 的一半(允许笔划宽度)。这个比例因子在宽度和高度上都是线性的:加倍它和加倍两者(世界投影大小的四倍)。因此,如果您想要一个 200x200 像素的世界,您需要:99.5 作为您的比例值。
这是d3.geoOrthographic的默认值,其他比例有其他比例默认值。例如,对于墨卡托,它是 480/π:跨 960 像素宽度的经度 2π。
Projection.translate()
但是,如果您更改 200x200 像素世界的比例,您将遇到默认投影转换的问题。默认设置为 [250,480] - [500,960] 的一半,默认 D3 预期大小 SVG/Canvas。此坐标是投影的地理中心(默认为 0°N,0°W)投影到的位置。您需要将其更改为 [100,100] 的值:SVG/Canvas.
的中心解决方案
const projection = d3.geoOrthographic()
.scale(99.5)
.translate([100,100]);
Automagic 解决方案
有一种更简单的方法,但了解其机制可能会有用。
projection.fitSize()
/.fitExtent()
均根据指定的 width/height / 范围自动设置比例和平移。在您的情况下,这很容易手动解决,但您也可以使用:
d3.geoOrthographic()
.fitSize([width,height],geoJsonObject)
或
d3.geoOrthographic()
.fitExtent([[left,top],[right,bottom]],geojsonObject)
当您使用 topojson 时:topojson.feature
returns geojson 对象(具有包含单个特征的特征 属性 - 特征数组不能传递给 fitSize 或fitExtent).