D3制图:lon/lat圆在地图上的错误位置(投影) [英] D3 cartography: lon/lat circles in wrong place on map (projection)

查看:102
本文介绍了D3制图:lon/lat圆在地图上的错误位置(投影)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对D3制图有疑问. 我正在做一个小项目,对D3还是陌生的.

I have a question about D3 cartography. I am working on a little project and I am new to D3.

我从以下示例开始: http://bl.ocks.org/mbostock/5914438 除了显示状态网格之外,我还想在地图上的特定位置(lon/lat)上显示圆圈.我目前遇到的问题是圆圈不在地图上的正确位置上.我怀疑问题出在Mike使用的特殊投影上.他使用1x1正方形投影.可能这对于显示磁贴是必需的.当我投影坐标时,值都在-1和1之间.我认为我可以通过将它的宽度与高度和宽度相乘来修复它,但是没有用.下面是我的代码(由于缺少文件,因此代码段无法运行).感谢您的协助!

I have started out from this example: http://bl.ocks.org/mbostock/5914438 Instead of the showing the state-mesh, I would like to show circles on the map in certain locations (lon/lat). I am currently facing a problem that the circles are not on the correct spots on the map. I suspect the problem lies in the special projection that Mike uses. He uses a 1x1 square projection. Probably this is necessary for displaying the tiles. When I project the coordinates, the values are all between -1 and 1. I thought I could fix it by multiplying it width the height and width but it didn't work. Below is my code (snippet does not run because it is missing a file). Thanks for the assistance!

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  margin: 0;
}

path {
  fill: none;
  stroke: red;
  stroke-linejoin: round;
  stroke-width: 1.5px;
}

circle {
  fill: #fff;
  fill-opacity: 0.4;
  stroke: #111;
}

</style>
<svg>
</svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//d3js.org/d3-tile.v0.0.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var pi = Math.PI,
    tau = 2 * pi;

var width = Math.max(960, window.innerWidth),
    height = Math.max(500, window.innerHeight);

// Initialize the projection to fit the world in a 1×1 square centered at the origin.
var projection = d3.geoMercator()
    .scale(1 / tau)
    .translate([0, 0]);

var path = d3.geoPath()
    .projection(projection);

var tile = d3.tile()
    .size([width, height]);

var zoom = d3.zoom()
    .scaleExtent([1 << 9, 1 << 23])
    .on("zoom", zoomed);

var svg = d3.select("svg")
    .attr("width", width)
    .attr("height", height);

var raster = svg.append("g");

var vector = svg.append("path");

var circle = svg.append("g")
      
d3.json("/data/flyingsites/AD.json", function(error, flyingsites) {
  if (error) console.log(error); 
  
  // Compute the projected initial center.
  var center = projection([6.2, 45.8]);//45,809718, 6,252314

  // Apply a zoom transform equivalent to projection.{scale,translate,center}.
  svg
      .call(zoom)
      .call(zoom.transform, d3.zoomIdentity
          .translate(width / 2, height / 2)
          .scale(1 << 12)
          .translate(-center[0], -center[1]));

//add flying sites
  circle.selectAll("circle")
  	.data(flyingsites.features)
  .enter().append("circle")
  	.attr('r',5)
    .attr('cx',function(d) { return projection(d.geometry.coordinates)[0]*width})
    .attr('cy',function(d) { return projection(d.geometry.coordinates)[1]*height})
    .style('fill','red')
    
//console.log(flyingsites.features);
//console.log(circle);
});


