【问题标题】:D3 V4 Properly placing a bubble in the US MapD3 V4 在美国地图中正确放置气泡
【发布时间】:2020-01-21 22:40:57
【问题描述】:

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

这是我得到的:

根据我的尝试:

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);

}

我不知道这些气泡是否在实际位置下降 - 我肯定在寻找。

【问题讨论】:

  • 数据如下:{'location': 'MD', 'count': 2, 'latitude': '47.286747', 'longitude': '28.5110236'}

标签: d3.js map-projections d3.geo


【解决方案1】:

作为一种测试方法来查看特征是否正确匹配,尝试放置易于识别的地标,我在下面使用西雅图和迈阿密 - 它们位于感兴趣区域的相对两侧,应该是很容易判断它们是否在错误的地方(在水中或内陆)。

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

我知道这是因为您对数据使用了两种不同的投影。

墨卡托投影

您定义其中一个投影并使用它来定位点:

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

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

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

您正在根据此投影投影圆的坐标。

为了重现性,这里有一个 sn-p - 您应该全屏查看:

  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>

神秘投影

第二个投影不明显。如果您查看用于创建上述图像的 sn-p,您会注意到它将投影分配给路径:

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

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

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

var path = d3.geoPath();

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

尽管没有使用投影,您的美国各州仍按预期绘制。为什么?因为geojson/topojson 已经被投影了。由于它是预先投影的,所以我们在用 D3 绘制它时不需要使用投影。

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

如果我们将您的预投影几何与您使用 d3.geoProjection 投影的几何重叠,我们会得到:

当然,您可以看到两者之间没有相同的点。因此,您没有投影点,以便它们正确地覆盖预投影的几何图形。

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 使用什么投影和坐标系的元数据,我们通常无法复制该投影以覆盖其他特征。

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

有时,我们可以猜测投影参数。由于我对这个文件()相当熟悉,我可以告诉你它使用以下参数:

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

这是一个显示迈阿密和西雅图重叠的示例:

  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>

但是,这有一个缺点,即在采用其他屏幕尺寸、平移、中心、比例等方面非常迟钝。预投影几何体在与未投影几何体结合时也会产生很多混乱。例如,question 显示了在正确调整和居中预投影几何体时常见的挫败感。

更好的解决方案

更好的解决方案是对所有内容都使用一个投影。要么先预先投影所有内容(这有点复杂),要么即时投影所有内容(浏览器确实不需要那么长时间)。这在修改可视化或地理数据时更加清晰和容易。

要以相同的方式投影所有内容,您需要确保所有数据都未投影,也就是说,它使用纬度/经度对作为其坐标/坐标空间。由于您的美国 json 是预先投影的,我们可能需要找到另一个:

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

Snippet 不会加载资源,而是 here's 一个 bl.ock,代码如下所示:

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

})

【讨论】:

  • 非常感谢。我会尝试这些并在完成后添加评论。
猜你喜欢
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多