mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-12 16:52:50 +00:00
Move each entity into its own file
This commit is contained in:
283
js/iD/Entity.js
283
js/iD/Entity.js
@@ -9,6 +9,7 @@ define(['dojo/_base/declare','dojo/_base/array','dojo/_base/lang',
|
||||
// Entity base class
|
||||
|
||||
declare("iD.Entity", null, {
|
||||
|
||||
connection: null,
|
||||
id: NaN,
|
||||
loaded: false,
|
||||
@@ -20,49 +21,80 @@ declare("iD.Entity", null, {
|
||||
MAINKEYS: ['highway','amenity','railway','waterway'],
|
||||
|
||||
constructor:function() {
|
||||
// summary: The base class for an entity (way, node or relation).
|
||||
this.tags={};
|
||||
this.parents=new Hashtable();
|
||||
},
|
||||
|
||||
isType:function(_type) {
|
||||
return this.entityType==_type;
|
||||
isType:function(type) {
|
||||
// summary: Is this entity of the specified type ('node','way','relation')?
|
||||
return this.entityType==type; // Boolean
|
||||
},
|
||||
|
||||
toString:function() {
|
||||
return this.entityType+"."+this.id;
|
||||
},
|
||||
|
||||
// --------------------------------
|
||||
// Provoke redraw and other changes
|
||||
|
||||
refresh:function() { this.connection.refreshEntity(this); },
|
||||
refresh:function() {
|
||||
// summary: Ask the connection to provoke redraw and other changes.
|
||||
this.connection.refreshEntity(this);
|
||||
},
|
||||
|
||||
// Clean and dirty (only called from UndoableEntityAction)
|
||||
// ---------------
|
||||
// Clean and dirty
|
||||
|
||||
markClean:function() { this.modified=false; },
|
||||
markDirty:function() { this.modified=true; },
|
||||
isDirty:function() { return this.modified; },
|
||||
_markClean:function() {
|
||||
// summary: Mark entity as clean. Should only be called from UndoableEntityAction.
|
||||
this.modified=false;
|
||||
},
|
||||
_markDirty:function() {
|
||||
// summary: Mark entity as dirty. Should only be called from UndoableEntityAction.
|
||||
this.modified=true;
|
||||
},
|
||||
isDirty:function() {
|
||||
// summary: Is the entity dirty?
|
||||
return this.modified; // Boolean
|
||||
},
|
||||
|
||||
// --------
|
||||
// Deletion
|
||||
|
||||
setDeletedState:function(isDeleted) { this.deleted=isDeleted; },
|
||||
setDeletedState:function(isDeleted) {
|
||||
// summary: Mark entity as deleted or not.
|
||||
this.deleted=isDeleted;
|
||||
},
|
||||
|
||||
// -------------------------------------
|
||||
// Bounding box check (to be overridden)
|
||||
|
||||
within:function(left,right,top,bottom) { return !this.deleted; },
|
||||
within:function(left,right,top,bottom) {
|
||||
// summary: Is the entity within the specified bbox?
|
||||
return !this.deleted; // Boolean
|
||||
},
|
||||
|
||||
// -------------
|
||||
// Tag functions
|
||||
|
||||
getTagsHash:function() {
|
||||
return this.tags;
|
||||
// summary: Tag getter.
|
||||
// returns: The tags hash (reference to the actual object property, not a copy).
|
||||
return this.tags; // Object
|
||||
},
|
||||
|
||||
numTags:function() {
|
||||
// summary: Count how many tags this entity has.
|
||||
var c=0;
|
||||
for (var i in this.tags) { c++; }
|
||||
return c;
|
||||
return c; // int
|
||||
},
|
||||
|
||||
friendlyName:function() {
|
||||
// summary: Rough-and-ready function to return a human-friendly name
|
||||
// for the object. Really just a placeholder for something better.
|
||||
// returns: A string such as 'river' or 'Fred's House'.
|
||||
if (this.numTags()==0) { return ''; }
|
||||
var n=[];
|
||||
if (this.tags['name']) { n.push(this.tags['name']); }
|
||||
@@ -72,37 +104,45 @@ declare("iD.Entity", null, {
|
||||
if (this.tags[this.MAINKEYS[i]]) { n.push(this.tags[this.MAINKEYS[i]]); break; }
|
||||
}
|
||||
}
|
||||
return n.length==0 ? 'unknown' : n.join('; ');
|
||||
return n.length==0 ? 'unknown' : n.join('; '); // String
|
||||
},
|
||||
|
||||
// ---------------
|
||||
// Parent-handling
|
||||
|
||||
addParent:function(_entity) {
|
||||
this.parents.put(_entity,true);
|
||||
addParent:function(entity) {
|
||||
// summary: Record a parent (a relation or way which contains this entity).
|
||||
this.parents.put(entity,true);
|
||||
},
|
||||
removeParent:function(_entity) {
|
||||
removeParent:function(entity) {
|
||||
// summary: Remove a parent (e.g. when node removed from a way).
|
||||
this.parents.remove(_entity);
|
||||
},
|
||||
hasParent:function(_entity) {
|
||||
return this.parents.containsKey(_entity);
|
||||
hasParent:function(entity) {
|
||||
// summary: Does this entity have the specified parent (e.g. is it in a certain relation)?
|
||||
return this.parents.containsKey(entity); // Boolean
|
||||
},
|
||||
parentObjects:function() {
|
||||
return this.parents.keys();
|
||||
// summary: List of all parents of this entity.
|
||||
return this.parents.keys(); // Boolean
|
||||
},
|
||||
hasParentWays:function() {
|
||||
// summary: Does this entity have any parents which are ways?
|
||||
var p=this.parentObjects();
|
||||
for (var i in p) {
|
||||
if (p[i].entityType=='way') { return true; }
|
||||
}
|
||||
return false;
|
||||
return false; // Boolean
|
||||
},
|
||||
parentWays:function() {
|
||||
return this.parentObjectsOfClass('way');
|
||||
// summary: Return an array of all ways that this entity is a member of.
|
||||
return this._parentObjectsOfClass('way'); // Array
|
||||
},
|
||||
parentRelations:function() {
|
||||
return this.parentObjectsOfClass('relation');
|
||||
// summary: Return an array of all relations that this entity is a member of.
|
||||
return this._parentObjectsOfClass('relation'); // Array
|
||||
},
|
||||
parentObjectsOfClass:function(_class) {
|
||||
_parentObjectsOfClass:function(_class) {
|
||||
var p=this.parentObjects(), c=[];
|
||||
for (var i in p) {
|
||||
if (p[i].entityType==_class) { c.push(p[i]); }
|
||||
@@ -119,205 +159,6 @@ declare("iD.Entity", null, {
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Node class
|
||||
|
||||
declare("iD.Node", [iD.Entity], {
|
||||
lat:NaN,
|
||||
latp:NaN,
|
||||
lon:NaN,
|
||||
entityType:"node",
|
||||
|
||||
constructor:function(_conn,_id,_lat,_lon,_tags,_loaded) {
|
||||
this.connection=_conn;
|
||||
this.id=Number(_id);
|
||||
this.lat=Number(_lat);
|
||||
this.lon=Number(_lon);
|
||||
this.tags=_tags;
|
||||
this.loaded=(_loaded==undefined) ? true : _loaded;
|
||||
this.project();
|
||||
this.modified=this.id<0;
|
||||
},
|
||||
|
||||
project:function() { this.latp=180/Math.PI * Math.log(Math.tan(Math.PI/4+this.lat*(Math.PI/180)/2)); },
|
||||
latp2lat:function(a) { return 180/Math.PI * (2 * Math.atan(Math.exp(a*Math.PI/180)) - Math.PI/2); },
|
||||
|
||||
within:function(left,right,top,bottom) { return (this.lon>=left) && (this.lon<=right) && (this.lat>=bottom) && (this.lat<=top) && !this.deleted; },
|
||||
|
||||
refresh:function() {
|
||||
var ways=this.parentWays();
|
||||
var conn=this.connection;
|
||||
array.forEach(ways,function(way) { conn.refreshEntity(way); });
|
||||
this.connection.refreshEntity(this);
|
||||
},
|
||||
|
||||
doSetLonLatp:function(lon,latproj,performAction) {
|
||||
performAction(new iD.actions.MoveNodeAction(this, this.latp2lat(latproj), lon, lang.hitch(this,this._setLatLonImmediate) ));
|
||||
},
|
||||
|
||||
_setLatLonImmediate:function(lat,lon) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
this.project();
|
||||
var ways = this.parentWays();
|
||||
for (var i=0; i<ways.length; i++) { ways[i].expandBbox(this); }
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Way class
|
||||
|
||||
declare("iD.Way", [iD.Entity], {
|
||||
nodes: null,
|
||||
entityType: "way",
|
||||
edgel: NaN,
|
||||
edger: NaN,
|
||||
edget: NaN,
|
||||
edgeb: NaN,
|
||||
|
||||
constructor:function(_conn,_id,_nodes,_tags,_loaded) {
|
||||
this.connection=_conn;
|
||||
this.id=Number(_id);
|
||||
this.nodes=_nodes;
|
||||
this.tags=_tags;
|
||||
this.loaded=(_loaded==undefined) ? true : _loaded;
|
||||
this.modified=this.id<0;
|
||||
var w=this; array.forEach(_nodes,function(node) {
|
||||
node.addParent(w);
|
||||
});
|
||||
this.calculateBbox();
|
||||
},
|
||||
|
||||
length:function() {
|
||||
return this.nodes.length;
|
||||
},
|
||||
|
||||
isClosed:function() {
|
||||
return this.nodes[this.nodes.length-1]==this.nodes[0];
|
||||
},
|
||||
|
||||
isType:function(_type) {
|
||||
switch (_type) {
|
||||
case 'way': return true;
|
||||
case 'area': return this.isClosed;
|
||||
case 'line': return !(this.isClosed);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getNode:function(index) { return this.nodes[index]; },
|
||||
getFirstNode:function() { return this.nodes[0]; },
|
||||
getLastNode:function() { return this.nodes[this.nodes.length-1]; },
|
||||
|
||||
// Bounding-box handling
|
||||
|
||||
within:function(left,right,top,bottom) {
|
||||
if (!this.edgel ||
|
||||
(this.edgel<left && this.edger<left ) ||
|
||||
(this.edgel>right && this.edger>right ) ||
|
||||
(this.edgeb<bottom && this.edget<bottom) ||
|
||||
(this.edgeb>top && this.edgeb>top ) || this.deleted) { return false; }
|
||||
return true;
|
||||
},
|
||||
|
||||
calculateBbox:function() {
|
||||
this.edgel=999999; this.edger=-999999;
|
||||
this.edgeb=999999; this.edget=-999999;
|
||||
for (var i in this.nodes) { this.expandBbox(this.nodes[i]); }
|
||||
},
|
||||
|
||||
expandBbox:function(node) {
|
||||
this.edgel=Math.min(this.edgel,node.lon);
|
||||
this.edger=Math.max(this.edger,node.lon);
|
||||
this.edgeb=Math.min(this.edgeb,node.lat);
|
||||
this.edget=Math.max(this.edget,node.lat);
|
||||
},
|
||||
|
||||
// Action callers
|
||||
|
||||
doAppendNode:function(node, performAction) {
|
||||
if (node!=this.getLastNode()) performAction(new iD.actions.AddNodeToWayAction(this, node, this.nodes, -1, true));
|
||||
return this.nodes.length + 1;
|
||||
},
|
||||
|
||||
doPrependNode:function(node, performAction) {
|
||||
if (node!=this.getFirstNode()) performAction(new iD.actions.AddNodeToWayAction(this, node, this.nodes, 0, true));
|
||||
return this.nodes.length + 1;
|
||||
},
|
||||
|
||||
doInsertNode:function(index, node, performAction) {
|
||||
if (index>0 && this.getNode(index-1)==node) return;
|
||||
if (index<this.nodes.length-1 && this.getNode(index)==node) return;
|
||||
performAction(new iD.actions.AddNodeToWayAction(this, node, this.nodes, index, false));
|
||||
},
|
||||
|
||||
doInsertNodeAtClosestPosition:function(newNode, isSnap, performAction) {
|
||||
var closestProportion = 1;
|
||||
var newIndex = 0;
|
||||
var snapped;
|
||||
|
||||
for (var i=0; i<this.nodes.length-1; i++) {
|
||||
var node1 = this.getNode(i);
|
||||
var node2 = this.getNode(i+1);
|
||||
var directDist = this.pythagoras(node1, node2);
|
||||
var viaNewDist = this.pythagoras(node1, newNode) + this.pythagoras(node2, newNode);
|
||||
var 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;
|
||||
},
|
||||
|
||||
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 };
|
||||
},
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Relation class
|
||||
|
||||
declare("iD.Relation", [iD.Entity], {
|
||||
members:null,
|
||||
entityType:"relation",
|
||||
|
||||
constructor:function(_conn,_id,_members,_tags,_loaded) {
|
||||
this.connection=_conn;
|
||||
this.id=Number(_id);
|
||||
this.members=_members;
|
||||
this.tags=_tags;
|
||||
this.modified=this.id<0;
|
||||
this.loaded=(_loaded==undefined) ? true : _loaded;
|
||||
var r=this; array.forEach(_members,function(member) {
|
||||
member.entity.addParent(r);
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// RelationMember class
|
||||
|
||||
declare("iD.RelationMember", [], {
|
||||
entity:null,
|
||||
role:"",
|
||||
constructor:function(_entity,_role) {
|
||||
this.entity=_entity;
|
||||
this.role=_role;
|
||||
},
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// End of module
|
||||
});
|
||||
|
||||
64
js/iD/Node.js
Normal file
64
js/iD/Node.js
Normal file
@@ -0,0 +1,64 @@
|
||||
// iD/Node.js
|
||||
|
||||
define(['dojo/_base/declare','dojo/_base/array','dojo/_base/lang',
|
||||
'iD/Entity','iD/actions/AddNodeToWayAction','iD/actions/MoveNodeAction'
|
||||
], function(declare,array,lang){
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Node class
|
||||
|
||||
declare("iD.Node", [iD.Entity], {
|
||||
lat:NaN,
|
||||
latp:NaN,
|
||||
lon:NaN,
|
||||
entityType:"node",
|
||||
|
||||
constructor:function(conn,id,lat,lon,tags,loaded) {
|
||||
// summary: An OSM node.
|
||||
this.connection=conn;
|
||||
this.id=Number(id);
|
||||
this.lat=Number(lat);
|
||||
this.lon=Number(lon);
|
||||
this.tags=tags;
|
||||
this.loaded=(loaded==undefined) ? true : loaded;
|
||||
this.project();
|
||||
this.modified=this.id<0;
|
||||
},
|
||||
|
||||
project:function() {
|
||||
// summary: Update the projected latitude value (this.latp) from the latitude (this.lat).
|
||||
this.latp=180/Math.PI * Math.log(Math.tan(Math.PI/4+this.lat*(Math.PI/180)/2));
|
||||
},
|
||||
latp2lat:function(a) {
|
||||
// summary: Get a latitude from a projected latitude.
|
||||
// returns: Latitude.
|
||||
return 180/Math.PI * (2 * Math.atan(Math.exp(a*Math.PI/180)) - Math.PI/2); // Number
|
||||
},
|
||||
|
||||
within:function(left,right,top,bottom) { return (this.lon>=left) && (this.lon<=right) && (this.lat>=bottom) && (this.lat<=top) && !this.deleted; },
|
||||
|
||||
refresh:function() {
|
||||
var ways=this.parentWays();
|
||||
var conn=this.connection;
|
||||
array.forEach(ways,function(way) { conn.refreshEntity(way); });
|
||||
this.connection.refreshEntity(this);
|
||||
},
|
||||
|
||||
doSetLonLatp:function(lon,latproj,performAction) {
|
||||
// summary: Change the position of a node, using an undo stack.
|
||||
performAction(new iD.actions.MoveNodeAction(this, this.latp2lat(latproj), lon, lang.hitch(this,this._setLatLonImmediate) ));
|
||||
},
|
||||
|
||||
_setLatLonImmediate:function(lat,lon) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
this.project();
|
||||
var ways = this.parentWays();
|
||||
for (var i=0; i<ways.length; i++) { ways[i].expandBbox(this); }
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// End of module
|
||||
});
|
||||
44
js/iD/Relation.js
Normal file
44
js/iD/Relation.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// iD/Relation.js
|
||||
|
||||
define(['dojo/_base/declare','dojo/_base/array','dojo/_base/lang',
|
||||
'iD/Entity','iD/actions/AddNodeToWayAction','iD/actions/MoveNodeAction'
|
||||
], function(declare,array,lang){
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Relation class
|
||||
|
||||
declare("iD.Relation", [iD.Entity], {
|
||||
members:null,
|
||||
entityType:"relation",
|
||||
|
||||
constructor:function(conn,id,members,tags,loaded) {
|
||||
// summary: An OSM relation.
|
||||
this.connection=conn;
|
||||
this.id=Number(id);
|
||||
this.members=members;
|
||||
this.tags=tags;
|
||||
this.modified=this.id<0;
|
||||
this.loaded=(loaded==undefined) ? true : loaded;
|
||||
var r=this; array.forEach(members,function(member) {
|
||||
member.entity.addParent(r);
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// RelationMember class
|
||||
|
||||
declare("iD.RelationMember", [], {
|
||||
entity:null,
|
||||
role:"",
|
||||
constructor:function(entity,role) {
|
||||
// summary: An object containing both the entity that is in the relation, and its role.
|
||||
this.entity=entity;
|
||||
this.role=role;
|
||||
},
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// End of module
|
||||
});
|
||||
153
js/iD/Way.js
Normal file
153
js/iD/Way.js
Normal file
@@ -0,0 +1,153 @@
|
||||
// iD/Way.js
|
||||
|
||||
define(['dojo/_base/declare','dojo/_base/array','dojo/_base/lang',
|
||||
'iD/Entity','iD/actions/AddNodeToWayAction','iD/actions/MoveNodeAction'
|
||||
], function(declare,array,lang){
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Way class
|
||||
|
||||
declare("iD.Way", [iD.Entity], {
|
||||
nodes: null,
|
||||
entityType: "way",
|
||||
edgel: NaN,
|
||||
edger: NaN,
|
||||
edget: NaN,
|
||||
edgeb: NaN,
|
||||
|
||||
constructor:function(conn,id,nodes,tags,loaded) {
|
||||
// summary: An OSM way.
|
||||
this.connection=conn;
|
||||
this.id=Number(id);
|
||||
this.nodes=nodes;
|
||||
this.tags=tags;
|
||||
this.loaded=(loaded==undefined) ? true : loaded;
|
||||
this.modified=this.id<0;
|
||||
var w=this; array.forEach(nodes,function(node) {
|
||||
node.addParent(w);
|
||||
});
|
||||
this._calculateBbox();
|
||||
},
|
||||
|
||||
length:function() {
|
||||
// summary: Return the number of nodes in the way.
|
||||
return this.nodes.length; // int
|
||||
},
|
||||
|
||||
isClosed:function() {
|
||||
// summary: Is this a closed way (first and last nodes the same)?
|
||||
return this.nodes[this.nodes.length-1]==this.nodes[0]; // Boolean
|
||||
},
|
||||
|
||||
isType:function(_type) {
|
||||
// summary: Is this a 'way' (always true), an 'area' (closed) or a 'line' (unclosed)?
|
||||
switch (_type) {
|
||||
case 'way': return true;
|
||||
case 'area': return this.isClosed;
|
||||
case 'line': return !(this.isClosed);
|
||||
}
|
||||
return false; // Boolean
|
||||
},
|
||||
|
||||
getNode:function(index) {
|
||||
// summary: Return the node at the given position.
|
||||
return this.nodes[index]; // iD.Node
|
||||
},
|
||||
getFirstNode:function() {
|
||||
// summary: Return the first node in the way.
|
||||
return this.nodes[0]; // iD.Node
|
||||
},
|
||||
getLastNode:function() {
|
||||
// summary: Return the last node in the way.
|
||||
return this.nodes[this.nodes.length-1]; // iD.Node
|
||||
},
|
||||
|
||||
// ---------------------
|
||||
// Bounding-box handling
|
||||
|
||||
within:function(left,right,top,bottom) {
|
||||
if (!this.edgel ||
|
||||
(this.edgel<left && this.edger<left ) ||
|
||||
(this.edgel>right && this.edger>right ) ||
|
||||
(this.edgeb<bottom && this.edget<bottom) ||
|
||||
(this.edgeb>top && this.edgeb>top ) || this.deleted) { return false; }
|
||||
return true;
|
||||
},
|
||||
|
||||
_calculateBbox:function() {
|
||||
this.edgel=999999; this.edger=-999999;
|
||||
this.edgeb=999999; this.edget=-999999;
|
||||
for (var i in this.nodes) { this.expandBbox(this.nodes[i]); }
|
||||
},
|
||||
|
||||
expandBbox:function(node) {
|
||||
// summary: Enlarge the way's bounding box to make sure it includes the co-ordinates of a supplied node.
|
||||
this.edgel=Math.min(this.edgel,node.lon);
|
||||
this.edger=Math.max(this.edger,node.lon);
|
||||
this.edgeb=Math.min(this.edgeb,node.lat);
|
||||
this.edget=Math.max(this.edget,node.lat);
|
||||
},
|
||||
|
||||
// --------------
|
||||
// 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.getFirstNode()) 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.getNode(index-1)==node) return;
|
||||
if (index<this.nodes.length-1 && this.getNode(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;
|
||||
var newIndex = 0;
|
||||
var snapped;
|
||||
|
||||
for (var i=0; i<this.nodes.length-1; i++) {
|
||||
var node1 = this.getNode(i);
|
||||
var node2 = this.getNode(i+1);
|
||||
var directDist = this._pythagoras(node1, node2);
|
||||
var viaNewDist = this._pythagoras(node1, newNode) + this._pythagoras(node2, newNode);
|
||||
var 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 };
|
||||
},
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// End of module
|
||||
});
|
||||
Reference in New Issue
Block a user