代码之家  ›  专栏  ›  技术社区  ›  JJF

日食地图上的线条没有按d3中的比例绘制。js地图

  •  1
  • JJF  · 技术社区  · 8 年前

    我有下面的美国地图,显示了当前预测的日食时日食区内或附近每个县的天气状况。我希望能够显示出代表全食带北部、南部和中心线的线,但我无法使它们正确缩放。

    Map of weather in the eclipse zone

    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>
    
    1 回复  |  直到 8 年前
        1
  •  0
  •   Andrew Reid    7 年前

    您的地图按预期运行,但您可以使其按预期运行。

    此文件: https://d3js.org/us-10m.v1.json 已经 投影(由2d平面上的x、y坐标组成,具有任意距离单位)。

    最终,您在两个不同的坐标系中拥有特征,这会导致问题,因为您以相同的方式从两个不同的坐标系绘制这些点。

    要确定您的数据是否已被投影,您可以查看:

    • 地理路径是否使用空投影?
    • 坐标对是否有效经纬度对?

    查看此文件,您可以看到没有将投影指定给地质路径(这将像以下操作一样完成: d3.geoPath().projection(projection)

    此外,您可以看到topojson坐标大致在 [0,0] [960,600] [+/-180,+/-90] ).

    然而,线要素是用经度/纬度对绘制的。该特征不是投影的(如果使用特定球体表示地球,则可以在WGS84中称其为“投影”,但实际上,在这种情况下,WGS84仅代表一个基准,而不是投影。作为参考,WGS84是d3在从长/纬度转换为平面上的点时使用的基准/参考球体)。

    因此,快速放弃已经投影的特征是在查看器(mapshaper.org)中查看特征,如果特征是颠倒的,那么就有投影数据。这是因为svg坐标空间将零放置在顶部,而经度/纬度对将零放置在赤道,再向下-90。

           coordinates: [
                [136.9522, 45.1172],
                [36.8017, 13.6517],
            ]
    

    当您使用空投影时,直线只从顶部向下45个像素和左侧137个像素的点到向下13个像素和左侧37个像素的点。 .

    解决方案

    1. 计算出用于topojson的投影,并将坐标转换为该投影,以便可以在直线的坐标数组中使用转换后的坐标,并使用空投影“投影”直线。

    2. 找出用于topojson的投影,并用d3进行仿真。geoProjection,该解决方案将使用一个对topojson(当前)具有空投影的geoPath,以及一个具有非常特定投影的geoPath,以将线转换为相同的坐标空间

    3. 取消投影topojson,并对两个特性(line和topojson)使用相同的投影。

    考虑到您拥有的源文件可能是由Mike Bostock创建的,并且他可能使用了与他在d3中实现的相同的投影公式来创建文件,我们可能会幸运地发现是什么 d3.geoProjection

    默认d3。geoAlbers投影以美国为中心,与大多数其他d3投影相比,这是一个例外。默认情况下,这可能包含用于创建topojson的关键投影参数,即标准平行线、中心点和旋转。

    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 here (我没有你的tsv数据,所以choropleth只是噪音)。

    您还可以使用geoTransform而不是geoProjection来缩放已投影的数据,请参见 answer

    解决方案警告

    遗憾的是,选项1、2和3要求您知道topojson中的地理数据是如何投影的,以便您可以模拟或反转它。 您没有这些信息,这些选项是不可能的。无论何时处理使用不同空间参考系(SRS或坐标参考系,CRS)的地理数据,都是如此。

    在这些情况下,剩下选项4。状态的未投影topojson可以在这里找到 block topojson . Here ).