将圆的半径(以米为单位)缩放到 D3.js d3.geo.mercator 地图

Posted

技术标签:

【中文标题】将圆的半径(以米为单位)缩放到 D3.js d3.geo.mercator 地图【英文标题】:Scale a circle's radius (given in meters) to D3.js d3.geo.mercator map 【发布时间】:2015-10-12 22:32:36 【问题描述】:

我正在使用 D3.js 和 TopoJSON 库在网页上的一个小 div 中呈现世界的平面 SVG 地图。我还拍摄了一些地理对象(多边形和圆形),并通过纬度/经度坐标将它们绘制在这张地图上。这一切似乎都运行良好,但是,我在地图上绘制的圆形对象包含一个以米为单位的半径元素。我无法找到或弄清楚如何将此测量值适当地转换/缩放到 SVG 地图上。任何帮助将不胜感激。

画圆和设置的代码sn-p是:

if (formattedGeoObjects[a].shape.indexOf('circle') >= 0) 
  //plot point for circle
  svg.selectAll('.pin')
    .data(formattedGeoObjects).enter().append('circle', '.pin')
    .attr(fill: formattedGeoObjects[a].color.toString())
    .attr('r', 5) //formattedGeoObjects[a].radius is in meters
    .attr('transform', 'translate(' +
      projection([
        formattedGeoObjects[a].location[0],
        formattedGeoObjects[a].location[1]
      ]) + ')'
    );

精简版代码的JSFiddle链接:https://jsfiddle.net/vnrL0fdc/7/

这是完整的代码供参考...

完成大部分工作的函数:

setupMap: function(mapJson, theElement, geoObject, colorCb, normalizeCb) 
    var width = 200;
    var height = 120;

    //define projection of spherical coordinates to Cartesian plane
    var projection = d3.geo.mercator().scale((width + 1) / 2 / Math.PI).translate([width / 2, height / 2]);

    //define path that takes projected geometry from above and formats it appropriately
    var path = d3.geo.path().projection(projection);

    //select the canvas-svg div and apply styling attributes
    var svg =
      d3.select('#' + theElement + ' .canvas-svg').append('svg')
        .attr('width', width)
        .attr('height', height)
        .attr('class', 'ocean');

    //convert the topoJSON back to GeoJSON
    var countries = topojson.feature(mapJson, mapJson.objects.countries).features;

    //give each country its own path element and add styling
    svg.selectAll('.countries')
      .data(countries).enter().append('path')
      .attr('class', 'country')
      .attr('d', path);

    //add borders around all countries with mesh
    svg.append('path')
      .datum(topojson.mesh(mapJson, mapJson.objects.countries, function() 
        return true;
      ))
      .attr('d', path)
      .attr('class', 'border');

    //if shape data exists, draw it on the map
    if (geoObject !== null && geoObject.length !== 0) 
      //normalize geoObject into format needed for d3 arc functionality and store each shapes color
      var formattedGeoObjects = normalizeCb(geoObject, colorCb);

      for (a = 0; a < formattedGeoObjects.length; a++) 
        if (formattedGeoObjects[a].shape.indexOf('polygon') >= 0) 
          for (b = 0; b < formattedGeoObjects[a].lines.length; b++) 
            //plot point for polygon
            svg.selectAll('.pin')
              .data(formattedGeoObjects).enter().append('circle', '.pin')
              .style(fill: formattedGeoObjects[a].color.toString()).attr('r', 2)
              .attr('transform', 'translate(' +
                projection([
                  formattedGeoObjects[a].lines[b].coordinates[0][0],
                  formattedGeoObjects[a].lines[b].coordinates[0][1]
                ]) + ')'
              );
          
          //draw lines for polygon
          svg.append('g').selectAll('.arc')
            .data(formattedGeoObjects[a].lines).enter().append('path')
            .attr(d: path)
            .style(
              stroke: formattedGeoObjects[a].color.toString(),
              'stroke-width': '1px'
            );
        
        if (formattedGeoObjects[a].shape.indexOf('circle') >= 0) 
          //plot point for circle
          svg.selectAll('.pin')
            .data(formattedGeoObjects).enter().append('circle', '.pin')
            .attr(fill: formattedGeoObjects[a].color.toString())
            .attr('r', 5)
            .attr('transform', 'translate(' +
              projection([
                formattedGeoObjects[a].location[0],
                formattedGeoObjects[a].location[1]
              ]) + ')'
            );
        
      
    
  

以下是格式化的GeoObjects 的精简版:

