缩放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使用未投影的数据或成对的数据。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投影进行预投影以适合指定的视口。有关示例(示例使用未投影的源文件在投影数据上使用d3投影所引起的相同问题在浏览器和命令行中均适用)。预投影文件以用于null投影的主要优点是性能。
地理身份
如果只需要缩放要素并使其居中,则可以使用geoIdentity。这实现了geoTransform,但具有标准的投影方法,例如和scale
,translate
最重要的是-
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);
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
。
但是,如果您的几何图形已经是平面的,该怎么办?也就是说,如果您只想采用投影的几何形状,但仍将其平移或缩放以适合视口怎么办?
您可以实施自定义几何变换,以完全控制投影过程。
geoTransform
假设 您不想更改投影类型,使用a相对简单。例如,如果要缩放数据,可以使用以下方法实现一个简短的缩放功能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的示例:
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复制特定的投影以匹配已经使用未投影特征进行投影的特征
您如何判断您的数据是否已经投影?
您可以检查一下要素的几何形状是否符合纬度和经度的限制。例如,如果您要登录:
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坐标也是潜在的指标。但是,这些预测数据的最后指标也可能意味着其他问题。
以上是 缩放d3 v4映射以适合SVG(或完全适合) 的全部内容, 来源链接: utcz.com/qa/409469.html