-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add validation rule to flag impossible oneway highways and waterways (c…
…lose #6216)
- Loading branch information
1 parent
7d29a98
commit ecc217f
Showing
11 changed files
with
274 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import { t } from '../util/locale'; | ||
import { modeDrawLine } from '../modes/draw_line'; | ||
import { actionReverse } from '../actions/reverse'; | ||
import { utilDisplayLabel } from '../util'; | ||
import { osmFlowingWaterwayTagValues, osmOneWayTags, osmRoutableHighwayTagValues } from '../osm/tags'; | ||
import { validationIssue, validationIssueFix } from '../core/validation'; | ||
|
||
|
||
export function validationImpossibleOneway() { | ||
var type = 'impossible_oneway'; | ||
|
||
function typeForWay(way) { | ||
if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway'; | ||
if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway'; | ||
return null; | ||
} | ||
|
||
function isOneway(way) { | ||
if (way.tags.oneway === 'yes') return true; | ||
if (way.tags.oneway) return false; | ||
|
||
for (var key in way.tags) { | ||
if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
function continueDrawing(way, vertex, context) { | ||
// make sure the vertex is actually visible and editable | ||
var map = context.map(); | ||
if (!map.editable() || !map.trimmedExtent().contains(vertex.loc)) { | ||
map.zoomToEase(vertex); | ||
} | ||
|
||
context.enter( | ||
modeDrawLine(context, way.id, context.graph(), context.graph(), 'line', way.affix(vertex.id), true) | ||
); | ||
} | ||
|
||
function nodeOccursMoreThanOnce(way, nodeID) { | ||
var occurences = 0; | ||
for (var index in way.nodes) { | ||
if (way.nodes[index] === nodeID) { | ||
occurences += 1; | ||
if (occurences > 1) return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
function issuesForNode(context, way, nodeID) { | ||
|
||
var isFirst = nodeID === way.first(); | ||
|
||
var wayType = typeForWay(way); | ||
var isWaterway = wayType === 'waterway'; | ||
|
||
// ignore if this way is self-connected at this node | ||
if (nodeOccursMoreThanOnce(way, nodeID)) return []; | ||
|
||
var osm = context.connection(); | ||
if (!osm) return []; | ||
|
||
var node = context.hasEntity(nodeID); | ||
|
||
// ignore if this node or its tile are unloaded | ||
if (!node || !osm.isDataLoaded(node.loc)) return []; | ||
|
||
var attachedWaysOfSameType = context.graph().parentWays(node).filter(function(parentWay) { | ||
if (parentWay.id === way.id) return false; | ||
return typeForWay(parentWay) === wayType; | ||
}); | ||
|
||
// assume it's okay for waterways to start or end disconnected for now | ||
if (isWaterway && attachedWaysOfSameType.length === 0) return []; | ||
|
||
var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) { | ||
return isOneway(attachedWay); | ||
}); | ||
|
||
// ignore if the way is connected to some non-oneway features | ||
if (attachedOneways.length < attachedWaysOfSameType.length) return []; | ||
|
||
if (attachedOneways.length) { | ||
var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) { | ||
return (isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID; | ||
}); | ||
if (connectedEndpointsOkay) return []; | ||
} | ||
|
||
var fixes = []; | ||
|
||
if (attachedOneways.length) { | ||
fixes.push(new validationIssueFix({ | ||
icon: 'iD-operation-reverse', | ||
title: t('issues.fix.reverse_feature.title'), | ||
entityIds: [way.id], | ||
onClick: function() { | ||
var id = this.issue.entities[0].id; | ||
context.perform(actionReverse(id), t('operations.reverse.annotation')); | ||
} | ||
})); | ||
} | ||
if (node.tags.noexit !== 'yes') { | ||
fixes.push(new validationIssueFix({ | ||
icon: 'iD-operation-continue' + (isFirst ? '-left' : ''), | ||
title: t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'), | ||
onClick: function() { | ||
var entityID = this.issue.entities[0].id; | ||
var vertexID = this.issue.entities[1].id; | ||
var way = context.entity(entityID); | ||
var vertex = context.entity(vertexID); | ||
continueDrawing(way, vertex, context); | ||
} | ||
})); | ||
} | ||
|
||
var placement = isFirst ? 'start' : 'end', | ||
messageID = wayType + '.', | ||
referenceID = wayType + '.'; | ||
|
||
if (isWaterway) { | ||
messageID += 'connected.' + placement; | ||
referenceID += 'connected'; | ||
} else { | ||
messageID += placement; | ||
referenceID += placement; | ||
} | ||
|
||
return [new validationIssue({ | ||
type: type, | ||
subtype: wayType, | ||
severity: 'warning', | ||
message: t('issues.impossible_oneway.' + messageID + '.message', { | ||
feature: utilDisplayLabel(way, context) | ||
}), | ||
reference: getReference(referenceID), | ||
entities: [way, node], | ||
fixes: fixes | ||
})]; | ||
|
||
function getReference(referenceID) { | ||
return function showReference(selection) { | ||
selection.selectAll('.issue-reference') | ||
.data([0]) | ||
.enter() | ||
.append('div') | ||
.attr('class', 'issue-reference') | ||
.text(t('issues.impossible_oneway.' + referenceID + '.reference')); | ||
}; | ||
} | ||
} | ||
|
||
var validation = function checkDisconnectedWay(entity, context) { | ||
|
||
if (entity.type !== 'way' || entity.geometry(context.graph()) !== 'line') return []; | ||
|
||
if (entity.isClosed()) return []; | ||
|
||
if (!typeForWay(entity)) return []; | ||
|
||
if (!isOneway(entity)) return []; | ||
|
||
var firstIssues = issuesForNode(context, entity, entity.first()); | ||
var lastIssues = issuesForNode(context, entity, entity.last()); | ||
|
||
return firstIssues.concat(lastIssues); | ||
}; | ||
|
||
|
||
validation.type = type; | ||
|
||
return validation; | ||
} |
Oops, something went wrong.