[
  
    "shape": "polygon0",
    "color": "#000000",
    "lines": [
      
        "type": "LineString",
        "coordinates": [
          [
            -24.9609375,
            36.5625
          ],
          [
            -24.9609375,
            55.1953125
          ]
        ]
      
      ..... more coords
    ]
  ,
  
    "shape": "polygon1",
    "color": "#006600",
    "lines": [
      
        "type": "LineString",
        "coordinates": [
          [
            -42.1875,
            26.3671875
          ],
          [
            -71.71875,
            7.734375
          ]
        ]
      
      ..... more coordindates
    ]
  ,
  
    "shape": "circle2",
    "color": "#FF0000",
    "location": [
      13.359375,
      31.640625
    ],
    "radius": 1881365.33
  
]

最后,CSS/html

.canvas-svg 
  .ocean 
    background: #85E0FF;
  
  .country 
    fill: #FFFFFF;
  
  .border 
    fill: none;
    stroke: #777;
    stroke-width: .5;
  


<div class="canvas-svg"></div>

【问题讨论】:

现在把所有这些都放入小提琴中:D 抱歉,一开始就应该包含它。我使用 JSFiddle 链接 (jsfiddle.net/vnrL0fdc/7) 更新了原始帖子 【参考方案1】:

我的一位同事通过向我展示了一种更简单的方法来帮助我(仅供参考 - 对圆心的纬度/经度进行了更新)。在画布上绘制两个点并计算距离以找到比例有效且准确 - 但是有一种更简单的方法可以使用图像中的总像素和世界的总面积,代码 sn-ps 和JSFiddle 如下:

var width = 200;
var height = 120;

//variables for scaling circle radius
var totPixels = (width * height);
var totWorldMeters = 510000000000;
var metersPerPixel = (totWorldMeters / totPixels);
var scaledRadius;

//scale the radius given in meters to pixels on the map
scaledRadius = (100 * (formattedGeoObjects[a].radius / metersPerPixel));
if(scaledRadius < 2) 
    scaledRadius = 2;

工作 JSFiddle:https://jsfiddle.net/vnrL0fdc/15/

【讨论】:

【参考方案2】:

所以,我“认为”我找到了一种方法,可以将半径(以米为单位)缩放到笛卡尔平面 d3 地理地图上的像素。我可能让这种方式变得比它需要的更复杂 - 但我不知道该怎么做。

地图的高、宽、投影定义为:

var width = 200;
var height = 120;
var projection = 
    d3.geo.mercator()
    .scale((width + 1) / 2 / Math.PI)
    .translate([width / 2, height / 2]);

我在地图上绘制的地理对象包含多个点的纬度/经度坐标。通过在***上搜索,我找到了一个距离公式:

d = sqrt((x2 - x1)^2 + (y2 - y1)^2)

公式需要笛卡尔平面上的两个点 (x1,y1) 和 (x2,y2)。我选择了圆的点和多边形点之一,经纬度坐标如下:

Lat/Long for polygon1, point1: 36.5625, -24.9609375
Lat/Long for circle: 31.640625, 13.359375

我使用以下网站找出上面两个坐标之间的距离 - http://www.freemaptools.com/how-far-is-it-between.htm

Miles between the two coordinates on the map are: 3020.207

然后我通过以下方式找到了两个纬度/经度坐标在笛卡尔平面上的投影坐标 (x,y):

projection([long,lat])
X/Y for polygon1, point1: 86.0634765625, 38.040230671805666
X/Y for circle: 107.458984375, 41.36090550209383

于是,我将这些值代入公式,计算两点之间的像素距离:

d = sqrt ( (107.458984375 - 86.0634765625)^2 + (41.36090550209383 - 38.040230671805666)^2  )
result = 21.651665891641176 pixels
miles per pixel = 139.49074473599643 (calculated by: 3020.207/21.651665891641176)
meters per pixel = 224488.5930964074505 (calculated by 139.49074473599643 * 1609.34)

工作 JSFiddle:https://jsfiddle.net/vnrL0fdc/8/

这似乎是一种将米缩放到墨卡托地图投影的非常迂回的方法。如果有人有更简单的解决方案 - 请分享!

【讨论】:

以上是关于将圆的半径(以米为单位)缩放到 D3.js d3.geo.mercator 地图的主要内容,如果未能解决你的问题,请参考以下文章

传单地图宽度(以米为单位)

d3.js画圆弧和圆的坐标、弧长计算方法

MK 圆 - 笔画宽度相当于米

如何在openlayers中测量从圆心到边缘的距离

将 MKCircle 半径转换为 ​​CoreGraphics 圆弧半径

将距离转换为坐标[重复]