使用d3.js和TopoJSON创建地图,使用Albers Siberia投影。

6
我正在尝试使用d3.js制作Choropleth地图,但我在一开始就遇到了困难。我找到了一个Shapefile文件,并从中生成了GeoJSON和TopoJson文件,就像这里所示here。该地图采用Albers-Siberia投影。关于这个投影,我发现以下内容:
投影方式:阿尔伯斯等面积圆锥投影
单位:米
椭球体:Krasovsky
中央经线:105
标准平行线1:52
标准平行线2:64
参考纬度:0
东偏移:18500000
北偏移:0
PROJ.4:+proj=aea +lat_1=52 +lat_2=64 +lat_0=0 +lon_0=105 +x_0=18500000 +y_0=0 +ellps=krass +units=m +towgs84=28,-130,-95,0,0,0,0 +no_defs

地图信息: "阿尔伯斯-西伯利亚",9,1001,7,105,0,64,52,18500000,0。

最终我得到了这段代码,但它什么也没做(甚至还卡住了),出了什么问题?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Choropleth</title>
    <script type="text/javascript" src="d3/d3.v3.js"></script>
    <script type="text/javascript" src="d3/queue.v1.min.js"></script>
    <script type="text/javascript" src="d3/topojson.v0.min.js"></script>
</head>
<body>
    <h1>My Choropleth</h1>
    <script type="text/javascript">

        var width = 960,
            height = 500;

        var svg = d3.select("body").append("svg")
                    .attr("width", width)
                    .attr("height", height);

        var pr = d3.geo.albers()
            .center([105,0])
            .parallels([52, 64])
            .scale(1000);


        var path = d3.geo.path().projection(pr);

        d3.json("map_rus_topo.json", function(error, map) {
         svg.append("path")
          .datum(topojson.object(map, map.objects.map_rus))
          .attr("d", path);
        });

    </script>
</body>

你可以在这里找到所有的JSON文件。
还有一个问题:我如何引用我自己的TopoJson文件中region字段的值?

当您运行代码时会发生什么?您是否会收到任何错误消息或输出? - Lars Kotthoff
控制台没有错误。 - KoGor
JSON文件的大小可能是一个问题。你尝试简化它们或者等待更长时间了吗? - Lars Kotthoff
是的,我尝试了使用简化文件(337KB),但仍然无法工作。 - KoGor
2个回答

17

第一个问题是您的GeoJSON文件不是以度数[经度°,纬度°]的方式表示,也就是说,它不符合EPSG:4326或WGS 84标准。如果要将您的GeoJSON文件转换为WGS 84,则需要首先创建一个投影文件,比如albers.prj,这样您就可以告诉OGR源投影是什么。

+proj=aea +lat_1=52 +lat_2=64 +lat_0=0 +lon_0=105 +x_0=18500000 +y_0=0 +ellps=krass +units=m +towgs84=28,-130,-95,0,0,0,0 +no_defs

然后,通过将GeoJSON文件转换为WGS 84来“反投影”:

ogr2ogr -f GeoJSON -s_srs albers.prj -t_srs EPSG:4326 map_rus_wgs84_geo.json map_rus_geo.json

现在您可以将数据转换为WGS 84格式的TopoJSON,而不是使用投影坐标。我还做了一些简化处理:

topojson -o map_rus_wgs84_topo.json -s 1e-7 -- russia=map_rus_wgs84_geo.json

第二个问题是你在D3中的投影定义不正确。d3.geo.albers投影具有默认旋转和中心,适用于以美国为中心的地图,因此除了定义中心之外,您还需要覆盖默认旋转。实际上,+lon_0(中央子午线)投影参数映射到投影的旋转而不是投影的中心。给出:

var projection = d3.geo.albers()
    .rotate([-105, 0])
    .center([-10, 65])
    .parallels([52, 64])
    .scale(700)
    .translate([width / 2, height / 2]);

(我用中心参数进行了调整,将俄罗斯放在视口的中心位置。如果您愿意,可以自动计算。) 您现在应该看到像这样的东西:

Albers Siberia

在TopoJSON中也可以使用投影(笛卡尔)坐标,然后定义一个具有空(identity)投影的d3.geo.path,但我会留下另一个问题来讨论。


此外,您能否解释一下这段代码中的 make --russia= 是什么意思:> topojson -o map_rus_wgs84_topo.json -s 1e-7 -- russia=map_rus_wgs84_geo.json。我还不太明白关于中心坐标的问题,您提供的链接是关于自动计算比例尺和平移参数的,或者我可能漏掉了什么。 - KoGor
1
请参阅TopoJSON命令行参考以获得完整的说明。在上面的示例中,我使用-o来指定输出文件名,-s来指定球面度量单位下的简化阈值,然后是输入文件跟随--分隔符。只有一个输入文件(map_rus_wgs84_geo.json),通过在前面加上russia=,我可以设置生成拓扑图中对象的名称。这就是为什么在链接的示例中,我引用了 russia.objects.russia的原因。 - mbostock
1
至于投影的中心坐标[-10°,65°],我只是根据外观随意设置的。中心坐标会随着其他所有内容一起旋转,因此在圆锥投影中,通常中心经度(这里为-10°)将接近零。同样,中心纬度将大致处于与您的平行线(52°和64°)相同的范围内,以最小化圆锥投影的失真。 - mbostock
1
感谢您,已添加描述。 - mbostock
2
topojson 命令行工具假定 GeoJSON 输入为 UTF-8,Shapefile 输入为 Windows1252,并始终生成 UTF-8 输出。如果您的 shapefile 输入使用不同的编码方式,则可以使用 --shapefile-encoding 参数,但这种情况非常罕见。如果您的 GeoJSON 输入不是 UTF-8,请使用 ogr2ogr -lco ENCODING=UTF-8 进行修复。 - mbostock
显示剩余6条评论

0

由于@mbostock的回答,我在此添加一个解答。我的 d3.js 地图没有显示或者用任何投影方式显示不正确,后来通过这个帖子意识到问题是因为它不符合 WGS-84 地理坐标系。根据 Datawrapper 的说法,TopoJSON 或 GeoJSON 需要使用 WGS-84 坐标系。

幸运的是,2022 年有一种更快更容易的方法来解决这个问题。只需上传您的地理文件到 Mapshaper,打开控制台,输入 proj wgs84 并按 'enter' 键。若要验证投影是否已更改,请输入 info 并再次按 enter 键。你应该在出现的代码中找到以下行:Proj.4:+ proj = longlat + datum = WGS84。这并不适用于所有情况 - 这取决于您的形状文件最初使用的坐标系。但是值得一试。

在 Mapshaper 中预览地图可能会在转换后发生变化,但只要您在 d3.js JavaScript 代码中正确投影地图,最终结果就不会失真。我不得不回到下载我的 shapefile 的位置,以获取所需的 d3 投影函数信息。

我希望这能帮助其他有同样困扰的人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接