D3 V4在美国地图上正确放置气泡 [英] D3 V4 Properly placing a bubble in the US Map

查看:116
本文介绍了D3 V4在美国地图上正确放置气泡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建美国地图,并且在美国的某些地方有一系列的ACTUAL坐标.我想在地图上的正确位置放置一个点或气泡.如何缩放/翻译这些内容?

I am creating a US Map and I have a series of ACTUAL coordinates of some places in US. I would like to put a point or bubble on the right spot in the map. How do I scale/translate these?

这就是我得到的:

使用我尝试过的方法:

function USAPlot(divid, data) {

    var margin = { top: 20, right: 20, bottom: 30, left: 50 },
        width = 1040 - margin.left - margin.right,
        height = 700 - margin.top - margin.bottom;

    // formatting the data
    data.forEach(function (d) {
        d.loc = d.location;
        d.count = d.count;
        d.lat = d.latitude;
        d.lon = d.longitude;
    });

    var svg = d3.select(divid)
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        ;
    var path = d3.geoPath();
    var projection = d3.geoMercator()       
        .scale(200)  
        .translate([margin.left + width / 2, margin.top + height / 2])

    d3.json("https://d3js.org/us-10m.v1.json", function (error, us) {
        if (error) throw error;

        svg.append("g")
            .attr("class", "states")
            .attr("fill-opacity", 0.4)
            .selectAll("path")
            .data(topojson.feature(us, us.objects.states).features)
            .enter().append("path")
            .attr("d", path);

        svg.append("path")
            .attr("class", "state-borders")
            .attr("d", path(topojson.mesh(us, us.objects.states, function (a, b) { return a !== b; })));
    });


    svg.selectAll("myCircles")
        .data(data)
        .enter()
        .append("circle")
        .attr("cx", function (d) { return projection([d.lon, d.lat])[0]; })
        .attr("cy", function (d) { return projection([d.lon, d.lat])[1]; })
        .attr("r", 14)  //first testing with fixed radius and then will scale acccording to count
        .style("fill", "69b3a2")
        .attr("stroke", "#69b3a2")
        .attr("stroke-width", 3)
        .attr("fill-opacity", 1);

}

我不知道这些气泡是否已落在实际位置上-我绝对会在寻找它.

I have no idea whether these bubbles are dropping at the actual place - which I am definitely looking for.

推荐答案

至于一种测试方法,以查看要素是否正确定位,请尝试放置易于识别的地标,我在下面使用Seatle和Miami-位于感兴趣区域的相对两侧,应该很容易分辨它们是否位于错误的位置(在水还是内陆).

我不确定他们应该下落到哪里,因为我没有坐标,但是我可以告诉你它们不在应该下落的地方.

I'm not sure where they are supposed to fall as I do not have the coordinates but I can tell you they aren't where they are supposed to be.

我之所以知道这一点,是因为您对数据使用了两个不同的投影.

The reason I can know this is because you are using two different projections for your data.

您定义投影之一,并使用其定位点:

You define one of the projections and use it to position the dots:

var projection = d3.geoMercator()       
    .scale(200)  
    .translate([margin.left + width / 2, margin.top + height / 2])

这是一个以[0°,0°]为中心的墨卡托投影(默认情况下).这是该投影所投影的世界(具有边距和相同大小的SVG):

This is a Mercator projection centred at [0°,0°] (by default). Here is the world projected with that projection (with the margin and same sized SVG):

D3GᴇᴏMᴇʀᴄᴀᴛᴏʀWɪᴛʜCᴇɴᴛᴇʀ[0,0]ᴀɴᴅSᴄᴀʟᴇ200

D3 GᴇᴏMᴇʀᴄᴀᴛᴏʀ Wɪᴛʜ Cᴇɴᴛᴇʀ [0,0] ᴀɴᴅ Sᴄᴀʟᴇ 200

您正在基于此投影投影圆的坐标.

You are projecting coordinates for the circles based on this projection.

为实现可重现性,以下是代码段-您应全屏查看:

For reproducability, here's a snippet - you should view in full screen:

  var margin = { top: 20, right: 20, bottom: 30, left: 50 },
  width = 1040 - margin.left - margin.right,
  height = 700 - margin.top - margin.bottom;
		
