javascript - 缩放d3 v4映射以适合SVG(或完全适合)

标签 javascript d3.js svg geojson topojson

我正在尝试缩小美国地图。要么是我的SVG,要么是手动的。

这是我的代码中最简单的:

function initializeMapDifferent(){
    var svg = d3.select("#map").append("svg")
        .attr("width", 1000)
        .attr("height", 500);



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

        svg.append("g")
            .attr("class", "states")
            .selectAll("path")
            .data(topojson.feature(us, us.objects.states).features)
            .enter().append("path")
            .attr("fill", "gray")
            .attr("d", d3.geoPath());
    });
}


我已经尝试过类似的东西:

  var path = d3.geoPath()
  .projection(d3.geoConicConformal()
      .parallels([33, 45])
      .rotate([96, -39])
      .fitSize([width, height], conus));


但是每次我在路径变量中添加任何内容时,我都会从D3的内部收到NAN错误。谢谢你的帮助!

最佳答案

为什么数据无法正确投影

关键问题是您的数据已经被投影。 D3 geoProjections使用未投影的数据或成对的数据。 WGS84基准中的数据。实质上,d3 geoProjection会采用球面坐标并将其转换为平面笛卡尔x,y坐标。

您的数据与此不符-它已经是平面的。您可以看到最明显的原因是阿拉斯加不在应有的位置(除非有人更改了阿拉斯加的长对,这不太可能)。已经投影的数据的其他迹象和症状可能是覆盖整个星球以及NaN错误的特征。

由于这是一个复合投影,因此很难取消投影,但是您可以在d3.js中显示已经投影的数据。

“正在投影”已投影的数据

空投影:

最简单的是,您可以将投影定义为null:

var path = d3.geoPath(null);


这将从geojson几何中获取x,y数据,并将其显示为x,y数据。但是,如果您的x,y坐标超过了svg的宽度和高度,则地图将不会包含在svg中(如您在示例中使用.attr("d", d3.geoPath());所找到的那样)。

这个问题中的特定文件已预先投影以适合960x600地图,因此这对于空投影非常理想-设计时要考虑到尺寸。它的单位是像素,所有坐标都在所需的尺寸之内。但是,大多数投影的几何图形都使用以米为单位的坐标系,因此要素坐标的边界框可能跨越数百万个单位。在这些情况下,空投影将不起作用-它将地图单位值转换为没有缩放比例的像素值。

对于d3,geojson / topojson通常使用空投影,该投影使用d3投影进行预投影以适合指定的视口。有关示例,请参见command line cartography(示例使用未投影的源文件-在投影数据上使用d3投影所引起的相同问题在浏览器和命令行中均适用)。预投影文件以用于空投影的主要优点是performance

地理身份

如果只需要缩放要素并使其居中,则可以使用geoIdentity。这实现了geoTransform,但具有标准投影方法,例如scaletranslate,最重要的是-fitSize / fitExtent。因此,我们可以将投影设置为geoIdentity:

var projection = d3.geoIdentity();


当前,这与上面使用的null投影相同,它从geojson几何中获取x,y数据,并将其显示为x,y数据,且不进行任何转换-将geojson中的每个坐标视为像素坐标。但是,我们可以将fitSize应用于此(或fitExtent),这将自动缩放数据并将其转换为指定的边界框:

var projection = d3.geoIdentity()
  .fitSize([width,height],geojsonObject);


要么

var projection = d3.geoIdentity()
  .fitExtent([[left,top],[right,bottom]], geojsonObject);


请记住,大多数投影数据使用地理惯例,y = 0位于底部,y值随着向北移动而增加。在svg /画布坐标空间中,y = 0位于顶部,y值随着向下移动而增加。因此,我们经常需要翻转y轴:

var projection = d3.geoIdentity()
 .fitExtent([width,height],geojsonObject)
 .reflectY(true);


这个特定的数据集:https://d3js.org/us-10m.v1.json用d3投影投影,因此当d3投影投影到svg或canvas坐标空间时,其y轴已被翻转。

geoIdentity演示



var width = 600;
var height = 300;

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



d3.json("https://d3js.org/us-10m.v1.json", function (error, us){
  var featureCollection = topojson.feature(us, us.objects.states);
  
  var projection = d3.geoIdentity()
  .fitExtent([[50,50],[600-50,300-50]], featureCollection)

  var path = d3.geoPath().projection(projection)
  
  svg.append("g")
    .attr("class", "states")
    .selectAll("path")
    .data(featureCollection.features)
    .enter().append("path")
    .attr("fill", "gray")
    .attr("d", path);
  
});

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.6.0/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/2.2.0/topojson.js"></script>





geoTransform

如果您想更多地控制数据的显示方式,可以使用geoTransform

