mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-16 18:52:57 +00:00
192 lines
5.5 KiB
JavaScript
192 lines
5.5 KiB
JavaScript
iD.Way = iD.Entity.way = function iD_Way() {
|
|
if (!(this instanceof iD_Way)) {
|
|
return (new iD_Way()).initialize(arguments);
|
|
} else if (arguments.length) {
|
|
this.initialize(arguments);
|
|
}
|
|
};
|
|
|
|
iD.Way.prototype = Object.create(iD.Entity.prototype);
|
|
|
|
_.extend(iD.Way.prototype, {
|
|
type: 'way',
|
|
nodes: [],
|
|
|
|
extent: function(resolver) {
|
|
return resolver.transient(this, 'extent', function() {
|
|
return this.nodes.reduce(function(extent, id) {
|
|
var node = resolver.hasEntity(id);
|
|
if (node) {
|
|
return extent.extend(node.extent());
|
|
} else {
|
|
return extent;
|
|
}
|
|
}, iD.geo.Extent());
|
|
});
|
|
},
|
|
|
|
first: function() {
|
|
return this.nodes[0];
|
|
},
|
|
|
|
last: function() {
|
|
return this.nodes[this.nodes.length - 1];
|
|
},
|
|
|
|
contains: function(node) {
|
|
return this.nodes.indexOf(node) >= 0;
|
|
},
|
|
|
|
affix: function(node) {
|
|
if (this.nodes[0] === node) return 'prefix';
|
|
if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
|
|
},
|
|
|
|
isOneWay: function() {
|
|
return this.tags.oneway === 'yes' ||
|
|
this.tags.oneway === '1' ||
|
|
this.tags.oneway === '-1' ||
|
|
this.tags.waterway === 'river' ||
|
|
this.tags.waterway === 'stream' ||
|
|
this.tags.junction === 'roundabout';
|
|
},
|
|
|
|
isClosed: function() {
|
|
return this.nodes.length > 0 && this.first() === this.last();
|
|
},
|
|
|
|
isArea: function() {
|
|
if (this.tags.area === 'yes')
|
|
return true;
|
|
if (!this.isClosed() || this.tags.area === 'no')
|
|
return false;
|
|
for (var key in this.tags)
|
|
if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
|
|
return true;
|
|
return false;
|
|
},
|
|
|
|
isDegenerate: function() {
|
|
return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
|
|
},
|
|
|
|
areAdjacent: function(n1, n2) {
|
|
for (var i = 0; i < this.nodes.length; i++) {
|
|
if (this.nodes[i] === n1) {
|
|
if (this.nodes[i - 1] === n2) return true;
|
|
if (this.nodes[i + 1] === n2) return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
geometry: function(graph) {
|
|
return graph.transient(this, 'geometry', function() {
|
|
return this.isArea() ? 'area' : 'line';
|
|
});
|
|
},
|
|
|
|
addNode: function(id, index) {
|
|
var nodes = this.nodes.slice();
|
|
nodes.splice(index === undefined ? nodes.length : index, 0, id);
|
|
return this.update({nodes: nodes});
|
|
},
|
|
|
|
updateNode: function(id, index) {
|
|
var nodes = this.nodes.slice();
|
|
nodes.splice(index, 1, id);
|
|
return this.update({nodes: nodes});
|
|
},
|
|
|
|
replaceNode: function(needle, replacement) {
|
|
if (this.nodes.indexOf(needle) < 0)
|
|
return this;
|
|
|
|
var nodes = this.nodes.slice();
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
if (nodes[i] === needle) {
|
|
nodes[i] = replacement;
|
|
}
|
|
}
|
|
return this.update({nodes: nodes});
|
|
},
|
|
|
|
removeNode: function(id) {
|
|
var nodes = [];
|
|
|
|
for (var i = 0; i < this.nodes.length; i++) {
|
|
var node = this.nodes[i];
|
|
if (node !== id && nodes[nodes.length - 1] !== node) {
|
|
nodes.push(node);
|
|
}
|
|
}
|
|
|
|
// Preserve circularity
|
|
if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
|
|
nodes.push(nodes[0]);
|
|
}
|
|
|
|
return this.update({nodes: nodes});
|
|
},
|
|
|
|
asJXON: function(changeset_id) {
|
|
var r = {
|
|
way: {
|
|
'@id': this.osmId(),
|
|
'@version': this.version || 0,
|
|
nd: _.map(this.nodes, function(id) {
|
|
return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
|
|
}),
|
|
tag: _.map(this.tags, function(v, k) {
|
|
return { keyAttributes: { k: k, v: v } };
|
|
})
|
|
}
|
|
};
|
|
if (changeset_id) r.way['@changeset'] = changeset_id;
|
|
return r;
|
|
},
|
|
|
|
asGeoJSON: function(resolver) {
|
|
return resolver.transient(this, 'GeoJSON', function() {
|
|
var coordinates = _.pluck(resolver.childNodes(this), 'loc');
|
|
if (this.isArea() && this.isClosed()) {
|
|
return {
|
|
type: 'Polygon',
|
|
coordinates: [coordinates]
|
|
};
|
|
} else {
|
|
return {
|
|
type: 'LineString',
|
|
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;
|
|
});
|
|
}
|
|
});
|