mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-14 21:28:11 +02:00
Building logic for turn restrictions
This commit is contained in:
@@ -59,6 +59,7 @@
|
||||
<script src="js/id/svg/tag_classes.js"></script>
|
||||
<script src="js/id/svg/vertices.js"></script>
|
||||
<script src="js/id/svg/labels.js"></script>
|
||||
<script src="js/id/svg/restrictions.js"></script>
|
||||
|
||||
<script src="js/id/ui.js"></script>
|
||||
<script src='js/id/ui/intro.js'></script>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
iD.svg.Restrictions = function(context) {
|
||||
var projection = context.projection;
|
||||
|
||||
function drawRestrictions(surface) {
|
||||
var turns = drawRestrictions.turns(context.graph(), context.selectedIDs());
|
||||
|
||||
var groups = surface.select('.layer-hit').selectAll('g.restriction')
|
||||
.data(turns, iD.Entity.key);
|
||||
|
||||
var enter = groups.enter().append('g')
|
||||
.attr('class', 'restriction');
|
||||
|
||||
enter.append('circle')
|
||||
.attr('class', 'restriction')
|
||||
.attr('r', 4);
|
||||
|
||||
groups
|
||||
.attr('transform', function(restriction) {
|
||||
var via = context.entity(restriction.memberByRole('via').id);
|
||||
return iD.svg.PointTransform(projection)(via);
|
||||
});
|
||||
|
||||
groups.exit()
|
||||
.remove();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
drawRestrictions.turns = function (graph, selectedIDs) {
|
||||
if (selectedIDs.length != 1)
|
||||
return [];
|
||||
|
||||
var from = graph.entity(selectedIDs[0]);
|
||||
if (from.type !== 'way')
|
||||
return [];
|
||||
|
||||
return graph.parentRelations(from).filter(function(relation) {
|
||||
var f = relation.memberById(from.id),
|
||||
t = relation.memberByRole('to'),
|
||||
v = relation.memberByRole('via');
|
||||
|
||||
return relation.tags.type === 'restriction' && f.role === 'from' &&
|
||||
t && t.type === 'way' && graph.hasEntity(t.id) &&
|
||||
v && v.type === 'node' && graph.hasEntity(v.id) &&
|
||||
!graph.entity(t.id).isDegenerate() &&
|
||||
!graph.entity(f.id).isDegenerate() &&
|
||||
graph.entity(t.id).affix(v.id) &&
|
||||
graph.entity(f.id).affix(v.id);
|
||||
});
|
||||
};
|
||||
|
||||
drawRestrictions.datum = function(graph, from, restriction, projection) {
|
||||
var to = graph.entity(restriction.memberByRole('to').id),
|
||||
a = graph.entity(restriction.memberByRole('via').id),
|
||||
b;
|
||||
|
||||
if (to.first() === a.id) {
|
||||
b = graph.entity(to.nodes[1]);
|
||||
} else {
|
||||
b = graph.entity(to.nodes[to.nodes.length - 2]);
|
||||
}
|
||||
|
||||
a = projection(a.loc);
|
||||
b = projection(b.loc);
|
||||
|
||||
return {
|
||||
from: from,
|
||||
to: to,
|
||||
restriction: restriction,
|
||||
angle: Math.atan2(b[1] - a[1], b[0] - a[0])
|
||||
}
|
||||
};
|
||||
|
||||
return drawRestrictions;
|
||||
};
|
||||
@@ -60,6 +60,7 @@
|
||||
<script src="../js/id/svg/tag_classes.js"></script>
|
||||
<script src="../js/id/svg/vertices.js"></script>
|
||||
<script src="../js/id/svg/labels.js"></script>
|
||||
<script src="../js/id/svg/restrictions.js"></script>
|
||||
|
||||
<script src="../js/id/ui.js"></script>
|
||||
<script src='../js/id/ui/attribution.js'></script>
|
||||
@@ -242,6 +243,7 @@
|
||||
<script src="spec/svg/points.js"></script>
|
||||
<script src="spec/svg/vertices.js"></script>
|
||||
<script src="spec/svg/tag_classes.js"></script>
|
||||
<script src="spec/svg/restrictions.js"></script>
|
||||
|
||||
<script src="spec/ui/inspector.js"></script>
|
||||
<script src="spec/ui/raw_tag_editor.js"></script>
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<script src="spec/svg/points.js"></script>
|
||||
<script src="spec/svg/vertices.js"></script>
|
||||
<script src="spec/svg/tag_classes.js"></script>
|
||||
<script src="spec/svg/restrictions.js"></script>
|
||||
|
||||
<script src="spec/ui/inspector.js"></script>
|
||||
<script src="spec/ui/raw_tag_editor.js"></script>
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
describe("iD.svg.Restrictions", function() {
|
||||
var restrictions = iD.svg.Restrictions({});
|
||||
|
||||
describe("#turns", function() {
|
||||
it("returns an empty array with no selection", function() {
|
||||
var graph = iD.Graph();
|
||||
expect(restrictions.turns(graph, [])).to.eql([]);
|
||||
});
|
||||
|
||||
it("returns an empty array with a multiselection", function() {
|
||||
var graph = iD.Graph();
|
||||
expect(restrictions.turns(graph, ['a', 'b'])).to.eql([]);
|
||||
});
|
||||
|
||||
var valid = iD.Graph({
|
||||
'u': iD.Node({id: 'u'}),
|
||||
'v': iD.Node({id: 'v'}),
|
||||
'w': iD.Node({id: 'w'}),
|
||||
'f': iD.Way({id: 'f', nodes: ['u', 'v']}),
|
||||
't': iD.Way({id: 't', nodes: ['v', 'w']}),
|
||||
'r': iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'from', id: 'f', type: 'way' },
|
||||
{ role: 'to', id: 't', type: 'way' }
|
||||
]})
|
||||
});
|
||||
|
||||
it("returns a valid restriction when the selected way has role 'from'", function() {
|
||||
expect(restrictions.turns(valid, ['f'])).to.eql([valid.entity('r')]);
|
||||
});
|
||||
|
||||
it("returns an empty array when the selected way has role 'to'", function() {
|
||||
expect(restrictions.turns(valid, ['t'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions missing a 'to' role", function() {
|
||||
var graph = valid.replace(valid.entity('r').removeMembersWithID('t'));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions with an incomplete 'to' role", function() {
|
||||
var graph = valid.remove(valid.entity('t'));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions missing a 'via' role", function() {
|
||||
var graph = valid.replace(valid.entity('r').removeMembersWithID('v'));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions with an incomplete 'via' role", function() {
|
||||
var graph = valid.remove(valid.entity('v'));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions whose 'from' role is not a way", function() {
|
||||
var graph = valid.replace(iD.Node({id: 'f2'}))
|
||||
.replace(valid.entity('r').replaceMember({id: 'f'}, {id: 'f2', type: 'node'}));
|
||||
expect(restrictions.turns(graph, ['f2'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions whose 'to' role is not a way", function() {
|
||||
var graph = valid.replace(iD.Node({id: 't2'}))
|
||||
.replace(valid.entity('r').replaceMember({id: 't'}, {id: 't2', type: 'node'}));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions whose 'via' role is not a node", function() {
|
||||
var graph = valid.replace(iD.Way({id: 'v2'}))
|
||||
.replace(valid.entity('r').replaceMember({id: 'v'}, {id: 'v2', type: 'way'}));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions whose 'from' role does not start or end with the via node", function() {
|
||||
var graph = valid.replace(valid.entity('f').update({nodes: ['o']}));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions whose 'to' role does not start or end with the via node", function() {
|
||||
var graph = valid.replace(valid.entity('t').update({nodes: ['o']}));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions whose 'from' role has less than two nodes", function() {
|
||||
var graph = valid.replace(valid.entity('f').update({nodes: ['v']}));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restrictions whose 'to' role has less than two nodes", function() {
|
||||
var graph = valid.replace(valid.entity('t').update({nodes: ['v']}));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
|
||||
it("ignores restriction subtypes", function() {
|
||||
var graph = valid.replace(valid.entity('r').update({tags: {type: 'restriction:hgv'}}));
|
||||
expect(restrictions.turns(graph, ['f'])).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#datum", function() {
|
||||
function projection(x) { return x; }
|
||||
|
||||
it("calculates the angle of a forward 'to' role", function() {
|
||||
// w---x--->y
|
||||
// |
|
||||
// u====>v
|
||||
// From = to - via v
|
||||
|
||||
var graph = iD.Graph({
|
||||
'u': iD.Node({id: 'u', loc: [0, 0]}),
|
||||
'v': iD.Node({id: 'v', loc: [1, 0]}),
|
||||
'w': iD.Node({id: 'w', loc: [1, 1]}),
|
||||
'x': iD.Node({id: 'w', loc: [2, 1]}),
|
||||
'y': iD.Node({id: 'w', loc: [3, 1]}),
|
||||
'=': iD.Way({id: '=', nodes: ['u', 'v']}),
|
||||
'-': iD.Way({id: '-', nodes: ['v', 'w', 'x', 'y']}),
|
||||
'r': iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'from', id: '=', type: 'way' },
|
||||
{ role: 'to', id: '-', type: 'way' }
|
||||
]})
|
||||
});
|
||||
|
||||
expect(restrictions.datum(graph, graph.entity('='), graph.entity('r'), projection).angle).to.eql(Math.PI / 2);
|
||||
});
|
||||
|
||||
it("calculates the angle of a reverse 'to' role", function() {
|
||||
// w<---x---y
|
||||
// |
|
||||
// u====>v
|
||||
// From = to - via v
|
||||
|
||||
var graph = iD.Graph({
|
||||
'u': iD.Node({id: 'u', loc: [0, 0]}),
|
||||
'v': iD.Node({id: 'v', loc: [1, 0]}),
|
||||
'w': iD.Node({id: 'w', loc: [1, 1]}),
|
||||
'x': iD.Node({id: 'w', loc: [2, 1]}),
|
||||
'y': iD.Node({id: 'w', loc: [3, 1]}),
|
||||
'=': iD.Way({id: '=', nodes: ['u', 'v']}),
|
||||
'-': iD.Way({id: '-', nodes: ['y', 'x', 'w', 'v']}),
|
||||
'r': iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{ role: 'via', id: 'v', type: 'node' },
|
||||
{ role: 'from', id: '=', type: 'way' },
|
||||
{ role: 'to', id: '-', type: 'way' }
|
||||
]})
|
||||
});
|
||||
|
||||
expect(restrictions.datum(graph, graph.entity('='), graph.entity('r'), projection).angle).to.eql(Math.PI / 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user