编程语言
首页 > 编程语言> > javascript – 缩放d3 v4地图以适应SVG(或根本不适合)

javascript – 缩放d3 v4地图以适应SVG(或根本不适合)

作者:互联网

我试图让这个美国规模的地图变小.要么是我的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使用未投影的数据,或使用lat long对. 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());).

此问题中的特定文件是预先投影以适合960×600地图,因此这是空投影的理想选择 – 它的设计考虑了尺寸.它的单位是像素,所有坐标都在所需的尺寸范围内.但是,大多数投影几何体使用具有米等单位的坐标系,因此要素坐标的边界框可能是数百万个单位.在这些情况下,空投影将不起作用 – 它将地图单位值转换为没有缩放的像素值.

对于d3,空投影通常与geojson / topojson一起使用,该投影使用d3投影预先投影以适合指定的视口.有关示例,请参阅command line cartography(该示例使用未投影的源文件 – 在投影数据上使用d3投影产生的相同问题同时适用于浏览器和命令行).预先投影文件以使用空投影的主要优点是performance.

geoIdentity

如果您只需要缩放和居中功能,则可以使用geoIdentity.这是实现geoTransform但使用标准投影方法,如scale,translate,最重要的是 – fitSize / fitExtent.因此,我们可以将投影设置为地理位置:

var projection = d3.geoIdentity();

这与当前使用的空投影相同,它从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 / canvas坐标空间中,y = 0位于顶部,y值随着向下移动而增加.所以,我们经常需要翻转y轴:

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

此特定数据集:https://d3js.org/us-10m.v1.json使用d3投影进行投影,因此当d3投影投影到svg或画布坐标空间时,其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.

Mike Bostock开始:

But what if your geometry is already planar? That is, what if you just
want to take projected geometry, but still translate or scale it to
fit the viewport?

You can implement a custom geometry transform to gain complete control
over the projection process.

假设您不想更改投影类型,使用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甚至是mapshaper,您可以更改数据的投影,使其“投影”为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时没有使用任何投影.

标签:geojson,topojson,javascript,svg,d3-js
来源: https://codeday.me/bug/20190918/1810975.html