mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-22 08:17:30 +02:00
132 lines
4.8 KiB
JavaScript
132 lines
4.8 KiB
JavaScript
if (typeof iD === 'undefined') iD = {};
|
|
|
|
iD.Way = function(connection, id, nodes, tags, loaded) {
|
|
// summary: An OSM way.
|
|
this.connection = connection;
|
|
this.entityType = 'way';
|
|
this.id = id;
|
|
this._id = iD.Util.id();
|
|
this.deleted = false;
|
|
this.entity = new iD.Entity();
|
|
this.tags = tags || {};
|
|
this.loaded = (loaded === undefined) ? true : loaded;
|
|
this.modified = this.id < 0;
|
|
this.nodes = nodes || [];
|
|
this.extent = {};
|
|
_.each(nodes, _.bind(function(node) {
|
|
node.entity.addParent(this);
|
|
}, this));
|
|
};
|
|
|
|
iD.Way.prototype = {
|
|
isClosed: function() {
|
|
// summary: Is this a closed way (first and last nodes the same)?
|
|
return this.nodes[this.nodes.length - 1] === this.nodes[0];
|
|
},
|
|
|
|
isType: function(type) {
|
|
// summary: Is this a 'way' (always true), an 'area' (closed) or a 'line' (unclosed)?
|
|
if (type === 'way') return true;
|
|
if (type === 'area') return this.isClosed();
|
|
if (type === 'line') return !(this.isClosed());
|
|
return false; // Boolean
|
|
},
|
|
|
|
toGeoJSON: function() {
|
|
return {
|
|
type: 'Feature',
|
|
properties: this.tags,
|
|
geometry: {
|
|
'type': 'LineString',
|
|
'coordinates': _.map(this.nodes, function(node) {
|
|
return [node.lon, node.lat];
|
|
})
|
|
}
|
|
};
|
|
},
|
|
|
|
bounds: function() {
|
|
return d3.geo.bounds(this.toGeoJSON());
|
|
},
|
|
|
|
// ---------------------
|
|
// Bounding-box handling
|
|
within: function(left,right,top,bottom) {
|
|
var bounds = this.bounds();
|
|
// TODO invert and just return
|
|
if (!this.extent.west ||
|
|
(this.extent.west < left && this.extent.east < left ) ||
|
|
(this.extent.west > right && this.extent.east > right ) ||
|
|
(this.extent.south < bottom && this.extent.north < bottom) ||
|
|
(this.extent.south > top && this.extent.south > top)) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
},
|
|
|
|
// --------------
|
|
// Action callers
|
|
|
|
doAppendNode: function(node, performAction) {
|
|
// summary: Add a node to the end of the way, using an undo stack.
|
|
// returns: New length of the way.
|
|
if (node!=this.getLastNode()) performAction(new iD.actions.AddNodeToWayAction(this, node, this.nodes, -1, true));
|
|
return this.nodes.length + 1; // int
|
|
},
|
|
|
|
doPrependNode: function(node, performAction) {
|
|
// summary: Add a node to the start of the way, using an undo stack.
|
|
// returns: New length of the way.
|
|
if (node!=this.nodes[0]) performAction(new iD.actions.AddNodeToWayAction(this, node, this.nodes, 0, true));
|
|
return this.nodes.length + 1; // int
|
|
},
|
|
|
|
doInsertNode:function(index, node, performAction) {
|
|
// summary: Add a node at a given index within the way, using an undo stack.
|
|
if (index > 0 && this.nodes[index - 1]==node) return;
|
|
if (index < this.nodes.length - 1 && this.nodes[index]==node) return;
|
|
performAction(new iD.actions.AddNodeToWayAction(this, node, this.nodes, index, false));
|
|
},
|
|
|
|
doInsertNodeAtClosestPosition:function(newNode, isSnap, performAction) {
|
|
// summary: Add a node into whichever segment of the way is nearest, using an undo stack.
|
|
// isSnap: Boolean Should the node position be snapped to be exactly on the segment?
|
|
// returns: The index at which the node was inserted.
|
|
var closestProportion = 1,
|
|
newIndex = 0,
|
|
snapped;
|
|
|
|
for (var i = 0; i < this.nodes.length - 1; i++) {
|
|
var node1 = this.nodes[i],
|
|
node2 = this.nodes[i + 1],
|
|
directDist = this._pythagoras(node1, node2),
|
|
viaNewDist = this._pythagoras(node1, newNode) +
|
|
this._pythagoras(node2, newNode),
|
|
proportion = Math.abs(viaNewDist/directDist - 1);
|
|
if (proportion < closestProportion) {
|
|
newIndex = i+1;
|
|
closestProportion = proportion;
|
|
snapped = this._calculateSnappedPoint(node1, node2, newNode);
|
|
}
|
|
}
|
|
|
|
// splice in new node
|
|
if (isSnap) { newNode.doSetLonLatp(snapped.x, snapped.y, performAction); }
|
|
this.doInsertNode(newIndex, newNode, performAction);
|
|
return newIndex; // int
|
|
},
|
|
|
|
_pythagoras:function(node1, node2) {
|
|
return (Math.sqrt(Math.pow(node1.lon-node2.lon,2) +
|
|
Math.pow(node1.latp-node2.latp,2)));
|
|
},
|
|
|
|
_calculateSnappedPoint:function(node1, node2, newNode) {
|
|
var w = node2.lon - node1.lon;
|
|
var h = node2.latp - node1.latp;
|
|
var u = ((newNode.lon-node1.lon) * w + (newNode.latp-node1.latp) * h) / (w*w + h*h);
|
|
return { x: node1.lon + u*w, y: node1.latp + u*h };
|
|
}
|
|
};
|