eclipse 地图上的线条未在 d3.js 地图中按比例绘制
Lines on eclipse map not drawing to scale in d3.js map
我有以下美国地图,显示了日食发生时位于或靠近日食区的每个县当前预测的天气状况。我希望能够显示指示整体区域的北部、南部和中心线的线,但我无法正确缩放它们。
显示的红线应该是北线,但未按比例绘制。
这是代码,有什么想法吗?最底部的线
svg.append('path').datum(feature.geometry).attr('class', 'mine').attr("d", path2);
这是我要划清界限的地方。
谢谢
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.counties {
fill: none;
stroke: #ddd;
}
.states {
fill: none;
stroke: #000;
stroke-linejoin: round;
}
.mine {
fill: #f00;
stroke: #f00;
stroke-linejoin: round;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var unemployment = d3.map();
var path = d3.geoPath();
var path2 = d3.geoPath();
var x = d3.scaleLinear()
.domain([1, 10])
.rangeRound([600, 860]);
var color = d3.scaleThreshold()
.domain(d3.range(2, 10))
.range(d3.schemeBlues[9]);
var g = svg.append("g")
.attr("class", "key")
.attr("transform", "translate(0,40)");
g.selectAll("rect")
.data(color.range().map(function (d) {
d = color.invertExtent(d);
if (d[0] == null) d[0] = x.domain()[0];
if (d[1] == null) d[1] = x.domain()[1];
return d;
}))
.enter().append("rect")
.attr("height", 8)
.attr("x", function (d) {
return x(d[0]);
})
.attr("width", function (d) {
return x(d[1]) - x(d[0]);
})
.attr("fill", function (d) {
return color(d[0]);
});
g.append("text")
.attr("class", "caption")
.attr("x", x.range()[0])
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Forecast Conditions");
g.call(d3.axisBottom(x)
.tickSize(13)
.tickFormat(function (x, i) {
//return i ? x : x;
if (i == 0)
return "Clear";
else if (i == 7)
return "Rain";
else
return "";
})
.tickValues(color.domain()))
.select(".domain")
.remove();
d3.queue()
.defer(d3.json, "https://d3js.org/us-10m.v1.json")
.defer(d3.tsv, "unemployment.tsv", function (d) {
var forecast = {
forecastNum: d.forecastNum,
name: d.name,
forecastText: d.forecastText
};
unemployment.set(d.id, forecast);
})
.await(ready);
function ready(error, us) {
if (error) throw error;
var feature = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[136.9522, 45.1172],
[36.8017, 13.6517],
]
}
};
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("fill", function (d) {
return color(d.forecastNum = unemployment.get(d.id).forecastNum);
})
.attr("d", path)
.append("title")
.text(function (d) {
var fc = unemployment.get(d.id);
var s = fc.name + " " + fc.forecastText;
console.log('[' + s + "]");
return s;
});
svg.append("path")
.datum(topojson.mesh(us, us.objects.states, function (a, b) {
return a !== b;
}))
.attr("class", "states")
.attr("d", path)
;
// svg.append("circle").attr("r",50).attr("transform", function() {return "translate(" + projection([-75,43]) + ")";});
svg.append('path').datum(feature.geometry).attr('class', 'mine').attr("d", path2);
}
</script>
您的地图按预期运行,但您可以使其按需要运行。
问题
此文件:https://d3js.org/us-10m.v1.json 已经 投影(它由二维平面上的 x、y 坐标和任意距离单位组成)。
你的线没有投影(它由 longitude/latitude 对组成,代表 3d 地球上的点)。
最终,您在两个不同的坐标系中拥有特征,这会导致问题,因为您以相同的方式从两个不同的坐标系绘制这些点。
确定地理数据是否已在 d3 中投影
要确定您的数据是否已投影,您可以查看:
- geoPath 是否使用空投影?
- 坐标对是有效的经纬度对吗?
查看此文件,您可以看到没有为地理路径分配任何投影(可以像这样完成:d3.geoPath().projection(projection)
)。
此外,您可以看到 topojson 坐标大致在 [0,0]
和 [960,600]
的范围内(转换为 geojson 以查看纯坐标)。这些不是有效的 longitude/latitude 对([+/-180,+/-90]
)。
然而,您的线特征是用 longitude/latitude 对绘制的。这个特征不是投影的(如果用一个特定的椭球体来表示地球,在WGS84中可能会说是"projected",但实际上,这里的WGS84只是表示一个基准面,而不是投影。供参考, WGS84 是 d3 从 long/lats 转换为平面上的点时使用的 datum/reference 椭球体。
发生了什么事?
空投影将采用坐标 [x,y] 和 return 这些完全相同的坐标。 因此,快速放弃已经投影的特征是在查看器 (mapshaper.org) 中查看特征,如果特征是颠倒的,那么您就有投影数据。发生这种情况是因为 svg 坐标 space 将零置于顶部,而 longitude/latitude 对将零置于赤道,-90 进一步向下。
绘制地图时,您有一条线要画:
coordinates: [
[136.9522, 45.1172],
[36.8017, 13.6517],
]
由于您使用的是空投影,因此该线只是从距顶部向下 45 像素且距左侧 137 像素的点延伸到向下 13 像素且距左侧 37 像素的点。 另外,美国大陆的经度是负的,但是当然,它们根本不会显示在您的地图上。
解决方案
您需要对数据使用一致的预测。为此,您可以执行以下操作之一:
找出用于 topojson 的投影并将坐标转换为该投影,这样您就可以在线的坐标数组中使用转换后的坐标,"project" 也可以使用空投影线.
找出用于 topojson 的投影并用 d3.geoProjection 模拟它,此解决方案将使用带有空投影的 geoPath topojson(如当前)和 geoPath使用非常具体的投影将线转换为相同的坐标 space
取消投影 topojson 并对两个特征(线和 topojson)使用相同的投影。
找到美国的未投影 topojson/geojson/etc 并对两个要素(线和 topojson)使用相同的投影。
鉴于您拥有的源文件可能是由 Mike Bostock 制作的,并且他可能使用与他在 d3 中实现的相同的投影公式来创建文件,我们可能会很幸运地弄清楚 d3.geoProjection
我们可以使用将我们的坐标投影到与 topojson(上面的选项 2)相同的坐标 space。
默认 d3.geoAlbers 投影以美国为中心,与大多数其他 d3 投影相比是个例外。默认情况下,这可能包含用于创建 topojson 的关键投影参数,即标准纬线、中心点和旋转。
但是,d3 投影的默认比例预期为 960 x 500 投影平面。 d3 投影的默认翻译假定翻译为 [960/2,500/2]。如果我们更改这些以适应 600 像素高的特征,我们很幸运并且可以将坐标投影到非常相似的坐标 space,如果不是相同的坐标 space:
var projection = d3.geoAlbers();
var scale = d3.geoAlbers().scale(); // get the default scale
projection.scale(scale * 1.2) // adjust for 600 pixels tall
.translate([960/2,600/2] // adjust for 600 pixels tall here too.
var geoPath = d3.geoPath.projection(projection) // use the projection to draw features.
这给了我们一个成功的重叠。 Here is a unprojected data topojson overlaid on the projected topojson. This allows us to achieve your goal by projecting the line on top of the unprojected features, see here(我没有你的数据 tsv,所以 choropleth 只是噪音)。
您还可以使用 geoTransform 而不是 geoProjection 来缩放已经投影的数据,请参阅此 。这将允许您选择不需要 960 像素 x 600 像素的 svg 大小来显示所有功能。
解决方案警告
遗憾的是,选项 1、2 和 3 要求您知道 topojson 中的地理数据是如何投影的,以便您可以模拟或反转它。 如果您没有这些信息,这些选项是不可能的。每当处理使用不同空间参考系统(SRS,或坐标参考系统,CRS)的地理数据时都是如此。
在这些情况下剩下选项 4。可以在此处找到美国未投影的 topojson block (along with good projection parameters). Here is the raw topojson. Here 是您的行的演示(但是此 topojson 数据没有县 ID,因此会破坏您的叶绿素) .
我有以下美国地图,显示了日食发生时位于或靠近日食区的每个县当前预测的天气状况。我希望能够显示指示整体区域的北部、南部和中心线的线,但我无法正确缩放它们。
显示的红线应该是北线,但未按比例绘制。
这是代码,有什么想法吗?最底部的线
svg.append('path').datum(feature.geometry).attr('class', 'mine').attr("d", path2);
这是我要划清界限的地方。
谢谢
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.counties {
fill: none;
stroke: #ddd;
}
.states {
fill: none;
stroke: #000;
stroke-linejoin: round;
}
.mine {
fill: #f00;
stroke: #f00;
stroke-linejoin: round;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var unemployment = d3.map();
var path = d3.geoPath();
var path2 = d3.geoPath();
var x = d3.scaleLinear()
.domain([1, 10])
.rangeRound([600, 860]);
var color = d3.scaleThreshold()
.domain(d3.range(2, 10))
.range(d3.schemeBlues[9]);
var g = svg.append("g")
.attr("class", "key")
.attr("transform", "translate(0,40)");
g.selectAll("rect")
.data(color.range().map(function (d) {
d = color.invertExtent(d);
if (d[0] == null) d[0] = x.domain()[0];
if (d[1] == null) d[1] = x.domain()[1];
return d;
}))
.enter().append("rect")
.attr("height", 8)
.attr("x", function (d) {
return x(d[0]);
})
.attr("width", function (d) {
return x(d[1]) - x(d[0]);
})
.attr("fill", function (d) {
return color(d[0]);
});
g.append("text")
.attr("class", "caption")
.attr("x", x.range()[0])
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Forecast Conditions");
g.call(d3.axisBottom(x)
.tickSize(13)
.tickFormat(function (x, i) {
//return i ? x : x;
if (i == 0)
return "Clear";
else if (i == 7)
return "Rain";
else
return "";
})
.tickValues(color.domain()))
.select(".domain")
.remove();
d3.queue()
.defer(d3.json, "https://d3js.org/us-10m.v1.json")
.defer(d3.tsv, "unemployment.tsv", function (d) {
var forecast = {
forecastNum: d.forecastNum,
name: d.name,
forecastText: d.forecastText
};
unemployment.set(d.id, forecast);
})
.await(ready);
function ready(error, us) {
if (error) throw error;
var feature = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[136.9522, 45.1172],
[36.8017, 13.6517],
]
}
};
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("fill", function (d) {
return color(d.forecastNum = unemployment.get(d.id).forecastNum);
})
.attr("d", path)
.append("title")
.text(function (d) {
var fc = unemployment.get(d.id);
var s = fc.name + " " + fc.forecastText;
console.log('[' + s + "]");
return s;
});
svg.append("path")
.datum(topojson.mesh(us, us.objects.states, function (a, b) {
return a !== b;
}))
.attr("class", "states")
.attr("d", path)
;
// svg.append("circle").attr("r",50).attr("transform", function() {return "translate(" + projection([-75,43]) + ")";});
svg.append('path').datum(feature.geometry).attr('class', 'mine').attr("d", path2);
}
</script>
您的地图按预期运行,但您可以使其按需要运行。
问题
此文件:https://d3js.org/us-10m.v1.json 已经 投影(它由二维平面上的 x、y 坐标和任意距离单位组成)。
你的线没有投影(它由 longitude/latitude 对组成,代表 3d 地球上的点)。
最终,您在两个不同的坐标系中拥有特征,这会导致问题,因为您以相同的方式从两个不同的坐标系绘制这些点。
确定地理数据是否已在 d3 中投影
要确定您的数据是否已投影,您可以查看:
- geoPath 是否使用空投影?
- 坐标对是有效的经纬度对吗?
查看此文件,您可以看到没有为地理路径分配任何投影(可以像这样完成:d3.geoPath().projection(projection)
)。
此外,您可以看到 topojson 坐标大致在 [0,0]
和 [960,600]
的范围内(转换为 geojson 以查看纯坐标)。这些不是有效的 longitude/latitude 对([+/-180,+/-90]
)。
然而,您的线特征是用 longitude/latitude 对绘制的。这个特征不是投影的(如果用一个特定的椭球体来表示地球,在WGS84中可能会说是"projected",但实际上,这里的WGS84只是表示一个基准面,而不是投影。供参考, WGS84 是 d3 从 long/lats 转换为平面上的点时使用的 datum/reference 椭球体。
发生了什么事?
空投影将采用坐标 [x,y] 和 return 这些完全相同的坐标。 因此,快速放弃已经投影的特征是在查看器 (mapshaper.org) 中查看特征,如果特征是颠倒的,那么您就有投影数据。发生这种情况是因为 svg 坐标 space 将零置于顶部,而 longitude/latitude 对将零置于赤道,-90 进一步向下。
绘制地图时,您有一条线要画:
coordinates: [
[136.9522, 45.1172],
[36.8017, 13.6517],
]
由于您使用的是空投影,因此该线只是从距顶部向下 45 像素且距左侧 137 像素的点延伸到向下 13 像素且距左侧 37 像素的点。 另外,美国大陆的经度是负的,但是当然,它们根本不会显示在您的地图上。
解决方案
您需要对数据使用一致的预测。为此,您可以执行以下操作之一:
找出用于 topojson 的投影并将坐标转换为该投影,这样您就可以在线的坐标数组中使用转换后的坐标,"project" 也可以使用空投影线.
找出用于 topojson 的投影并用 d3.geoProjection 模拟它,此解决方案将使用带有空投影的 geoPath topojson(如当前)和 geoPath使用非常具体的投影将线转换为相同的坐标 space
取消投影 topojson 并对两个特征(线和 topojson)使用相同的投影。
找到美国的未投影 topojson/geojson/etc 并对两个要素(线和 topojson)使用相同的投影。
鉴于您拥有的源文件可能是由 Mike Bostock 制作的,并且他可能使用与他在 d3 中实现的相同的投影公式来创建文件,我们可能会很幸运地弄清楚 d3.geoProjection
我们可以使用将我们的坐标投影到与 topojson(上面的选项 2)相同的坐标 space。
默认 d3.geoAlbers 投影以美国为中心,与大多数其他 d3 投影相比是个例外。默认情况下,这可能包含用于创建 topojson 的关键投影参数,即标准纬线、中心点和旋转。
但是,d3 投影的默认比例预期为 960 x 500 投影平面。 d3 投影的默认翻译假定翻译为 [960/2,500/2]。如果我们更改这些以适应 600 像素高的特征,我们很幸运并且可以将坐标投影到非常相似的坐标 space,如果不是相同的坐标 space:
var projection = d3.geoAlbers();
var scale = d3.geoAlbers().scale(); // get the default scale
projection.scale(scale * 1.2) // adjust for 600 pixels tall
.translate([960/2,600/2] // adjust for 600 pixels tall here too.
var geoPath = d3.geoPath.projection(projection) // use the projection to draw features.
这给了我们一个成功的重叠。 Here is a unprojected data topojson overlaid on the projected topojson. This allows us to achieve your goal by projecting the line on top of the unprojected features, see here(我没有你的数据 tsv,所以 choropleth 只是噪音)。
您还可以使用 geoTransform 而不是 geoProjection 来缩放已经投影的数据,请参阅此
解决方案警告
遗憾的是,选项 1、2 和 3 要求您知道 topojson 中的地理数据是如何投影的,以便您可以模拟或反转它。 如果您没有这些信息,这些选项是不可能的。每当处理使用不同空间参考系统(SRS,或坐标参考系统,CRS)的地理数据时都是如此。
在这些情况下剩下选项 4。可以在此处找到美国未投影的 topojson block (along with good projection parameters). Here is the raw topojson. Here 是您的行的演示(但是此 topojson 数据没有县 ID,因此会破坏您的叶绿素) .