使用topomerge合并topojson会使缠绕顺序混乱

问题描述

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

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

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)可视化地图时,形状似乎都被弄乱了。我怀疑在合并这些功能时做错了什么,但我不知道是什么。

enter image description here

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

解决方法

最后一个称为“世界”的功能应包含所有剩余的国家,但相反,它包含所有国家/地区。您可以在以下展示柜中看到它。

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',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),};
  }),};