我可以成功获取要绘制的美国地图,但是我的数据点却不能。 (我了解d3.js在v5中进行了一些重大更改,因此请注意,先前提出的类似问题不适用)
$(document).ready(function () {
var us = d3.json('https://unpkg.com/us-atlas@1/us/10m.json');
var meteoriteData = d3.json('https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/meteorite-strike-data.json');
var svg = d3.select("svg")
.style("width", "1110px")
.style("height", "714px");
var path = d3.geoPath();
Promise.all([us, meteoriteData]).then(function (values) {
var map = values[0];
console.log("map", map);
var meteoriteData = values[1];
console.log("meteoriteData", meteoriteData);
svg.append("g")
.attr("fill", "#ccc")
.selectAll("path")
.data(topojson.feature(map, map.objects.states).features)
.enter().append("path")
.attr("d", path),
svg.append("path")
.datum(topojson.mesh(map, map.objects.states, (a, b) => a !== b))
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("pointer-events", "none")
.attr("d", path),
svg.selectAll("circle")
.data(meteoriteData)
.enter()
.append("circle")
.attr("class", "circles")
.attr("cx", function (d) { return ([d.geometry.coordinates[0], d.geometry.coordinates[1]])[1]; })
.attr("cy", function (d) { return ([d.geometry.coordinates[0], d.geometry.coordinates[1]])[0]; })
.attr("r", "1px");
});
});
可以在这里找到工作副本。https://codepen.io/lady-ace/pen/PooORoy
答案 0 :(得分:0)
这里有很多问题:
首先,使用时
svg.selectAll("circle")
.data(meteoriteData)
selectAll().data()
要求您传递函数或数组。在您的情况下,您需要将数据数组传递给它-但是,meteoriteData
是一个对象。这是一个具有以下结构的geojson要素集合:
{
"type": "FeatureCollection",
"features": [
/* features */
]
}
所有单独的geojson特征均位于该对象内部的数组中。要获得一系列特征,在这种情况下代表流星的特征,我们需要使用:
svg.selectAll("circle")
.data(meteoriteData.features)
现在,我们可以为要素集中的每个要素创建一个圆圈。如果这样做,您可以在检查SVG元素时找到圆,但是它们放置不正确。
如果进行上述更改,则在正确的位置看不到圆圈。您在此处未正确放置圆圈:
.attr("cx", function (d) { return ([d.geometry.coordinates[0], d.geometry.coordinates[1]])[1]; })
这与:
.attr("cx", function(d) { return d.geometry.coordinates[1]; })
这里有两个问题:一,geojson是[longitude,latitude]
或[x,y]
(您在这里得到y坐标,但是设置了x值)。
但是,更大的问题是您不打算投影数据。这是来自geojson的原始坐标:
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-113,
54.21667
]
...
您正在获取经度并将其直接转换为像素值。但是您的geojson使用3D坐标空间(3D地球上的未投影点),单位为度。如果我们将其简单地转换为像素cx = -113
,则您的圆圈将显示在屏幕外SVG的左侧。
您需要投影数据,为此,我们将定义一个投影函数并使用类似以下内容的
.attr("cx", function(d) { return projection(d.geometry.coordinates)[0] })
这将同时获得经度和纬度,并将它们传递给投影函数,然后获取返回的x值并将其设置为cx的值。
投影在3维空间(地球上的点或长/纬对上的点)中以度为单位的未投影坐标,并在2维空间中以像素为单位返回点。
但是,这现在把我们带到了最困难的部分:
您应该使用哪种投影?
我们需要将点与已经绘制的美国要素对齐,但是您没有为美国各州定义投影-如果您不向d3.geoPath提供投影,则它将使用空投影,它采用提供的坐标并将其绘制在SVG上,就好像它们是像素坐标一样。不进行任何转换。我知道您的美国地图是投影的,因为阿拉斯加不在原处,topojson中的坐标值在经度上超过+/- 180度,在纬度上超过+/- 90,并且地图看起来像用Albers投影进行投影。
如果geoPath并未使用d3投影来投影数据,而是像投影一样对数据进行了绘制,则数据将被预先投影-此topojson存储已投影的点。
我们有投影数据(美国各州)和非投影数据(流星雨),将它们混合始终是一个挑战。
这里的挑战是创建一个投影,该投影复制用于创建美国各州数据集的投影函数。我们可以复制该投影,因为它是一个有问题的文件,会引发很多问题。 here说明了如何执行此操作。但这比应该的要复杂得多:混合投影数据和非投影数据是繁重的,不灵活的,并且比需要的还要复杂。
我建议您同时使用美国和流星的非投影数据:
var projection = d3.geoAlbersUsa(); // create an Albers USA projection
var path = d3.geoPath().projection(projection); // set the projection for the path
我们可以以相同的方式绘制路径,只要找到未投影的美国topojson / geojson,并且可以使用以下方式放置点:
.attr("cx", function(d) { return projection(d.geometry.coordinates)[0]; })
.attr("cy", function(d) { return projection(d.geometry.coordinates)[1]; })
至于找到一个非预期的美国topojson,here是一个。
这是使用上述方法并投影所有数据的working version(我也过滤了要素以消除没有坐标的要素,因为美国Albers可能会破坏这些要素,因为它是复合投影)。