d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/land-50m.json").then(function(json) {
		
  var svg = d3.select("body")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)		
	
  var projection = d3.geoMercator()       
    .scale(200)  
    .translate([margin.left + width / 2, margin.top + height / 2])

  var path = d3.geoPath().projection(projection);
		
  svg.append("g")
     .attr("class", "states")
     .attr("fill-opacity", 0.4)
     .selectAll("path")
     .data(topojson.feature(json, json.objects.land).features)
     .enter().append("path")
     .attr("d", path);
	})
		

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.js"></script>

第二个预测并不明显.如果您查看用于创建上述图像的代码段,则会注意到该代码段将投影分配给了路径:

The second projection is not obvious. If you look at the snippet used to create the above image, you'll notice that it assigns the projection to the path:

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

因此,路径会将每个地理坐标(球形纬度/经度对)转换为屏幕上的正确坐标(笛卡尔像素x,y值):[-17°,85°]的坐标为转换为[100px,50px]之类的东西.

This is so the path converts each geographic coordinate (a spherical latitude/longitude pair) to the correct coordinate on the screen (a Cartesian pixel x,y value): a coordinate of [-17°,85°] will be converted to something like [100px,50px].

在您的问题中,您只需使用:

In your question you simply use:

var path = d3.geoPath();

您没有为路径分配投影-因此d3.geoPath()只是在geojson/topojson中绘制每个顶点/点,就好像该坐标包含像素坐标:geojson中的[100px,50px]坐标/topojson在SVG上以x = 100,y = 50绘制.

You don't assign a projection to the path - so d3.geoPath() simply plots every vertice/point in the geojson/topojson as though the coordinate contains pixel coordinates: a coordinate of [100px,50px] in the geojson/topojson is plotted on the SVG at x=100, y=50.

尽管未使用投影,但您的美国各州仍按预期绘制.为什么?因为geojson/topojson已经被投射 了.由于它是预先投影的,因此在D3中绘制时不需要使用投影.

Despite not using a projection, your the US states plot as expected. Why? Because the geojson/topojson was already projected. Since it was preprojected, we don't need to use a projection when we plot it with D3.

预先投影的几何图形很有用,因为它需要较少的计算绘制,从而加快了渲染速度,但代价是灵活性较低(请参见

Pre-projected geometry can be useful as it requires less calculations to draw, resulting in faster rendering speeds, but comes at a cost of less flexibility (see here).

如果我们用d3.geoProjection投影的几何图形覆盖您的预先投影的几何图形,则会得到:

If we overlay your pre-projected geometry with the geometry you project with d3.geoProjection, we get:

很自然地,您会看到两者之间没有相同之处.因此,您不是在投影点,以便它们正确地覆盖预投影的几何形状.

Naturally, you can see there is no point that is the same between the two. Consequently, you are not projecting points so that they properly overlay the pre-projected geometries.

Cᴏᴍᴘᴀʀɪsᴏɴᴛʜᴇᴘʀᴏᴊᴇᴄᴛɪᴘʀᴏᴊᴇᴄᴛs

Cᴏᴍᴘᴀʀɪsᴏɴ ʙᴇᴛᴡᴇᴇɴ ᴛʜᴇ ᴛᴡᴏ ᴘʀᴏᴊᴇᴄᴛɪᴏɴs

要复制的代码段

var margin = { top: 20, right: 20, bottom: 30, left: 50 },
        width = 1040 - margin.left - margin.right,
        height = 700 - margin.top - margin.bottom;
		
   var svg = d3.select("body")
		  .append("svg")
          .attr("width", width + margin.left + margin.right)
          .attr("height", height + margin.top + margin.bottom)	

	d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/land-50m.json").then(function(json) {
		
        var projection = d3.geoMercator()       
          .scale(200)  
          .translate([margin.left + width / 2, margin.top + height / 2])

		var path = d3.geoPath().projection(projection);
		
		svg.append("g")
            .attr("fill-opacity", 0.4)
            .selectAll("path")
            .data(topojson.feature(json, json.objects.land).features)
            .enter().append("path")
            .attr("d", path);
	
	})
	
	d3.json("https://d3js.org/us-10m.v1.json").then(function(us) {

		var path = d3.geoPath();

        svg.append("g")
            .attr("fill-opacity", 0.4)
            .selectAll("path")
            .data(topojson.feature(us, us.objects.states).features)
            .enter().append("path")
            .attr("d", path);
		
	})

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.js"></script>