Mike Bostock


  但是,如果您的几何图形已经是平面的,该怎么办?也就是说,如果你只是
  想要采用投影的几何体,但仍将其转换或缩放为
  适合视口?
  
  您可以实施自定义几何变换以获得完全控制
  在投影过程中。


假设您不想更改投影类型,使用geoTransform相对简单。例如,如果要缩放数据,则可以使用geoTransform来实现简短的缩放功能:

function scale (scaleFactor) {
    return d3.geoTransform({
        point: function(x, y) {
            this.stream.point(x * scaleFactor, y  * scaleFactor);
        }
    });
}

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


不过,这会在缩小时将所有内容缩放到左上角。为了使内容居中,可以添加一些代码来使投影居中:

function scale (scaleFactor,width,height) {
    return d3.geoTransform({
        point: function(x, y) {
            this.stream.point( (x - width/2) * scaleFactor + width/2 , (y - height/2) * scaleFactor + height/2);
        }
    });
    }

var path = d3.geoPath().projection(scale(0.2,width,height))


geoTransform演示:

这是使用您的文件和geoTransform的示例:



var width = 600;
var height = 300;

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


function scale (scaleFactor,width,height) {
  return d3.geoTransform({
    point: function(x, y) {
      this.stream.point( (x - width/2) * scaleFactor + width/2 , (y - height/2) * scaleFactor + height/2);
    }
  });
}
  
d3.json("https://d3js.org/us-10m.v1.json", function (error, us){
  var path = d3.geoPath().projection(scale(0.2,width,height))
 
  svg.append("g")
    .attr("class", "states")
    .selectAll("path")
    .data(topojson.feature(us, us.objects.states).features)
    .enter().append("path")
    .attr("fill", "gray")
    .attr("d", path);
  
});

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.6.0/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/2.2.0/topojson.js"></script>





取消投影数据

在某些情况下,此方法很有用。但是,这需要您了解用于创建数据的投影。使用QGIS / ArcGIS甚至是地图整形器,您可以更改数据的投影,以便将其“投影”为WGS84(又名EPSG 4326)。转换后,您将拥有未投影的数据。

在Mapshaper中,使用shapefile非常简单,将shapefile的.dbf,.shp和.prj文件拖到窗口中。在mapshaper中打开控制台,然后键入proj wgs84。

如果您不知道用于创建数据的投影,则无法对其进行投影-您不知道应用了什么转换以及使用了哪些参数。

一旦取消投影,就可以正常使用常规d3投影,因为在正确的坐标空间中有坐标:经度纬度对。

如果您还具有未投影的数据并想将两者混合在同一地图中,则非投影很有用。或者,您可以投影未投影的数据,以便两者都使用相同的坐标系。将地图中不匹配的坐标系与d3组合起来并不容易,而d3可能不是正确的工具。如果您确实要使用d3复制特定的投影以匹配已经使用未投影特征进行投影的特征,则此question可能会有用。

您如何判断您的数据是否已经投影?

您可以检查一下要素的几何形状是否符合纬度和经度的限制。例如,如果您要登录:

d3.json("https://d3js.org/us-10m.v1.json", function (error, us){
   console.log(topojson.feature(us, us.objects.states).features);
});


您将很快看到值超过+/- 90度N / S和+/- 180度E / W。不可能是长对。

或者,您可以将数据导入到诸如mapshaper.org之类的在线服务,并与您知道未投影(或使用WGS84“投影”)的另一个topojson / geojson进行比较。

如果处理geojson,您可能会很幸运地看到一个定义投影的属性,例如:"name": "urn:ogc:def:crs:OGC:1.3:CRS84"(CRS代表坐标参考系统)或EPSG编号:EPSG:4326(EPSG代表欧洲石油测量集团) 。

另外,如果您的数据投影使用的是空投影而不是标准投影(进行缩放/缩小以确保您不在错误的区域中),则可能是在处理投影数据。同样,如果您的视口完全被一项功能所覆盖(并且您没有放大)。 NaN坐标也是潜在的指标。但是,这些预测数据的最后指标也可能意味着其他问题。

最后,数据源还可能指示数据已经在元数据中或已被投影使用:通过查看此block,我们可以看到在定义geoPath时未使用任何投影。

关于javascript - 缩放d3 v4映射以适合SVG(或完全适合),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58015247/

相关文章:

javascript - 如何在 PHP 联系表单中附加选择列表

javascript - 重新加载页面但不要在 IE 中滚动到顶部

javascript - 0 用 d3 系列数据填充空值

html - SVG tspan 元素 + 字体系列样式?

javascript - 动态 Div ID 和在其中创建元素

javascript - javascript 的 Referrer 属性在幕后是如何工作的

javascript - 当鼠标悬停在 D3 上时显示文本

jquery - D3.js 仅在一行上绘制条形图矩形

python - 在 python 或 javascript 中将 RDF 转换为 SVG?

google-chrome - 使用 Google Web Fonts 优先使用 SVG 字体