mirror of
https://github.com/FoggedLens/iD.git
synced 2026-03-07 20:01:33 +00:00
Match inners with outers
After thinking about how the `evenodd` fill rule works, I pretty sure we don't actually need to do this. If I'm right, I'll back it out, and it will be in the history if we need it for some other purpose.
This commit is contained in:
@@ -25,49 +25,80 @@ iD.Relation = iD.Entity.extend({
|
||||
.filter(function (m) { return m.type === 'way'; })
|
||||
.map(function (m) { return { role: m.role || 'outer', id: m.id, nodes: resolver.fetch(m.id).nodes }; });
|
||||
|
||||
var outers = members.filter(function (m) { return m.role === 'outer'; }),
|
||||
inners = members.filter(function (m) { return m.role === 'inner'; });
|
||||
function join(ways) {
|
||||
var joined = [], current, first, last, i, how, what;
|
||||
|
||||
var result = [], current, first, last, i, how, what;
|
||||
while (ways.length) {
|
||||
current = ways.pop().nodes.slice();
|
||||
joined.push(current);
|
||||
|
||||
while (outers.length) {
|
||||
current = outers.pop().nodes.slice();
|
||||
result.push([current]);
|
||||
while (ways.length && _.first(current) !== _.last(current)) {
|
||||
first = _.first(current);
|
||||
last = _.last(current);
|
||||
|
||||
while (outers.length && _.first(current) !== _.last(current)) {
|
||||
first = _.first(current);
|
||||
last = _.last(current);
|
||||
for (i = 0; i < ways.length; i++) {
|
||||
what = ways[i].nodes;
|
||||
|
||||
for (i = 0; i < outers.length; i++) {
|
||||
what = outers[i].nodes;
|
||||
|
||||
if (last === _.first(what)) {
|
||||
how = current.push;
|
||||
what = what.slice(1);
|
||||
break;
|
||||
} else if (last === _.last(what)) {
|
||||
how = current.push;
|
||||
what = what.slice(0, -1).reverse();
|
||||
break;
|
||||
} else if (first == _.last(what)) {
|
||||
how = current.unshift;
|
||||
what = what.slice(0, -1);
|
||||
break;
|
||||
} else if (first == _.first(what)) {
|
||||
how = current.unshift;
|
||||
what = what.slice(1).reverse();
|
||||
break;
|
||||
} else {
|
||||
what = how = null;
|
||||
if (last === _.first(what)) {
|
||||
how = current.push;
|
||||
what = what.slice(1);
|
||||
break;
|
||||
} else if (last === _.last(what)) {
|
||||
how = current.push;
|
||||
what = what.slice(0, -1).reverse();
|
||||
break;
|
||||
} else if (first == _.last(what)) {
|
||||
how = current.unshift;
|
||||
what = what.slice(0, -1);
|
||||
break;
|
||||
} else if (first == _.first(what)) {
|
||||
how = current.unshift;
|
||||
what = what.slice(1).reverse();
|
||||
break;
|
||||
} else {
|
||||
what = how = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!what)
|
||||
break; // Invalid geometry (unclosed ring)
|
||||
|
||||
ways.splice(i, 1);
|
||||
how.apply(current, what);
|
||||
}
|
||||
|
||||
if (!what)
|
||||
break; // Invalid geometry (unclosed ring)
|
||||
|
||||
outers.splice(i, 1);
|
||||
how.apply(current, what);
|
||||
}
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
function findOuter(inner) {
|
||||
var o, outer;
|
||||
|
||||
inner = _.pluck(inner, 'loc');
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
if (iD.util.geo.polygonContainsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
if (iD.util.geo.polygonIntersectsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
var outers = join(members.filter(function (m) { return m.role === 'outer'; })),
|
||||
inners = join(members.filter(function (m) { return m.role === 'inner'; })),
|
||||
result = outers.map(function (o) { return [o]; });
|
||||
|
||||
for (var i = 0; i < inners.length; i++) {
|
||||
var o = findOuter(inners[i]);
|
||||
if (o !== undefined)
|
||||
result[o].push(inners[i]);
|
||||
else
|
||||
result.push(inners[i]); // Invalid geometry
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -133,3 +133,15 @@ iD.util.geo.pointInPolygon = function(point, polygon) {
|
||||
|
||||
return inside;
|
||||
};
|
||||
|
||||
iD.util.geo.polygonContainsPolygon = function(outer, inner) {
|
||||
return _.every(inner, function (point) {
|
||||
return iD.util.geo.pointInPolygon(point, outer);
|
||||
});
|
||||
};
|
||||
|
||||
iD.util.geo.polygonIntersectsPolygon = function(outer, inner) {
|
||||
return _.some(inner, function (point) {
|
||||
return iD.util.geo.pointInPolygon(point, outer);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -154,5 +154,83 @@ describe('iD.Relation', function () {
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, d]]]);
|
||||
});
|
||||
|
||||
specify("single polygon with single single-way inner", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [1, 0]}),
|
||||
c = iD.Node({loc: [0, 1]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.1, 0.2]}),
|
||||
outer = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
inner = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [{id: outer.id, type: 'way'}, {id: inner.id, role: 'inner', type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, e, f, outer, inner, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, a], [d, e, f, d]]]);
|
||||
});
|
||||
|
||||
specify("single polygon with single multi-way inner", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [1, 0]}),
|
||||
c = iD.Node({loc: [0, 1]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.2, 0.1]}),
|
||||
outer = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
inner1 = iD.Way({nodes: [d.id, e.id]}),
|
||||
inner2 = iD.Way({nodes: [e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [
|
||||
{id: outer.id, type: 'way'},
|
||||
{id: inner2.id, role: 'inner', type: 'way'},
|
||||
{id: inner1.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, outer, inner1, inner2, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a, b, c, a], [d, e, f, d]]]);
|
||||
});
|
||||
|
||||
specify("single polygon with multiple single-way inners", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [1, 0]}),
|
||||
c = iD.Node({loc: [0, 1]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.1, 0.2]}),
|
||||
g = iD.Node({loc: [0.2, 0.2]}),
|
||||
h = iD.Node({loc: [0.3, 0.2]}),
|
||||
i = iD.Node({loc: [0.2, 0.3]}),
|
||||
outer = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
inner1 = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
inner2 = iD.Way({nodes: [g.id, h.id, i.id, g.id]}),
|
||||
r = iD.Relation({members: [
|
||||
{id: outer.id, type: 'way'},
|
||||
{id: inner2.id, role: 'inner', type: 'way'},
|
||||
{id: inner1.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, g, h, i, outer, inner1, inner2, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a, b, c, a], [d, e, f, d], [g, h, i, g]]]);
|
||||
});
|
||||
|
||||
specify("multiple polygons with single single-way inner", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [1, 0]}),
|
||||
c = iD.Node({loc: [0, 1]}),
|
||||
d = iD.Node({loc: [0.1, 0.1]}),
|
||||
e = iD.Node({loc: [0.2, 0.1]}),
|
||||
f = iD.Node({loc: [0.1, 0.2]}),
|
||||
g = iD.Node({loc: [0, 0]}),
|
||||
h = iD.Node({loc: [-1, 0]}),
|
||||
i = iD.Node({loc: [0, -1]}),
|
||||
outer1 = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
outer2 = iD.Way({nodes: [g.id, h.id, i.id, g.id]}),
|
||||
inner = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [
|
||||
{id: outer2.id, type: 'way'},
|
||||
{id: outer1.id, type: 'way'},
|
||||
{id: inner.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, g, h, i, outer1, outer2, inner, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a, b, c, a], [d, e, f, d]], [[g, h, i, g]]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user