问题描述
我正在尝试创建一个自定义的世界地图,在该地图中,国家/地区合并为区域,而不是单个国家/地区。不幸的是,由于某些原因,在此过程中似乎有些混乱的缠绕顺序。
作为基础数据,我使用的是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)可视化地图时,形状似乎都被弄乱了。我怀疑在合并这些功能时做错了什么,但我不知道是什么。
当我尝试使用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),};
}),};