D3.js 给定geoJSON对象,将地图居中放置在d3中
目前在d3中,如果你要绘制一个geoJSON对象,你必须缩放并转换它,以使其达到你想要的大小,并转换它以使其居中。这是一项非常繁琐的反复试验的任务,我想知道是否有人知道获得这些值的更好方法 例如,如果我有这个代码D3.js 给定geoJSON对象,将地图居中放置在d3中,d3.js,geojson,topojson,D3.js,Geojson,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").app
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");
});
我他妈的怎么才能不经过一点一点地获得.scale(8500)和.translate([0,-1200])呢?有一种方法可以接受lat/lon对
据我所知,translate()只用于移动贴图的像素。我不知道如何确定比例。要平移/缩放地图,您应该查看将SVG覆盖在传单上。这将比转换SVG容易得多。请参见此示例,然后以下内容似乎可以大致满足您的要求。缩放似乎还可以。将其应用于我的地图时,有一个小偏移。这个小偏移可能是因为我使用了“平移”命令使地图居中,而我可能应该使用“居中”命令
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")
});
var宽度=300;
var高度=400;
var-vis=d3.选择(“vis”).追加(“svg”)
.attr(“宽度”,宽度).attr(“高度”,高度)
d3.json(“nld.json”,函数(json){
//为投影创建第一个猜测
var center=d3.geo.centroid(json)
var量表=150;
变量偏移=[宽度/2,高度/2];
var projection=d3.geo.mercator().scale(比例)。center(中心)
.翻译(抵销);
//创建路径
var path=d3.geo.path().projection(projection);
//使用路径确定当前贴图的边界并使用
//这些参数用于确定比例尺和平移的更好值
var-bounds=path.bounds(json);
var hscale=标度*宽度/(界限[1][0]-界限[0][0]);
var vscale=标度*高度/(界限[1][1]-界限[0][1]);
变量标度=(hscale
我的答案与扬·范德兰的答案相近,但你可以稍微简化一些,因为你不需要计算地理质心;您只需要边界框。而且,通过使用未缩放、未转换的单位投影,可以简化数学
守则的重要部分如下:
// 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);
在单位投影中计算特征后,您可以通过比较边界框的纵横比(b[1][0]-b[0][0]
和b[1][1]-b[0][1]
)与画布的纵横比(width
和height
)来计算适当的比例。在本例中,我还将边界框缩放到画布的95%,而不是100%,因此边缘上有一点额外的空间用于笔划和周围特征或填充
然后,您可以使用边界框的中心((b[1][0]+b[0][0])/2和(b[1][1]+b[0][1])/2和画布的中心(宽度/2和高度/2)计算翻译。请注意,由于边界框位于单位投影的坐标中,因此它必须乘以比例(s
)
例如:
还有一个相关的问题,即如何在不调整投影的情况下缩放集合中的特定特征,即将投影与几何变换相结合以放大和缩小。这使用了与上述相同的原理,但数学上略有不同,因为几何变换(SVG“transform”属性)与地理投影相结合
例如:
我在互联网上四处寻找一种无需大惊小怪的方法来将我的地图居中,并从扬·范德兰和姆博斯托克的答案中获得了灵感。如果您使用svg容器,这里有一种使用jQuery的简单方法。我为填充/边框等创建了95%的边框
var width = $("#container").width() * 0.95,
height = $("#container").width() * 0.95 / 1.9 //using height() doesn't work since there's nothing inside
var projection = d3.geo.mercator().translate([width / 2, height / 2]).scale(width);
var path = d3.geo.path().projection(projection);
var svg = d3.select("#container").append("svg").attr("width", width).attr("height", height);
如果你想要精确的缩放,这个答案对你来说是行不通的。但如果像我一样,您希望显示集中在容器中的地图,这就足够了。我试图显示墨卡托地图,发现这种方法对集中我的地图很有用,而且我可以很容易地切断南极部分,因为我不需要它。我是d3新手-将尝试解释我是如何理解它的,但我不确定我是否一切都正确
秘密在于知道一些方法将在地图空间(纬度、经度)上操作,而另一些方法将在笛卡尔空间(屏幕上的x、y)上操作。地图空间(我们的星球)是(几乎)球形的,笛卡尔空间(屏幕)是平面的——为了将一个空间映射到另一个空间,你需要一个算法,称为。这个空间太短,无法深入探讨投影这一迷人的主题,以及它们如何扭曲地理特征,从而将球面变成平面;以此类推——总是有一个折衷方案(迈克·博斯托克有一个折衷方案)
在d3中,投影对象有一个中心属性/设置器,以地图单位表示:
b = d3.geo.bounds(feature);
投影中心([
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);
b = d3.geo.bounds(feature);
projection.center([(b[1][0]+b[0][0])/2, (b[1][1]+b[0][1])/2]);
projection.translate([width/2, height/2]);
var bounds = path.bounds(topoJson),
dx = Math.abs(bounds[1][0] - bounds[0][0]),
dy = Math.abs(bounds[1][1] - bounds[0][1]),
x = (bounds[1][0] + bounds[0][0]),
y = (bounds[1][1] + bounds[0][1]);
if(dx > 1){
var center = d3.geo.centroid(topojson.feature(json, json.objects[topoObj]));
scale = height / dy * 0.85;
console.log(scale);
projection = projection
.scale(scale)
.center(center)
.translate([ width/2, height/2]);
}else{
scale = 0.85 / Math.max( dx / width, dy / height );
offset = [ (width - scale * x)/2 , (height - scale * y)/2];
// new projection
projection = projection
.scale(scale)
.translate(offset);
}
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);
// adjust projection
var bounds = path.bounds(json);
offset[0] = offset[0] + (width - bounds[1][0] - bounds[0][0]) / 2;
offset[1] = offset[1] + (height - bounds[1][1] - bounds[0][1]) / 2;
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")
});
var projection = d3.geo.albersUsa();
var path = d3.geo.path()
.projection(projection);
var tracts = topojson.feature(mapdata, mapdata.objects.tx_counties);
projection
.scale(1)
.translate([0, 0]);
var b = path.bounds(tracts),
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];
projection
.scale(s)
.translate(t);
svg.append("path")
.datum(topojson.feature(mapdata, mapdata.objects.tx_counties))
.attr("d", path)
var projection = d3.geoMercator().fitSize([width, height], geojson);
var path = d3.geoPath().projection(projection);
g.selectAll('path')
.data(geojson.features)
.enter()
.append('path')
.attr('d', path)
.style("fill", "red")
.style("stroke-width", "1")
.style("stroke", "black");