将圆的半径(以米为单位)缩放到D3.js d3.geo.mercator贴图 [英] Scale a circle's radius (given in meters) to D3.js d3.geo.mercator map

查看:155
本文介绍了将圆的半径(以米为单位)缩放到D3.js d3.geo.mercator贴图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using D3.js and TopoJSON libraries to render a flat SVG map of the world in a small div on a web page. I'm also taking some geographic objects (polygons and circles), and plotting them on this map via lat/long coordinates. This all seems to be working pretty well, however, the circle objects that I am plotting on the map contain a radius element which is given in meters. I cannot find or figure out how to convert/scale this measurement appropriately onto the SVG map. Any help would be greatly appreciated.

绘制圆圈和设置的代码片段是:

The snippet of code that is drawing the circle and setting is:

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/

JSFiddle link for condensed version of the code: https://jsfiddle.net/vnrL0fdc/7/

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

Here's the full code for reference...

完成大部分工作的功能:

Function that does the bulk of the work:

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]
              ]) + ')'
            );
        }
      }
    }
  }

这里是formattedGeoObjects的缩写版本:

Here is a condensed version of what the formattedGeoObjects looks like:

[
  {
    "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:

And lastly, the CSS/HTML:

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

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


推荐答案

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

A colleague of mine helped me out by showing me a much simpler way to do this (FYI - there has been an update to the lat/lon for the center of the circle). Plotting two points on the canvas and computing the distance to find the scale works and is accurate - but there is a much more simple way of doing it using the total pixels in the image and the total area of the world, code snippets and JSFiddle below:

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/

Working JSFiddle: https://jsfiddle.net/vnrL0fdc/15/

这篇关于将圆的半径(以米为单位)缩放到D3.js d3.geo.mercator贴图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