cli*_*oid 133 geojson d3.js topojson
目前在d3中,如果您要绘制一个geoJSON对象,则必须对其进行缩放并对其进行翻译,以使其达到所需大小并将其翻译以使其居中.这是一个非常乏味的反复试验,我想知道是否有人知道更好的方法来获得这些价值观?
所以,例如,如果我有这个代码
var path, vis, xy;
xy = d3.geo.mercator().scale(8500).translate([0, -1200]);
path = d3.geo.path().projection(xy);
vis = d3.select("#vis").append("svg:svg").attr("width", 960).attr("height", 600);
d3.json("../../data/ireland2.geojson", function(json) {
return vis.append("svg:g")
.attr("class", "tracts")
.selectAll("path")
.data(json.features).enter()
.append("svg:path")
.attr("d", path)
.attr("fill", "#85C3C0")
.attr("stroke", "#222");
});
Run Code Online (Sandbox Code Playgroud)
到底如何获得.scale(8500)和.translate([0,-1200]),而一点一点去?
mbo*_*ock 165
我的答案接近Jan van der Laan,但你可以稍微简化一下,因为你不需要计算地理质心; 你只需要边界框.并且,通过使用未缩放的,未翻译的单位投影,您可以简化数学运算.
代码的重要部分是:
// Create a unit projection.
var projection = d3.geo.albers()
.scale(1)
.translate([0, 0]);
// Create a path generator.
var path = d3.geo.path()
.projection(projection);
// Compute the bounds of a feature of interest, then derive scale & translate.
var b = path.bounds(state),
s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
// Update the projection to use computed scale & translate.
projection
.scale(s)
.translate(t);
Run Code Online (Sandbox Code Playgroud)
在单位投影中对要素的边界框进行复合后,可以通过比较边界框(和)的纵横比与画布(和)的纵横比来计算适当的比例.在这种情况下,我还将边界框缩放到95%的画布,而不是100%,因此在笔迹和周围特征或填充的边缘上有一点额外的空间.b[1][0] - b[0][0]
b[1][1] - b[0][1]
width
height
然后你就可以计算转换使用边界框(中心(b[1][0] + b[0][0]) / 2
和(b[1][1] + b[0][1]) / 2
)和画布(中央width / 2
和height / 2
).请注意,由于边界框位于单位投影的坐标中,因此必须乘以scale(s
).
有一个相关的问题,其中它是如何放大到一个特定的功能集合在无需调整投影,即,投影用几何变换组合以放大和缩小.它使用与上述相同的原理,但数学略有不同,因为几何变换(SVG"变换"属性)与地理投影相结合.
Jan*_*aan 126
以下似乎大概做了你想要的.缩放似乎没问题.将它应用到我的地图时会有一个小的偏移量.这个小偏移可能是因为我使用translate命令使地图居中,而我应该使用center命令.
在代码中:
var width = 300;
var height = 400;
var vis = d3.select("#vis").append("svg")
.attr("width", width).attr("height", height)
d3.json("nld.json", function(json) {
// create a first guess for the projection
var center = d3.geo.centroid(json)
var scale = 150;
var offset = [width/2, height/2];
var projection = d3.geo.mercator().scale(scale).center(center)
.translate(offset);
// create the path
var path = d3.geo.path().projection(projection);
// using the path determine the bounds of the current map and use
// these to determine better values for the scale and translation
var bounds = path.bounds(json);
var hscale = scale*width / (bounds[1][0] - bounds[0][0]);
var vscale = scale*height / (bounds[1][1] - bounds[0][1]);
var scale = (hscale < vscale) ? hscale : vscale;
var offset = [width - (bounds[0][0] + bounds[1][0])/2,
height - (bounds[0][1] + bounds[1][1])/2];
// new projection
projection = d3.geo.mercator().center(center)
.scale(scale).translate(offset);
path = path.projection(projection);
// add a rectangle to see the bound of the svg
vis.append("rect").attr('width', width).attr('height', height)
.style('stroke', 'black').style('fill', 'none');
vis.selectAll("path").data(json.features).enter().append("path")
.attr("d", path)
.style("fill", "red")
.style("stroke-width", "1")
.style("stroke", "black")
});
Run Code Online (Sandbox Code Playgroud)
Pau*_*ine 51
我是d3的新手 - 将尝试解释我是如何理解的,但我不确定我是否做得对.
秘诀是知道某些方法将在制图空间(纬度,经度)和笛卡尔空间(屏幕上的x,y)上进行操作.制图空间(我们的星球)是(几乎)球形,笛卡尔空间(屏幕)是平坦的 - 为了将一个映射到另一个,你需要一个算法,称为投影.这个空间太短,无法深入到投影的迷人主题中,以及它们如何扭曲地理特征以便将球形转变为平面; 一些设计用于保存角度,另一些设计用于保存距离等等 - 总是存在折衷(Mike Bostock有大量示例).
在d3中,投影对象具有中心属性/ setter,以地图单位给出:
projection.center([位置])
如果指定了center,则将投影的中心设置为指定位置,以度为单位的经度和纬度的双元素数组并返回投影.如果未指定center,则返回当前中心,默认为⟨0°,0°⟩.
还有以像素为单位的平移 - 投影中心相对于画布的位置:
projection.translate([点])
如果指定了point,则将投影的平移偏移设置为指定的双元素数组[x,y]并返回投影.如果未指定point,则返回当前平移偏移,默认为[480,250].平移偏移确定投影中心的像素坐标.默认平移偏移在960×500区域的中心放置⟨0°,0°..
当我想在画布中居中显示一个特征时,我喜欢将投影中心设置为特征边界框的中心 - 当我的国家(巴西)使用mercator(WGS 84,用于谷歌地图)时,这适用于我,从未使用其他投影和半球进行测试.您可能需要对其他情况进行调整,但如果您确定了这些基本原则,那么您将没事.
例如,给定投影和路径:
var projection = d3.geo.mercator()
.scale(1);
var path = d3.geo.path()
.projection(projection);
Run Code Online (Sandbox Code Playgroud)
该bounds
方法以像素为单位path
返回边界框.使用它来查找正确的比例,将像素的大小与地图单位的大小进行比较(0.95给出的宽度或高度最佳拟合度为5%).这里的基本几何,计算对角线对角的矩形宽度/高度:
var b = path.bounds(feature),
s = 0.9 / Math.max(
(b[1][0] - b[0][0]) / width,
(b[1][1] - b[0][1]) / height
);
projection.scale(s);
Run Code Online (Sandbox Code Playgroud)
使用此d3.geo.bounds
方法以地图单位查找边界框:
b = d3.geo.bounds(feature);
Run Code Online (Sandbox Code Playgroud)
将投影的中心设置为边界框的中心:
projection.center([(b[1][0]+b[0][0])/2, (b[1][1]+b[0][1])/2]);
Run Code Online (Sandbox Code Playgroud)
使用此translate
方法将地图的中心移动到画布的中心:
projection.translate([width/2, height/2]);
Run Code Online (Sandbox Code Playgroud)
到目前为止,你应该让地图中心的特征放大5%边距.
dnl*_*tsk 44
随着d3 v4,它越来越容易!
var projection = d3.geoMercator().fitSize([width, height], geojson);
var path = d3.geoPath().projection(projection);
Run Code Online (Sandbox Code Playgroud)
最后
g.selectAll('path')
.data(geojson.features)
.enter()
.append('path')
.attr('d', path)
.style("fill", "red")
.style("stroke-width", "1")
.style("stroke", "black");
Run Code Online (Sandbox Code Playgroud)
享受,干杯