【问题标题】:Merging topojson using topomerge messes up winding order使用topomerge合并topojson会打乱缠绕顺序
【发布时间】:2023-05-08 06:56:01
【问题描述】:

我正在尝试创建一个自定义世界地图,其中国家被合并到区域中,而不是单独的国家。不幸的是,由于某种原因,整个过程中的缠绕顺序似乎有些混乱。

作为基础数据,我使用的是自然地球10m_admin_0_countries 可用的形状文件here。作为合并国家/地区的标准,我有一个如下所示的查找地图:

const countryGroups = {
  "EUR": ["ALA", "AUT", "BEL"...],
  "AFR": ["AGO", "BDI", "BEN"...],
  ...
}

合并我正在使用的形状topojson-client。由于我希望获得比 CLI 命令提供的更高级别的控制,因此我编写了一个脚本。它遍历查找图并挑选出属于一个组的所有 topojson 特征并将它们合并为一个形状,并将生成的合并特征放入一个 geojson 框架中:

const topojsonClient = require("topojson-client");
const topojsonServer = require("topojson-server");

const worldTopo = topojsonServer.topology({
  countries: JSON.parse(fs.readFileSync("./world.geojson", "utf-8")),
});

const geoJson = {
  type: "FeatureCollection",
  features: Object.entries(countryGroups).map(([region, ids]) => {
    const relevantCountries = worldTopo.objects.countries.geometries.filter(
      (country, i) =>
        ids.indexOf(country.properties.ISO_A3) >= 0
    );

    return {
      type: "Feature",
      properties: { region, countries: ids },
      geometry: topojsonClient.merge(worldTopo, relevantCountries),
    };
  }),
};

到目前为止,一切都运行良好(据称)。当我尝试使用github gist(或任何其他可视化工具,如 vega lite)来可视化地图时,形状似乎都一团糟。我怀疑我在合并功能期间做错了什么,但我无法弄清楚它是什么。

当我尝试使用 CLI 执行相同操作时,它似乎工作正常。但由于我需要对合并进行更多控制,因此仅使用 CLI 并不是一个真正的选择。

【问题讨论】:

    标签: geojson shapefile topojson vega-lite vega


    【解决方案1】:

    最后一个特征,称为“世界”,应该包含所有剩余个国家,但相反,它包含所有国家,句号。您可以在以下展示中看到这一点。

    var w = 900,
      h = 300;
    
    var projection = d3.geoMercator().translate([w / 2, h / 2]).scale(100);
    var path = d3.geoPath().projection(projection);
    var color = d3.scaleOrdinal(d3.schemeCategory10);
    
    var svg = d3.select('svg')
      .attr('width', w)
      .attr('height', h);
    
    var url = "https://gist.githubusercontent.com/Flave/832ebba5726aeca3518b1356d9d726cb/raw/5957dca433cbf50fe4dea0c3fa94bb4f91c754b7/world-regions-wrong.topojson";
    d3.json(url)
      .then(data => {
        var geojson = topojson.feature(data, data.objects.regions);
        geojson.features.forEach(f => {
          console.log(f.properties.region, f.properties.countries);
        });
    
        svg.selectAll('path')
          // Reverse because it's the last feature that is the problem
          .data(geojson.features.reverse())
          .enter()
          .append('path')
          .attr('d', path)
          .attr('fill', d => color(d.properties.region))
          .attr('stroke', d => color(d.properties.region))
          .on('mouseenter', function() {
            d3.select(this).style('fill-opacity', 1);
          })
          .on('mouseleave', function() {
            d3.select(this).style('fill-opacity', null);
          });
      });
    path {
      fill-opacity: 0.3;
      stroke-width: 2px;
      stroke-opacity: 0.4;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.js"></script>
    <script src="https://d3js.org/topojson.v3.js"></script>
    <svg></svg>

    要解决此问题,我会确保始终从列表中删除所有指定的国家/地区。从您的数据中,我看不出“世界”的定义位置,以及它是否包含地球上的所有国家,或者它是否是通配符分配。

    无论如何,您应该能够通过从worldTopo 中删除所有匹配项来修复它:

    const topojsonClient = require("topojson-client");
    const topojsonServer = require("topojson-server");
    
    const worldTopo = topojsonServer.topology({
      countries: JSON.parse(fs.readFileSync("./world.geojson", "utf-8")),
    });
    
    const geoJson = {
      type: "FeatureCollection",
      features: Object.entries(countryGroups).map(([region, ids]) => {
        const relevantCountries = worldTopo.objects.countries.geometries.filter(
          (country, i) =>
            ids.indexOf(country.properties.ISO_A3) >= 0
        );
    
        relevantCountries.forEach(c => {
          const index = worldTopo.indexOf(c);
          if (index === -1) throw Error(`Expected to find country ${c.properties.ISO_A3} in worldTopo`);
          worldTopo.splice(index, 1);
        });
    
        return {
          type: "Feature",
          properties: { region, countries: ids },
          geometry: topojsonClient.merge(worldTopo, relevantCountries),
        };
      }),
    };
    

    【讨论】:

    • 感谢您的回答!我明白了,那么这里实际上可能有两个问题。我通过简单地删除“世界”功能解决了重叠的形状。但是,缠绕顺序问题(我假设)仍然存在。至少当我尝试使用 vega lite 可视化地图时,形状似乎是错误的,如您所见here。但是,当我使用您的 d3 脚本时,它是 correct。那么这可能是一个 vega lite 问题吗?
    • 我没有Vega的经验,所以不能说
    • 我尝试了一些解决方法,但即使只有一个功能,这个问题总是存在。肯定和vega有关,不管是设置还是别的,我都说不准