首页 > 解决方案 > d3.js 地图图表绘制矩形

问题描述

我正在尝试使用 geojson 为阿联酋酋长国绘制地图。

geojson 在 geojson.io 上是有效和验证的,结果出现而不是 map a rectangle ,编写的代码在这里:

var width = 20,
height = 20;
var scal = (1 << 10) / 2 / Math.PI; 

//Define map projection 
var projection = d3.geo.mercator();
      projection
        .scale(scal)///3.5
        .translate([width/2, height/2]);

//Define path generator
var path = d3.geo.path().projection(projection);

d3.json("http://localhost/data/uae-em.json", function(error,json) {

    if(error) alert("error fetching data");
    var svg = d3.select("article")
        .append("svg")
        .attr("width",width)
        .attr("height",height);
    //draw map
    var map = svg.selectAll("path")
        .data(json.features)
        .enter()
        .append("path")
        .attr("d", path)
        .style("fill", "#3498db"); 
    });

JSON 数据在这里:https ://files.fm/f/ndsz3v85

这是geojson的一个示例功能:

{"type":"Feature","id":"4","properties":{"name":"UmmAlQwain"},"geometry":{"type":"Polygon","coordinates":[[[55.2350748,25.5700834],[55.4229709,25.3989174],[55.4236655,25.3981583],[55.423986,25.3978339],[55.4254059,25.3960785],[55.4272991,25.3935724],[55.4570596,25.3762406],[55.4577152,25.375909],[55.4915399,25.3593419],[55.4926741,25.3588472],[55.4954455,25.35793],[55.5013237,25.3571279],[55.5031779,25.3570031],[55.5390972,25.3630675],[55.5909015,25.3679027],[55.5918297,25.3680754],[55.5927443,25.3683591],[55.5935906,25.3688154],[55.5943278,25.3693581],[55.5952833,25.3702339],[55.6010034,25.3768825],[55.6044809,25.3808245],[55.6295136,25.4095735],[55.630044,25.4101151],[55.6303207,25.4106671],[55.6302516,25.411542],[55.6299137,25.4123589],[55.6293946,25.413368],[55.6287288,25.4144586],[55.6280518,25.4152536],[55.6211794,25.4201663],[55.6196221,25.4209307],[55.6177489,25.4214301],[55.605742,25.4232035],[55.6024463,25.4237462],[55.5975759,25.4244245],[55.5956543,25.4249531],[55.5919637,25.4262723],[55.554199,25.4399865],[55.5518752,25.4408552],[55.5505241,25.4414505],[55.5490758,25.4421533],[55.5473004,25.4425344],[55.5456819,25.4430884],[55.5370397,25.4462406],[55.5358338,25.4466226],[55.5349558,25.4467468],[55.5337817,25.4467659],[55.523193,25.4459922],[55.5198927,25.4460304],[55.5162327,25.4457248],[55.513895,25.4454286],[55.4882538,25.4420186],[55.4870268,25.4419613],[55.4857574,25.4421332],[55.4843188,25.4426681],[55.4771469,25.4472339],[55.4709611,25.4516781],[55.324428,25.6230138],[55.3205286,25.6184125],[55.3117729,25.6106044],[55.3015804,25.5994214],[55.2892555,25.5829995],[55.2795994,25.5665793],[55.2730684,25.5680481],[55.2385191,25.570346],[55.2350748,25.5700834]]]}}

标签: d3.jschartsmapsgeojsond3.geo

解决方案


这里有几个问题。

首先,你得到一个矩形的原因是因为缠绕顺序。D3 不包含 geojson 规范的缠绕顺序 - 它遵循 shapefile 的缠绕顺序。这很奇怪,因为 D3 是唯一一个想到缠绕顺序在绘图方面很重要的框架,因为它使用球面几何而不是平面。在您的情况下,您正在绘制除阿联酋以外的所有内容:

在此处输入图像描述

我添加了一个笔画,我们可以看到世界的边缘(东西方向+/-180 度,而不是顶部,因为墨卡托在 y 轴上延伸到无穷大,即使是圆角或截断,极限也超出了SVG 的边缘),我们还可以看到阿联酋的轮廓(或至少它的一部分)。所以我们需要颠倒缠绕顺序,这意味着颠倒保存定义多边形中每个环的坐标的数组。

为此,我们可以使用 turf.js 来预先准备数据或在每次加载数据时调用它:

json.features.forEach(function(feature) {
  feature.geometry = turf.rewind(feature.geometry, {reverse:true});
})

现在我们有了正确缠绕顺序的数据,我们得到:

在此处输入图像描述

该特征看起来更大,因为我们可以看到所有特征,在第一张图像中只有最后绘制的特征是真正可见的:它覆盖了其余的特征。

现在我们有一个不同的问题,我已经在 960x500 像素帧上显示了您的地图(使用您的代码(小提琴))。如果我们使用 20x20 帧和您的初始比例,我们将看不到任何东西。该地图以 为中心[0,0],位于非洲海岸外的大西洋:

在此处输入图像描述

正方形使用您的比例和平移勾勒出 20x20 版本中显示的地图的粗略区域,我添加了一张世界地图并将大小增加到 200x200,以便为我们在 20 像素版本中看到的内容提供一些视觉背景


如果目标是展示整个世界,我们需要适当的比例。诸如墨卡托之类的圆柱投影(如果圆柱面向北/南)在地球周围(沿赤道)进行 360 度,并以地图单位将其拉伸一定距离。在 d3 中,比例是这个转换因子,默认假设整个地图的 960 像素显示整个星球,地球周围是 2π 弧度,因此比例值为 960/2π(每 2π 弧度 960 像素)。因此,我们可以将您的比例设置为width/Math.PI/2

在此处输入图像描述

这将导致一个新的问题,阿联酋大约是 5 度,世界是 360 度,大约是世界“宽度”的 1%。在 20 x 20 像素的 SVG 上显示地图时,这有点问题:UAE 的宽度约为 0.2 像素,渲染效果并不理想。唯一的选择是增加地图的大小,或者将地图以阿联酋为中心并增加比例。

Automagic map centering 已经被介绍过很多次了,我不会通过再次介绍它来解释之前的出色解释(我只能说 v4/5 提供了一个很好的 fitSize/fitExtent 方法,但是 v3 的中心信息可以在这个找到问题)。但是,为了演示,我将在下面手动将阿联酋居中。

阿联酋的质心大约是 24 N, 54E(从这里开始)。

如果我们假设阿联酋的宽度大于高度,并假设一个数字,例如大约 5 度,并且我们希望在 20 像素上显示这 5 度,那么我们知道世界的宽度约为 1440 像素(mapWidthPixels * earthWidthDegrees / mapWidthDegrees = 20 * 360 / 5)。然后比例为 1440/2/π,将其向下舍入一点,得到一点额外的余量:200。

所以使用:

var width = 20;
var height = 20;
var scale = 200;
var center = [54,24];

我们可以得到这个:

在此处输入图像描述

仍然很小,但是有了上面的原则,很容易扩展。

小提琴

我在上面的 20 像素 SVG 上添加了一个边框,以便为 SVG 提供一个边界,因为它的尺寸很小,而且 SVG 边缘的位置没有区别


推荐阅读