javascript - 使用D3.js绘制自定义json映射

标签 javascript json d3.js geojson map-projections

我正在使用D3.js创建地图。
我首先从此处下载国家(加拿大)shapefile:
https://www.arcgis.com/home/item.html?id=dcbcdf86939548af81efbd2d732336db

..并在此处将其转换为geojson(链接到下面的文件):
http://mapshaper.org/

到目前为止,我所看到的只是一个彩色块,控制台上没有任何错误。我的问题是,我怎么知道我的json文件或代码不正确?
这是我的代码,底部是指向json文件的链接。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>D3: Setting path fills</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
        <!-- <script src="https://d3js.org/topojson.v1.min.js"></script> -->
        <style type="text/css">
        /* styles */       
        </style>
    </head>
    <body>
        <script type="text/javascript">
        var canvas = d3.select("body").append("svg")
        .attr("width", 760)
        .attr("height", 700)
        d3.json("canada.geo.json", function(data) {
        var group = canvas.selectAll("g")
        .data(data.features)
        .enter()
        .append("g")

        var projection = d3.geo.mercator();
        var path = d3.geo.path().projection(projection);
        var areas = group.append("path")
        .attr("d",path)
        .attr("class","area")
        })
        </script>
    </body>
</html>


链接到json文件:
https://github.com/returnOfTheYeti/CanadaJSON/blob/master/canada.geo.json

最佳答案

d3 geoProjection使用未投影的坐标-三维地球上的坐标。 geoProjection获取这些坐标并将其投影到二维平面上。未投影坐标的单位通常是经度和纬度,并且d3 geoProjection会期望这样做。问题在于您的数据已经被投影。

如何判断数据是否已投影?

有两种快速的方法来确定是否投影您的数据:


看一下数据的元数据
看一下自己的地理坐标


查看地理元数据

数据使用的投影是在.prj文件中定义的,该文件是构成shapefile的文件集合的一部分:

PROJCS["Canada_Albers_Equal_Area_Conic",
   GEOGCS["GCS_North_American_1983",
      DATUM["D_North_American_1983",
        SPHEROID["GRS_1980",6378137.0,298.257222101]],
      PRIMEM["Greenwich",0.0],
      UNIT["Degree",0.0174532925199433]],
  PROJECTION["Albers"],
  PARAMETER["False_Easting",0.0],
  PARAMETER["False_Northing",0.0],
  PARAMETER["Central_Meridian",-96.0],
  PARAMETER["Standard_Parallel_1",50.0],
  PARAMETER["Standard_Parallel_2",70.0],
  PARAMETER["Latitude_Of_Origin",40.0],
  UNIT["Meter",1.0]]


您的数据已经使用Albers投影进行了投影,测量单位是米。投影该数据就好象它由经/纬对组成,将无法正常工作。

如果只有geojson文件而没有参考shapefile,则某些geojson文件会在投影属性中指定EPSG编号,如果该编号不是4326,则可能是已投影的数据。

看坐标

您可以说您的数据没有非投影数据,因为每个坐标的值都在经度和纬度(东/西+/- 180度,南北+/- 90度)的范围之外:

"coordinates":[[[[899144.944639163,2633537.


您的坐标在全球范围内平移了几次:这就是为什么投影导致svg完全充满要素的原因。

好吧,现在呢?

有两种主要的解决方案供您选择:


转换投影,使geojson由纬度和经度点组成
使用d3.geoTransform或d3.geoIdentity转换您的投影数据。


转换投影

为此,您要“取消投影”数据,或者投影数据,使其包含经度,纬度点。

大多数GIS软件都可以重新投影数据。使用shapefile比使用geojson要容易得多,因为shapefile在GIS软件中更为常见。 GDAL,QGIS,ArcMap提供了相对容易的转换。

也有在线转换器,mapshaper.org可能是最简单的转换器,并且在处理d3时具有更多好处-简化(许多shapefile包含太多用于Web映射目的的细节)。将shapefile的所有文件拖到mapshaper窗口中,打开控制台并键入:proj wgs84。导出为geojson(简化后),您就可以为d3准备好geojson了。

重新投影后,您可能会发现您的数据看起来很尴尬。不用担心,它是未投影的(好吧,是某种未投影的,它显示为2d,但是具有非常简单的投影,假定笛卡尔输入数据)。

使用未投影的数据,现在可以在d3中投影数据了。

这是您的数据的example(d3-v4。数据已简化并在mapshaper上重新投影(对我没有隶属关系))

使用d3.geoIdentity或d3.geoTransform

为此,我建议使用d3v4(我看到您的代码是v3)。尽管geo.transform在v3中可用,但是如果没有v4中可用的新方法(即d3.geoIdentity和projection.fitSize),则麻烦得多。我将在这里介绍使用投影数据的v4方法

使用数据,您可以定义另一种类型的投影:

var projection = d3.geoIdentity();


但是,如果您不小心,这种类型的“投影”会给您带来麻烦。它基本上吐出给出的x,y值。但是,地理投影坐标空间通常在左下角的某个位置具有[0,0],而svg坐标空间在左上角的[0,0]。在svg坐标空间中,y值随着您沿着坐标平面下降而增加,在数据的投影坐标空间中,y值随着您上升而增大。因此,使用身份将使您的数据倒置。

幸运的是,我们可以使用:

var projection = d3.geoIdentity()
   .reflectY(true);


最后一个问题仍然存在:geojson中的坐标未缩放或转换,因此要素未正确居中。为此,有fitSize方法:

   var projection = d3.geoIdentity()
       .reflectY(true)
       .fitSize([width,height],geojsonObject)


这里的width和height是SVG(或我们要在其中显示特征的父容器)的宽度和高度,而geojsonObject是geojson特征。请注意,它不会采用一系列功能,如果您具有一系列功能,请将它们放置在功能集中。

Here's使用这种方法显示的数据(我仍然简化了geojson)。

您也可以使用geoTransform,这有点复杂,但是允许您指定自己的变换方程式。在大多数情况下,坚持使用geoIdentity可能是过大的。

每个选项的优点和缺点:

取消投影数据:

除了要取消投影数据的最初行程之外,还需要取消投影数据,使其包含经度纬度对,因此每次显示数据时都必须进行一些额外的处理。

但是,通过访问任何d3 geoProjection,您在显示数据方面也具有高度的灵活性。使用未投影的数据还可以使您更轻松地对齐不同的图层:您不必担心分别缩放和转换多个图层。

保留投影数据

通过保持数据的投影,您无需进行球形数学运算,从而节省了计算时间。缺点是上面列出的优点,很难匹配不共享此投影的数据(如果使用此投影导出所有内容,这很好),并且您难以使用表示法-d3.geoTransform在将您的投影从墨卡托转换为阿尔伯斯的方法。

请注意,我在上面的选项2中使用的fit.size()方法可用于所有geoProjections(v4)。

在两个示例中,我尽可能使用您的代码。虽然有几点警告,但我改为d3v4(例如,d3.geo.path-> d3.geoPath,d3.geo.mercator-> d3.geoMercator)。我还将变量canvas的名称更改为svg,因为它是svg而不是画布的选择。最后,在第一个示例中,我没有修改您的投影,墨卡托的中心默认为[0,0](长/拉),这解释了奇怪的位置

关于javascript - 使用D3.js绘制自定义json映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47447754/

相关文章:

Javascript - 通过键检索值,包括嵌套对象

java - 了解来自 Java 和 C++ 背景的 Javascript 原型(prototype)

javascript - 尝试删除事件源时,未捕获的 TypeError : $(. ..).fullCalendar 不是函数

php - 如何将一个 JavaScript 对象复制到另一个对象中?

javascript - 获取选定的 JSON 数据

javascript - D3 : Circle Points don't update when changing slider

javascript - React-vis sankey 标题和图例

javascript - React 和 PropTypes

java - 测试 JSONProcessingException

javascript - 节点转换后拖动无法正常工作,D3