【问题标题】:Drawing circles via d3js and converting coordinates通过 d3js 绘制圆圈并转换坐标
【发布时间】:2021-01-28 11:21:27
【问题描述】:

我有以下 d3js 代码,通过 d3.jsonrenderMap 函数绘制基于 geojson 的国家地图。我想将城市添加到地图中,以圆圈表示。城市将在与国家地图相同的 svg 中。 城市在ua_cities_admin.csv 中,通过renderCity 函数渲染。

我不明白的是如何将来自renderCity 的数据数组放在svg 组件中,以及如何将城市坐标映射到国家地图。

我们将不胜感激。

<!DOCTYPE html>
<meta charset="utf-8">
<head>
  <title>geoPath measures</title>
</head>

<body>
<div align="center">
<svg id="my_dataviz"></svg>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

<script>
  var height = window.innerHeight - 100;
  var width = window.innerWidth - 100;
  var svg = d3.select('#my_dataviz')
    .attr("width", width)
    .attr("height", height);
  
function renderMap(data) {
  var projection = d3.geoIdentity().reflectY(true).fitSize([width, height], data)
  var geoGenerator = d3.geoPath().projection(projection);
  svg.append("g")
    .selectAll("path")
    .data(data.features)
    .enter()
    .append('path')
    .attr('d', geoGenerator)
    .attr('fill', 'steelblue');
};

function renderCity(data) {
  var projection = d3.geoIdentity().reflectY(true).fitSize([width, height], data)
  console.log(data)
  svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", 50)
    .attr("cy", 50)
    .attr("r", 3);
};

d3.json('https://raw.githubusercontent.com/EugeneBorshch/ukraine_geojson/master/Ukraine.json', renderMap);
d3.csv('ua_cities_admin.csv', renderCity);
</script>
</body>
</html>

ua_cities_admin.csv 文件有以下字段:

  • City
  • lat
  • lng

【问题讨论】:

    标签: javascript html d3.js


    【解决方案1】:

    这是你的前提:

    1. 您有两个输入数据集,包括地理数据、geojson 文件和 csv 文件。两者都使用经纬度作为坐标系。
    2. 您想要投影它们,但又希望它们共享同一个投影坐标系,以便它们彼此对齐。

    但是,如果您使用两个不同的投影,您将无法对齐:两个输入数据集中的任何公共坐标都将被每个不同的投影以不同的方式投影。

    解决此问题的最简单方法是对两个数据集重复使用相同的投影,并使用地理投影,例如:

    var projection = d3.geoMercator()
    var geoGenerator = d3.geoPath().projection(projection);
    
    function renderMap(data) {
      projection.fitSize([width, height], data)   
      svg.append("g")
        .selectAll("path")
        .data(data.features)
        .enter()
        .append('path')
        .attr('d', geoGenerator)
        .attr('fill', 'steelblue');
    };
    
    function renderCity(data) {
      svg.selectAll("circle")
        .data(data)
        .enter()
        .append("circle")
        .attr("cx", d=>projection([d.lon,d.lat])[0])
        .attr("cy", d=>projection([d.lon,d.lat])[1])
        .attr("r", 3);
    };
    
     d3.json("file", function(geojson) {
       d3.csv("file", function(csv) {
           renderMap(geojson);
           renderCity(csv);
        })
     })
    

    我嵌套了您的请求,因为否则,首先加载的文件将首先被绘制。我们还需要先加载 geojson 来设置将用于 csv 圆圈的投影数据


    其他细节

    Projection.fitSize()

    作为参考,projection.fitSize() 需要一个有效的 geojson 对象。 d3.csv 生成的数据是一个对象数组。如果我们想让fitSize 工作,我们需要将其转换为geojson。 Geojson 点特征如下:

    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [longitude, latitude]
      },
      "properties": { /* ... */ }
    }
    

    所以要创建geojson,我们需要稍微处理一下您的数据:

    var features = data.map(function(d) {
        return {     
          "type": "Feature",
          "geometry": {
             "type": "Point",
             "coordinates": [d.lng, d.lat]
          },
          "properties": { "name":d.city }
        }
    })
    

    但是我们确实需要传递一个对象,而不是一个数组,所以我们需要创建一个 geojson 特征集合:

    var featureCollection = { type:"FeatureCollection", features:features }
    

    投影与身份

    与 d3.geoIdentity 不同,d3.geoProjection 可以将坐标传递给投影。 d3.geoIdenity 可以工作,但需要对上述代码进行一些调整;但是,它可能会导致感兴趣区域的形状异常或不熟悉的表示,因为它基本上实现了 Plate Carree 投影:纬度/经度被视为平面,并适当缩放/平移到中心并适当调整地图大小。 d3.geoProjection 可能适合这里,投影也允许使用 fitSize()

    如果您希望与 d3.geoIdentity 具有相同的形状,您可以在上面的代码中使用 d3.geoEquirectangular() 代替 d3.geoMercator。

    手动设置投影参数

    fitSize() 设置身份/投影的比例并翻译 - 就是这样。

    您可以定义一次投影,而不是在加载数据后等待重新定义其参数。如果您的数据是静态的,您可以在运行fitSize() 后通过记录projection.scale()projection.translate() 来提取fitSize() 使用的比例和转换值,然后使用这些值设置投影比例并自行转换。这可能意味着在绘制任何圆圈之前您无需等待 geojson 加载(前提是您确保没有在圆圈顶部绘制 geojson)。

    【讨论】:

    • 感谢您的回答。但是我收到以下错误:Uncaught TypeError: projection is not a function I can't find a solution to this.
    • 今晚晚些时候会做一个有效的sn-p
    • 我的错,定位错误(需要将投影嵌套在函数中),如果我们想要一个最少的解决方案,还需要使用 d3 投影(不是身份)的调整。 Here's 和例子。
    • 谢谢安德鲁·里德!非常感谢您的帮助!
    • @AndrewReid 这非常有帮助。有很多关于使用 .fitSize() 的文档,但是您将坐标数组转换为 geojson 特征集合的代码正是我一直在寻找的缺失部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 2016-02-27
    • 1970-01-01
    • 2015-10-20
    • 2018-08-09
    相关资源
    最近更新 更多