Fix unclosed area rendering (fixes #1958)

This commit is contained in:
John Firebaugh
2013-11-13 17:41:01 -08:00
parent 94d756ebca
commit b383bfec1f
6 changed files with 80 additions and 68 deletions

View File

@@ -102,12 +102,6 @@ iD.Entity.prototype = {
resolver.parentRelations(this).length > 0;
},
area: function(resolver) {
return resolver.transient(this, 'area', function() {
return d3.geo.area(this.asGeoJSON(resolver, true));
});
},
hasInterestingTags: function() {
return _.keys(this.tags).some(function(key) {
return key !== 'attribution' &&

View File

@@ -156,6 +156,12 @@ _.extend(iD.Relation.prototype, {
});
},
area: function(resolver) {
return resolver.transient(this, 'area', function() {
return d3.geo.area(this.asGeoJSON(resolver));
});
},
isMultipolygon: function() {
return this.tags.type === 'multipolygon';
},

View File

@@ -146,34 +146,47 @@ _.extend(iD.Way.prototype, {
return r;
},
asGeoJSON: function(resolver, polygon) {
asGeoJSON: function(resolver) {
return resolver.transient(this, 'GeoJSON', function() {
var nodes = resolver.childNodes(this);
if (this.isArea() && polygon && nodes.length >= 4) {
if (!this.isClosed()) {
nodes = nodes.concat([nodes[0]]);
}
var json = {
var coordinates = _.pluck(resolver.childNodes(this), 'loc');
if (this.isArea() && this.isClosed()) {
return {
type: 'Polygon',
coordinates: [_.pluck(nodes, 'loc')]
coordinates: [coordinates]
};
// Heuristic for detecting counterclockwise winding order. Assumes
// that OpenStreetMap polygons are not hemisphere-spanning.
if (d3.geo.area(json) > 2 * Math.PI) {
json.coordinates[0] = json.coordinates[0].reverse();
}
return json;
} else {
return {
type: 'LineString',
coordinates: _.pluck(nodes, 'loc')
coordinates: coordinates
};
}
});
},
area: function(resolver) {
return resolver.transient(this, 'area', function() {
var nodes = resolver.childNodes(this);
if (!this.isClosed() && nodes.length) {
nodes = nodes.concat([nodes[0]]);
}
var json = {
type: 'Polygon',
coordinates: [_.pluck(nodes, 'loc')]
};
var area = d3.geo.area(json);
// Heuristic for detecting counterclockwise winding order. Assumes
// that OpenStreetMap polygons are not hemisphere-spanning.
if (d3.geo.area(json) > 2 * Math.PI) {
json.coordinates[0] = json.coordinates[0].reverse();
area = d3.geo.area(json);
}
return isNaN(area) ? 0 : area;
});
}
});

View File

@@ -31,7 +31,7 @@ iD.svg = {
if (entity.id in cache) {
return cache[entity.id];
} else {
return cache[entity.id] = path(entity.asGeoJSON(graph, polygon)); // jshint ignore:line
return cache[entity.id] = path(entity.asGeoJSON(graph)); // jshint ignore:line
}
};
},

View File

@@ -201,42 +201,4 @@ describe('iD.Entity', function () {
expect(iD.Entity({tags: {'tiger:source': 'blah', 'tiger:foo': 'bar'}}).hasInterestingTags()).to.equal(false);
});
});
describe("#area", function() {
it("returns a relative measure of area", function () {
var graph = iD.Graph([
iD.Node({id: 'a', loc: [-0.0002, 0.0001]}),
iD.Node({id: 'b', loc: [ 0.0002, 0.0001]}),
iD.Node({id: 'c', loc: [ 0.0002, -0.0001]}),
iD.Node({id: 'd', loc: [-0.0002, -0.0001]}),
iD.Node({id: 'e', loc: [-0.0004, 0.0002]}),
iD.Node({id: 'f', loc: [ 0.0004, 0.0002]}),
iD.Node({id: 'g', loc: [ 0.0004, -0.0002]}),
iD.Node({id: 'h', loc: [-0.0004, -0.0002]}),
iD.Way({id: 's', tags: {area: 'yes'}, nodes: ['a', 'b', 'c', 'd', 'a']}),
iD.Way({id: 'l', tags: {area: 'yes'}, nodes: ['e', 'f', 'g', 'h', 'e']})
]);
var s = Math.abs(graph.entity('s').area(graph)),
l = Math.abs(graph.entity('l').area(graph));
expect(s).to.be.lt(l);
});
it("returns 0 for degenerate areas", function () {
var graph = iD.Graph([
iD.Node({id: 'a', loc: [-0.0002, 0.0001]}),
iD.Node({id: 'b', loc: [ 0.0002, 0.0001]}),
iD.Node({id: 'c', loc: [ 0.0002, -0.0001]}),
iD.Node({id: 'd', loc: [-0.0002, -0.0001]}),
iD.Way({id: '0', tags: {area: 'yes'}, nodes: []}),
iD.Way({id: '1', tags: {area: 'yes'}, nodes: ['a']}),
iD.Way({id: '2', tags: {area: 'yes'}, nodes: ['a', 'b']})
]);
expect(graph.entity('0').area(graph)).to.equal(0);
expect(graph.entity('1').area(graph)).to.equal(0);
expect(graph.entity('2').area(graph)).to.equal(0);
});
});
});

