Flag unclosed multipolygon parts (close #2223)

This commit is contained in:
Quincy Morgan
2019-11-22 14:55:51 -05:00
parent d0c5add378
commit 45ac186a68
4 changed files with 61 additions and 0 deletions
+1
View File
@@ -459,6 +459,7 @@ A feature's tags indicate it should have a different geometry than it currently
* `area_as_line`: an unclosed way has tags implying it should be a closed area (e.g. `area=yes` or `building=yes`)
* `vertex_as_point`: a detached node has tags implying it should be part of a way (e.g. `highway=stop`)
* `point_as_vertex`: a vertex node has tags implying it should be detached from ways (e.g. `amenity=cafe`)
* `unclosed_multipolygon_part`: a relation is tagged as a multipolygon but not all of its member ways form closed rings
##### `missing_role`
+3
View File
@@ -1513,6 +1513,9 @@ en:
end:
message: "{feature} has no outlet"
reference: "One-way roads must lead to other roads."
unclosed_multipolygon_part:
message: "{feature} has an unclosed part"
reference: "All inner and outer parts of multipolygons should have connected endpoints."
unsquare_way:
title: "Unsquare Corners (up to {val}°)"
message: "{feature} has unsquare corners"
+4
View File
@@ -1883,6 +1883,10 @@
}
}
},
"unclosed_multipolygon_part": {
"message": "{feature} has an unclosed part",
"reference": "All inner and outer parts of multipolygons should have connected endpoints."
},
"unsquare_way": {
"title": "Unsquare Corners (up to {val}°)",
"message": "{feature} has unsquare corners",
@@ -3,6 +3,7 @@ import { actionChangeTags } from '../actions/change_tags';
import { actionMergeNodes } from '../actions/merge_nodes';
import { actionExtract } from '../actions/extract';
import { modeSelect } from '../modes/select';
import { osmJoinWays } from '../osm/multipolygon';
import { osmNodeGeometriesForTags } from '../osm/tags';
import { geoHasSelfIntersections, geoSphericalDistance } from '../geo';
import { t } from '../util/locale';
@@ -224,11 +225,63 @@ export function validationMismatchedGeometry(context) {
return null;
}
function unclosedMultipolygonPartIssues(entity, graph) {
if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate()) return null;
var sequences = osmJoinWays(entity.members, graph);
var issues = [];
for (var i in sequences) {
var sequence = sequences[i];
if (!sequence.nodes) continue;
var firstNode = sequence.nodes[0];
var lastNode = sequence.nodes[sequence.nodes.length - 1];
// part is closed if the first and last nodes are the same
if (firstNode === lastNode) continue;
var issue = new validationIssue({
type: type,
subtype: 'unclosed_multipolygon_part',
severity: 'warning',
message: function(context) {
var entity = context.hasEntity(this.entityIds[0]);
return entity ? t('issues.unclosed_multipolygon_part.message', {
feature: utilDisplayLabel(entity, context)
}) : '';
},
reference: showReference,
loc: sequence.nodes[0].loc,
entityIds: [entity.id],
hash: sequence.map(function(way) {
return way.id;
}).join()
});
issues.push(issue);
}
return issues;
function showReference(selection) {
selection.selectAll('.issue-reference')
.data([0])
.enter()
.append('div')
.attr('class', 'issue-reference')
.text(t('issues.unclosed_multipolygon_part.reference'));
}
}
var validation = function checkMismatchedGeometry(entity, graph) {
var issues = [
vertexTaggedAsPointIssue(entity, graph),
lineTaggedAsAreaIssue(entity)
];
issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
return issues.filter(Boolean);
};