function zoomed() {
  var transform = d3.event.transform;

  var tiles = tile
      .scale(transform.k)
      .translate([transform.x, transform.y])
      ();

  vector
      .attr("transform", transform)
      .style("stroke-width", 1 / transform.k);
     
  circle
      .attr("transform", "translate(" + transform.x + "," + transform.y + ")");
      
  var image = raster
      .attr("transform", stringify(tiles.scale, tiles.translate))
    .selectAll("image")
    .data(tiles, function(d) { return d; });

  image.exit().remove();

  image.enter().append("image")
      .attr("xlink:href", function(d) { return "http://" + "abc"[d[1] % 3] + ".tile.openstreetmap.org/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
      .attr("x", function(d) { return d[0] * 256; })
      .attr("y", function(d) { return d[1] * 256; })
      .attr("width", 256)
      .attr("height", 256);
}

function stringify(scale, translate) {
  var k = scale / 256, r = scale % 1 ? Number : Math.round;
  return "translate(" + r(translate[0] * scale) + "," + r(translate[1] * scale) + ") scale(" + k + ")";
}

</script>

推荐答案

您采用的方法不起作用,因为其中一种方法不考虑规模(仅翻译).这一点很关键,因为d3-tile使用几何缩放-将缩放变换(缩放和平移)应用于所有矢量元素(而不是图块),这就是为什么投影将所有内容投影到一个像素正方形区域,并且永远不会随着像素变化而改变的原因.缩放.

The approach you are taking won't work, for one, it doesn't consider the scale (just translate). This is critical as d3-tile uses geometric zooming - it applies a zoom transform (scale and translate) to all the vector elements (not the tiles), this is why the projection projects everything to a one pixel square area and never changes with the zoom.

要解决此问题,请将圆与示例放置(和尺寸)多边形的位置相同:

To solve this, place your circles the same as the example places (and sizes) the polygons:

vector
  .attr("d", path(topojson.mesh(us, us.objects.counties)));

circle
  .attr("cx", projection(coord)[0])
  .attr("cy", projection(coord)[1])
  .attr("r", 5/(1<<12));

这两个位置的功能相同:仅投影时,投影到一个像素正方形.缩放将应用变换以覆盖整个svg. 此外,由于我们要缩放一个像素以适合svg,因此我们也希望半径也要适当缩放..

Both of these position features the same way: with the projection only, projecting to a one pixel square. The zoom applies the transform to cover the whole svg. Also, since we are scaling that one pixel to fit the svg, we want the radius to be scaled appropriately too.

现在,我们可以将与多边形相同的变换应用于圆:

Now we can apply a transform to the circles the same as the polygons:

circle
  .attr("transform", transform);

当然,我们也可以使用每个缩放比例缩小半径,使用缩放比例k修改圆的大小:

Of course, we could scale the radius down each zoom too, using the zoom k to modify the size of the circle:

var pi = Math.PI,
    tau = 2 * pi;

var width = Math.max(960, window.innerWidth),
    height = Math.max(500, window.innerHeight);

// Initialize the projection to fit the world in a 1×1 square centered at the origin.
var projection = d3.geoMercator()
    .scale(1 / tau)
    .translate([0, 0]);

var path = d3.geoPath()
    .projection(projection);

var tile = d3.tile()
    .size([width, height]);

var zoom = d3.zoom()
    .scaleExtent([1 << 11, 1 << 14])
    .on("zoom", zoomed);

var svg = d3.select("svg")
    .attr("width", width)
    .attr("height", height);

var raster = svg.append("g");

var vector = svg.append("circle");

  // Compute the projected initial center.
  var center = projection([-98.5, 39.5]);

  // Apply a zoom transform equivalent to projection.{scale,translate,center}.
  svg
      .call(zoom)
      .call(zoom.transform, d3.zoomIdentity
          .translate(width / 2, height / 2)
          .scale(1 << 12)
          .translate(-center[0], -center[1]));

  var coord = [-100,40]
		  
  vector
      .attr("cx", projection(coord)[0])
	  .attr("cy", projection(coord)[1])
	  .attr("r", 5/(1<<12));

function zoomed() {
  var transform = d3.event.transform;

  var tiles = tile
      .scale(transform.k)
      .translate([transform.x, transform.y])
      ();

  vector
      .attr("transform", transform)
      .attr("r", 5/transform.k);

  var image = raster
      .attr("transform", stringify(tiles.scale, tiles.translate))
    .selectAll("image")
    .data(tiles, function(d) { return d; });

  image.exit().remove();

  image.enter().append("image")
      .attr("xlink:href", function(d) { return "http://" + "abc"[d[1] % 3] + ".tile.openstreetmap.org/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
      .attr("x", function(d) { return d[0] * 256; })
      .attr("y", function(d) { return d[1] * 256; })
      .attr("width", 256)
      .attr("height", 256);
}

function stringify(scale, translate) {
  var k = scale / 256, r = scale % 1 ? Number : Math.round;
  return "translate(" + r(translate[0] * scale) + "," + r(translate[1] * scale) + ") scale(" + k + ")";
}

<svg></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-tile.v0.0.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>

由于您使用的坐标系非常多(瓷砖,缩放,像素,投影,地理),并且通常不会将整个地图投影到一个1像素的正方形,所以d3-tile最终可能有点令人困惑.

Ultimately d3-tile can be a bit confusing to start with because you are using quite a few coordinate systems (tile, zoom, pixel, projected, geographic), and normally aren't projecting the entire map to a 1 pixel square.

这篇关于D3制图:lon/lat圆在地图上的错误位置(投影)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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