View File

@@ -290,7 +290,7 @@ describe('iD.Way', function() {
json = w.asGeoJSON(graph);
expect(json.type).to.equal('LineString');
expect(json.coordinates).to.eql([[1, 2], [3, 4]]);
expect(json.coordinates).to.eql([a.loc, b.loc]);
});
it("converts an area to a GeoJSON Polygon geometry", function () {
@@ -305,15 +305,52 @@ describe('iD.Way', function() {
expect(json.coordinates).to.eql([[a.loc, b.loc, c.loc, a.loc]]);
});
it("forces clockwise polygon winding order", function () {
it("converts an unclosed area to a GeoJSON LineString geometry", function () {
var a = iD.Node({loc: [1, 2]}),
b = iD.Node({loc: [5, 6]}),
c = iD.Node({loc: [3, 4]}),
w = iD.Way({tags: {area: 'yes'}, nodes: [a.id, c.id, b.id, a.id]}),
w = iD.Way({tags: {area: 'yes'}, nodes: [a.id, b.id, c.id]}),
graph = iD.Graph([a, b, c, w]),
json = w.asGeoJSON(graph, true);
expect(json.coordinates).to.eql([[a.loc, b.loc, c.loc, a.loc]]);
expect(json.type).to.equal('LineString');
expect(json.coordinates).to.eql([a.loc, b.loc, c.loc]);
});
});
describe("#area", function() {
it("returns a relative measure of area", function () {
var graph = iD.Graph([
iD.Node({id: 'a', loc: [-0.0002, 0.0001]}),
iD.Node({id: 'b', loc: [ 0.0002, 0.0001]}),
iD.Node({id: 'c', loc: [ 0.0002, -0.0001]}),
iD.Node({id: 'd', loc: [-0.0002, -0.0001]}),
iD.Node({id: 'e', loc: [-0.0004, 0.0002]}),
iD.Node({id: 'f', loc: [ 0.0004, 0.0002]}),
iD.Node({id: 'g', loc: [ 0.0004, -0.0002]}),
iD.Node({id: 'h', loc: [-0.0004, -0.0002]}),
iD.Way({id: 's', tags: {area: 'yes'}, nodes: ['a', 'b', 'c', 'd', 'a']}),
iD.Way({id: 'l', tags: {area: 'yes'}, nodes: ['e', 'f', 'g', 'h', 'e']})
]);
var s = Math.abs(graph.entity('s').area(graph)),
l = Math.abs(graph.entity('l').area(graph));
expect(s).to.be.lt(l);
});
it("returns 0 for degenerate areas", function () {
var graph = iD.Graph([
iD.Node({id: 'a', loc: [-0.0002, 0.0001]}),
iD.Node({id: 'b', loc: [ 0.0002, 0.0001]}),
iD.Way({id: '0', tags: {area: 'yes'}, nodes: []}),
iD.Way({id: '1', tags: {area: 'yes'}, nodes: ['a']}),
iD.Way({id: '2', tags: {area: 'yes'}, nodes: ['a', 'b']})
]);
expect(graph.entity('0').area(graph)).to.equal(0);
expect(graph.entity('1').area(graph)).to.equal(0);
expect(graph.entity('2').area(graph)).to.equal(0);
});
});
});