在没有元数据解释geojson/topojson使用什么投影和坐标系的情况下,我们通常无法复制该投影以覆盖其他要素.

Without metadata explaining what projection and coordinate system a geojson/topojson uses, we generally cannot duplicate that projection to overlay other features.

但是,在这种情况下,如果我们仔细查看绘制的美国各州,就会发现使用Albers投影来预先投影各州的轮廓.

In this case, however, if we look carefully at the plotted US states, we can see that an Albers projection was used to pre-project the state outlines.

有时候,我们可以猜测投影参数.因为我对这个文件()相当熟悉,所以我可以告诉您它使用以下参数:

Sometimes, we can guess the projection parameters. As I'm fairly familiar with this file (), I can tell you it uses the following parameters:

d3.geoAlbersUsa()
  .scale(d3.geoAlbersUsa().scale()*6/5)
  .translate([480,300]);

以下是显示迈阿密和西雅图覆盖的示例:

Here's an example showing Miami and Seattle overlain:

  var width = 960,height = 600;
		
var svg = d3.select("body")
	.append("svg")
  .attr("width",width)
  .attr("height",height);

d3.json("https://d3js.org/us-10m.v1.json").then(function(us) {

  var path = d3.geoPath();
  var projection = d3.geoAlbersUsa()
    .scale(d3.geoAlbersUsa().scale()*6/5)
    .translate([width/2,height/2]);
    
  svg.append("g")
    .attr("fill-opacity", 0.4)
    .selectAll("path")
    .data(topojson.feature(us, us.objects.states).features)
    .enter().append("path")
    .attr("d", path);
    
    var places = [
      [-122.3367534,47.5996582],
      [-80.1942949,25.7645783]
    ]
    
    svg.selectAll(null)
      .data(places)
      .enter()
      .append("circle")
      .attr("r", 3)
      .attr("transform", function(d) {
        return "translate("+projection(d)+")";
      })
		
	})

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.js"></script>

但是,这样做的缺点是在采用其他屏幕尺寸,平移,中心,比例等时非常钝.预投影的几何图形与未投影的几何图形结合使用时也会产生很多混乱.例如,此问题显示了在正确调整大小和定心预先投影的几何图形时普遍存在的挫败感.

But, this has a downside of being very obtuse in adopting for other screen sizes, translations, centers, scales, etc. Pre-projected geometry also generates a lot of confusion when combined with unprojected geometry. For example, this question shows a common frustration on sizing and centring pre-projected geometry properly.

更好的解决方案是对所有内容都使用一个投影.要么先进行所有项目的预投影(这会稍微复杂一些),要么即时进行所有项目的投影(对于浏览器来说确实不需要那么长时间).修改可视化效果或地理数据时,这更清晰,更轻松.

A better solution is to use one projection for everything. Either pre-project everything first (which is a bit more complex), or project everything on the fly (it really doesn't take that long for a browser). This is just clearer and easier when modifying the visualization or the geographic data.

要以相同的方式投影所有内容,您需要确保未投影所有数据,也就是说,其坐标/坐标空间使用经/纬对.由于您的美国json已预先投影,因此我们需要找到另一个,也许是:

To project everything the same way, you'll need to make sure all your data is unprojected, that is to say it uses lat/long pairs for its coordinates / coordinate space. As your US json is pre-projected, we'll need to find another, perhaps:

我们只是通过投影运行所有内容:

And we simply run everything through the projection:

代码段不会加载资源,但这里是 .ock,代码如下所示:

Snippet won't load the resource, but here's a bl.ock, with the code shown below:

var width =960,height = 600;

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

d3.json("us.json").then(function(us) {

  var projection = d3.geoAlbersUsa()
    .scale(150)
    .translate([width/2,height/2]);
   var path = d3.geoPath().projection(projection);

  svg.append("g")
    .attr("fill-opacity", 0.4)
    .selectAll("path")
    .data(topojson.feature(us, us.objects.states).features)
    .enter().append("path")
    .attr("d", path);

    var places = [
      [-122.3367534,47.5996582],
      [-80.1942949,25.7645783]
    ]

    svg.selectAll(null)
      .data(places)
      .enter()
      .append("circle")
      .attr("r", 3)
      .attr("transform", function(d) {
        return "translate("+projection(d)+")";
      })

})

这篇关于D3 V4在美国地图上正确放置气泡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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