缩放d3 v4Map以适应SVG(或根本不适应)

zmeyuzjn  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(133)

我正试图使这张美国Map的比例缩小。无论是我的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错误。感谢您的帮助!

piztneat

piztneat1#

为什么数据不能正常投影

关键问题是您的数据 * 已经 * 投影。D3 geoProjection使用未投影的数据或纬度对中的数据。WGS 84基准面中的数据。本质上,d3 geoProjection采用球面坐标并将其转换为平面坐标x,y坐标。
你的数据不符合这一点-它已经是平面的。你可以看到最明显的,因为阿拉斯加不是它应该在的地方(除非有人改变了阿拉斯加的纬度对,这是不太可能的)。其他的迹象和症状已经预测的数据可能是一个覆盖整个地球的特征,NaN错误。
这是一个复合投影,很难取消投影,但你可以在d3.js中显示已经投影的数据。

“投影”已投影数据

投影:

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

var path = d3.geoPath(null);

字符串
这将从geojson几何中获取x,y数据并将其显示为x,y数据。但是,如果x,y坐标超过svg的宽度和高度,则Map将不会包含在svg中(如您在.attr("d", d3.geoPath());示例中所发现的那样)。
这个问题中的特定文件是预先投影的,以适应960 x600Map,因此这是空投影的理想选择-它的设计考虑到了尺寸。它的单位是像素,所有坐标都在所需的尺寸范围内。但是,大多数投影几何使用单位为米的坐标系,因此,特征坐标的边界框可能是数百万个单位。在这些情况下,空投影将不起作用-它将Map单位值转换为像素值,而不进行缩放。

  • 对于d3,空投影通常与geojson/topojson一起使用,geojson/topojson使用d3投影进行预投影以适应指定的视口。(该示例使用未投影的源文件-在投影数据上使用d3投影所产生的问题在浏览器和命令行中都适用)。预投影文件以用于空投影的主要优点是performance。*

地理标识

如果你只需要缩放和居中特征,你可以使用geoIdentity。这是实现geoTransform,但使用标准投影方法,如scaletranslate,最重要的是-fitSize/fitExtent。所以,我们可以将投影设置为geoIdentity:

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投影进行投影,因此其y轴已被翻转,因为d3投影投影到svg或canvas坐标空间。*
    geoIdentity Demo
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 Demo

下面是一个使用文件和geoTransform的示例:

<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,你可以改变数据的投影,使其被“投影”为WGS 84(又名EPSG 4326)。一旦转换,你就有了未投影的数据。

  • 在Mapshaper中,使用shapefiles非常简单,将shapefiles的.dbf、.shp和.prj文件拖到窗口中。在mapshaper中打开控制台并输入proj wgs 84。*

如果您不知道用于创建数据的投影,则无法取消投影-您不知道应用了什么转换以及使用了什么参数。
一旦取消投影,您可以正常使用常规的d3投影,因为您在正确的坐标空间中有坐标:经度纬度对。

如果您还有未投影的数据,并且希望在同一Map中混合这两种数据,则取消投影非常有用。或者,您可以投影未投影的数据,以便两者使用相同的坐标系。将Map中不匹配的坐标系与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。不太可能是lat long对。
或者,您可以将您的数据导入到在线服务(如mapshaper.org),并与您知道未投影(或使用WGS 84“投影”)的另一个topojson/geojson进行比较。
如果处理geojson,您可能会幸运地看到定义投影的属性,例如:"name": "urn:ogc:def:crs:OGC:1.3:CRS84"(CRS代表坐标参考系统)或EPSG编号:EPSG:4326(EPSG代表欧洲石油调查组)。
此外,如果您的数据投影为空投影,而不是标准投影(缩放/缩小以确保您没有在错误的区域中查看),则可能正在处理投影数据。同样,如果您的视口完全被一个要素覆盖(并且您没有放大)。NaN坐标也是一个潜在的指示器。然而,投影数据的最后这些指示器也可能意味着其他问题。
最后,数据源还可能指示数据已经在Meta数据中投影或如何使用:查看此block,我们可以看到定义geoPath时没有使用投影。

ipakzgxi

ipakzgxi2#

如果你像我一样从datav.aliyun.com这个地方下载了geoJson数据,你可能需要反转数据才能在d3中使用,像这样:

mapJson.features = mapJson.features.map(function (feature) {
    return turf.rewind(feature, { reverse: true });
});

字符串
您需要安装草皮:npm i @turf/rewind

相关问题