问题
I created this map (PLUNKER).
It rapresents Europe by nuts0 (country) and if you right-click on a country, this county is zoomed and its regions (nuts2) are showed.
A piece of code:
var projectionCurrent = d3.geoMercator()
.scale(1)
.translate([width / 2, height / 2]);
var projectionBase = d3.geoMercator()
.scale(1)
.translate([width / 2, height / 2]);
var path = d3.geoPath().projection(projectionCurrent);
var map = d3.select('#container-map');
var mapSvg = map.append('svg')
.attr('id', 'container-map-svg')
.attr('width', width)
.attr('height', height);
var mapSvgG = mapSvg.append('g');
function makeMap(data) {
var nuts0 = data[0];
var nuts2 = data[1];
var countries = topojson.feature(nuts0, nuts0.objects.nuts0);
var regions = topojson.feature(nuts2, nuts2.objects.nuts2);
projectionBase.fitSize([width, height], countries);
projectionCurrent.fitSize([width, height], countries);
mapSvgG.selectAll('path')
.data(countries.features)
.enter()
.append('path')
.attr('class', 'country')
.attr('fill', 'steelblue')
.style('stroke', 'white')
.style('stroke-width', 1)
.attr('d', path)
.attr('id', function(c) {
return 'country' + c.properties.nuts_id;
})
.on('mouseover', function(c) {
d3.select(this)
.attr('fill', 'white')
.raise();
})
.on('mouseout', function(c) {
d3.select(this)
.attr('fill', 'steelblue');
})
.on('contextmenu', function(d, i) {
d3.event.preventDefault();
d3.selectAll('.region').remove();
var features = regions.features.filter(function(feature) {
return feature.properties.nuts_id.substring(0, 2) == d.properties.nuts_id;
});
mapSvg.selectAll(null)
.data(features)
.enter()
.append('path')
.attr('class', 'region')
.attr('fill', 'tomato')
.style('stroke', 'white')
.style('stroke-width', 1)
.attr('d', path)
.attr('id', function(r) {
return 'region' + r.properties.nuts_id;
})
.on('mouseover', function(r) {
d3.select(this)
.attr('fill', 'white')
.raise();
})
.on('mouseout', function(r) {
d3.select(this)
.attr('fill', 'tomato');
})
.on('contextmenu', function(r, i) {
d3.event.preventDefault();
zoomCountries(projectionCurrent, projectionBase);
d3.selectAll('.region').remove();
})
.raise();
var featureCollection = { 'type': 'FeatureCollection', 'features': features }
var projectionEnd = d3.geoMercator();
zoomCountries(projectionCurrent, projectionEnd.fitExtent([[50, 50], [width-50, height-50]], featureCollection));
});
}
The problem is that if you right-click on a country, you see the regions and the are some imperfections near regions.
I think that an image explains better the problem:
In this case I selected Switzerland. You can see the regions (in red), the neighboring countries (in blue) and the path of the Switzerland country in white.
I think the problem is that the json files I used are not very precise.
The original files are these:
- nuts0
- nuts2
Then I simplified them with mapshaper (using the same settings). In particular:
- I have simplified the json files with the
Visvalingam/weighted area
algorithm at0.70%
. - I execute some command using console like:
filter 'name != "Iceland"'
andfilter 'name != "Turkey"'
(obviously for nuts2 I eliminated Iceland and Turkey, eliminating all their regions) - I export files as topoJson.
- Finally I removed some islands using Qgis, save as geoJson (I used I followed these steps: first and second)
- Import in Mapshaper and export as topoJson.
The resulting files are nuts0topojson3.json
and nuts2topojson3.json
(that I used to create map).
I would like this problem to be less visible. How can I do?
回答1:
Your problem is due to the simplification - you are heavily simplifying the polygons, the regional layer and the country layer have different area weightings, so the simplification changes each layer differently.
This will always be an issue with simplifying different layers. The files don't start with this discrepancy, so you could use unsimplified files, but this would be much too detailed.
Instead, use the simplified regional layer for both country and regions. Each region has an identifier that combines a country code and a number (two letter country code followed by some numbers). I created a new property that identifies country in the regional file by using:
nuts2.objects.nuts2.geometries.forEach(function(n) {
n.properties.country = n.properties.id.substring(0,2);
})
Now we can take our modified topojson and plunk it down into mapshaper and apply a dissolve in the console (dissolves are easily done in QGis too, or any other GIS platform). In mapshaper:
- import the topojson
- open the console
- type
dissolve country
- export the dissolved layer as a new topojson file
Now, one thing is that objects.nuts0
doesn't exist in this new file, since we used nuts2, so we can modify that to be nuts0
for clarity. We also don't have all the properties for the countries, we just have a two digit country code. This requires a few tweaks of the code to access the country
property rather than the id that was present in the original country layer.
Since we have a regional layer and a country layer that share the same boundaries from the same file (the regional layer), we have no issues with gaps or odd overlaps.
Here's a demonstration block based on your plunkr. (In my haste, I changed the topojson file names).
This can be optimized further, for example, if we combine regions and countries into the same topojson we only need to load the geometry once (and as the countries share arcs with the regions, there is less data to load). Different data preparation or GIS overlays could be used to preserve the country properties for each country in the dissolved file too.
来源:https://stackoverflow.com/questions/49779429/imprecision-between-overlapping-maps