mirror of
https://github.com/FoggedLens/iD.git
synced 2026-04-30 15:38:27 +02:00
Merge pull request #3182 from openstreetmap/ext-modules
Ext modules for core, actions, geo
This commit is contained in:
@@ -43,10 +43,8 @@ $(BUILDJS_TARGETS): $(BUILDJS_SOURCES) build.js
|
||||
|
||||
|
||||
MODULE_TARGETS = \
|
||||
js/lib/id/actions.js \
|
||||
js/lib/id/index.js \
|
||||
js/lib/id/behavior.js \
|
||||
js/lib/id/core.js \
|
||||
js/lib/id/geo.js \
|
||||
js/lib/id/modes.js \
|
||||
js/lib/id/operations.js \
|
||||
js/lib/id/presets.js \
|
||||
@@ -61,22 +59,14 @@ MODULE_TARGETS = \
|
||||
js/lib/id/util.js \
|
||||
js/lib/id/validations.js
|
||||
|
||||
js/lib/id/actions.js: $(shell find modules/actions -type f)
|
||||
js/lib/id/index.js: $(shell find modules/index.js -type f)
|
||||
@rm -f $@
|
||||
node_modules/.bin/rollup -f umd -n iD.actions modules/actions/index.js --no-strict -o $@
|
||||
node_modules/.bin/rollup -f umd -n iD modules/index.js --no-strict -o $@
|
||||
|
||||
js/lib/id/behavior.js: $(shell find modules/behavior -type f)
|
||||
@rm -f $@
|
||||
node_modules/.bin/rollup -f umd -n iD.behavior modules/behavior/index.js --no-strict -o $@
|
||||
|
||||
js/lib/id/core.js: $(shell find modules/core -type f)
|
||||
@rm -f $@
|
||||
node_modules/.bin/rollup -f umd -n iD modules/core/index.js --no-strict -o $@
|
||||
|
||||
js/lib/id/geo.js: $(shell find modules/geo -type f)
|
||||
@rm -f $@
|
||||
node_modules/.bin/rollup -f umd -n iD.geo modules/geo/index.js --no-strict -o $@
|
||||
|
||||
js/lib/id/modes.js: $(shell find modules/modes -type f)
|
||||
@rm -f $@
|
||||
node_modules/.bin/rollup -f umd -n iD.modes modules/modes/index.js --no-strict -o $@
|
||||
|
||||
+2
-3
@@ -34,10 +34,9 @@
|
||||
<script src='js/lib/marked.js'></script>
|
||||
|
||||
<script src='js/id/id.js'></script>
|
||||
<script src='js/lib/id/actions.js'></script>
|
||||
<script src='js/lib/id/index.js'></script>
|
||||
|
||||
<script src='js/lib/id/behavior.js'></script>
|
||||
<script src='js/lib/id/core.js'></script>
|
||||
<script src='js/lib/id/geo.js'></script>
|
||||
<script src='js/lib/id/modes.js'></script>
|
||||
<script src='js/lib/id/operations.js'></script>
|
||||
<script src='js/lib/id/presets.js'></script>
|
||||
|
||||
+796
-4
@@ -115,6 +115,798 @@
|
||||
|
||||
});
|
||||
|
||||
function interestingTag(key) {
|
||||
return key !== 'attribution' &&
|
||||
key !== 'created_by' &&
|
||||
key !== 'source' &&
|
||||
key !== 'odbl' &&
|
||||
key.indexOf('tiger:') !== 0;
|
||||
|
||||
}
|
||||
|
||||
var oneWayTags = {
|
||||
'aerialway': {
|
||||
'chair_lift': true,
|
||||
'mixed_lift': true,
|
||||
't-bar': true,
|
||||
'j-bar': true,
|
||||
'platter': true,
|
||||
'rope_tow': true,
|
||||
'magic_carpet': true,
|
||||
'yes': true
|
||||
},
|
||||
'highway': {
|
||||
'motorway': true,
|
||||
'motorway_link': true
|
||||
},
|
||||
'junction': {
|
||||
'roundabout': true
|
||||
},
|
||||
'man_made': {
|
||||
'piste:halfpipe': true
|
||||
},
|
||||
'piste:type': {
|
||||
'downhill': true,
|
||||
'sled': true,
|
||||
'yes': true
|
||||
},
|
||||
'waterway': {
|
||||
'river': true,
|
||||
'stream': true
|
||||
}
|
||||
};
|
||||
|
||||
function Entity(attrs) {
|
||||
// For prototypal inheritance.
|
||||
if (this instanceof Entity) return;
|
||||
|
||||
// Create the appropriate subtype.
|
||||
if (attrs && attrs.type) {
|
||||
return Entity[attrs.type].apply(this, arguments);
|
||||
} else if (attrs && attrs.id) {
|
||||
return Entity[Entity.id.type(attrs.id)].apply(this, arguments);
|
||||
}
|
||||
|
||||
// Initialize a generic Entity (used only in tests).
|
||||
return (new Entity()).initialize(arguments);
|
||||
}
|
||||
|
||||
Entity.id = function(type) {
|
||||
return Entity.id.fromOSM(type, Entity.id.next[type]--);
|
||||
};
|
||||
|
||||
Entity.id.next = {node: -1, way: -1, relation: -1};
|
||||
|
||||
Entity.id.fromOSM = function(type, id) {
|
||||
return type[0] + id;
|
||||
};
|
||||
|
||||
Entity.id.toOSM = function(id) {
|
||||
return id.slice(1);
|
||||
};
|
||||
|
||||
Entity.id.type = function(id) {
|
||||
return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
|
||||
};
|
||||
|
||||
// A function suitable for use as the second argument to d3.selection#data().
|
||||
Entity.key = function(entity) {
|
||||
return entity.id + 'v' + (entity.v || 0);
|
||||
};
|
||||
|
||||
Entity.prototype = {
|
||||
tags: {},
|
||||
|
||||
initialize: function(sources) {
|
||||
for (var i = 0; i < sources.length; ++i) {
|
||||
var source = sources[i];
|
||||
for (var prop in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, prop)) {
|
||||
if (source[prop] === undefined) {
|
||||
delete this[prop];
|
||||
} else {
|
||||
this[prop] = source[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.id && this.type) {
|
||||
this.id = Entity.id(this.type);
|
||||
}
|
||||
if (!this.hasOwnProperty('visible')) {
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
if (iD.debug) {
|
||||
Object.freeze(this);
|
||||
Object.freeze(this.tags);
|
||||
|
||||
if (this.loc) Object.freeze(this.loc);
|
||||
if (this.nodes) Object.freeze(this.nodes);
|
||||
if (this.members) Object.freeze(this.members);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
copy: function(resolver, copies) {
|
||||
if (copies[this.id])
|
||||
return copies[this.id];
|
||||
|
||||
var copy = Entity(this, {id: undefined, user: undefined, version: undefined});
|
||||
copies[this.id] = copy;
|
||||
|
||||
return copy;
|
||||
},
|
||||
|
||||
osmId: function() {
|
||||
return Entity.id.toOSM(this.id);
|
||||
},
|
||||
|
||||
isNew: function() {
|
||||
return this.osmId() < 0;
|
||||
},
|
||||
|
||||
update: function(attrs) {
|
||||
return Entity(this, attrs, {v: 1 + (this.v || 0)});
|
||||
},
|
||||
|
||||
mergeTags: function(tags) {
|
||||
var merged = _.clone(this.tags), changed = false;
|
||||
for (var k in tags) {
|
||||
var t1 = merged[k],
|
||||
t2 = tags[k];
|
||||
if (!t1) {
|
||||
changed = true;
|
||||
merged[k] = t2;
|
||||
} else if (t1 !== t2) {
|
||||
changed = true;
|
||||
merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
|
||||
}
|
||||
}
|
||||
return changed ? this.update({tags: merged}) : this;
|
||||
},
|
||||
|
||||
intersects: function(extent, resolver) {
|
||||
return this.extent(resolver).intersects(extent);
|
||||
},
|
||||
|
||||
isUsed: function(resolver) {
|
||||
return _.without(Object.keys(this.tags), 'area').length > 0 ||
|
||||
resolver.parentRelations(this).length > 0;
|
||||
},
|
||||
|
||||
hasInterestingTags: function() {
|
||||
return _.keys(this.tags).some(interestingTag);
|
||||
},
|
||||
|
||||
isHighwayIntersection: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
deprecatedTags: function() {
|
||||
var tags = _.toPairs(this.tags);
|
||||
var deprecated = {};
|
||||
|
||||
iD.data.deprecated.forEach(function(d) {
|
||||
var match = _.toPairs(d.old)[0];
|
||||
tags.forEach(function(t) {
|
||||
if (t[0] === match[0] &&
|
||||
(t[1] === match[1] || match[1] === '*')) {
|
||||
deprecated[t[0]] = t[1];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return deprecated;
|
||||
}
|
||||
};
|
||||
|
||||
function Way() {
|
||||
if (!(this instanceof Way)) {
|
||||
return (new Way()).initialize(arguments);
|
||||
} else if (arguments.length) {
|
||||
this.initialize(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
Entity.way = Way;
|
||||
|
||||
Way.prototype = Object.create(Entity.prototype);
|
||||
|
||||
_.extend(Way.prototype, {
|
||||
type: 'way',
|
||||
nodes: [],
|
||||
|
||||
copy: function(resolver, copies) {
|
||||
if (copies[this.id])
|
||||
return copies[this.id];
|
||||
|
||||
var copy = Entity.prototype.copy.call(this, resolver, copies);
|
||||
|
||||
var nodes = this.nodes.map(function(id) {
|
||||
return resolver.entity(id).copy(resolver, copies).id;
|
||||
});
|
||||
|
||||
copy = copy.update({nodes: nodes});
|
||||
copies[this.id] = copy;
|
||||
|
||||
return copy;
|
||||
},
|
||||
|
||||
extent: function(resolver) {
|
||||
return resolver.transient(this, 'extent', function() {
|
||||
var extent = Extent();
|
||||
for (var i = 0; i < this.nodes.length; i++) {
|
||||
var node = resolver.hasEntity(this.nodes[i]);
|
||||
if (node) {
|
||||
extent._extend(node.extent());
|
||||
}
|
||||
}
|
||||
return 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';
|
||||
},
|
||||
|
||||
layer: function() {
|
||||
// explicit layer tag, clamp between -10, 10..
|
||||
if (this.tags.layer !== undefined) {
|
||||
return Math.max(-10, Math.min(+(this.tags.layer), 10));
|
||||
}
|
||||
|
||||
// implied layer tag..
|
||||
if (this.tags.location === 'overground') return 1;
|
||||
if (this.tags.location === 'underground') return -1;
|
||||
if (this.tags.location === 'underwater') return -10;
|
||||
|
||||
if (this.tags.power === 'line') return 10;
|
||||
if (this.tags.power === 'minor_line') return 10;
|
||||
if (this.tags.aerialway) return 10;
|
||||
if (this.tags.bridge) return 1;
|
||||
if (this.tags.cutting) return -1;
|
||||
if (this.tags.tunnel) return -1;
|
||||
if (this.tags.waterway) return -1;
|
||||
if (this.tags.man_made === 'pipeline') return -10;
|
||||
if (this.tags.boundary) return -10;
|
||||
return 0;
|
||||
},
|
||||
|
||||
isOneWay: function() {
|
||||
// explicit oneway tag..
|
||||
if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
|
||||
if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
|
||||
|
||||
// implied oneway tag..
|
||||
for (var key in this.tags) {
|
||||
if (key in oneWayTags && (this.tags[key] in oneWayTags[key]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
isClosed: function() {
|
||||
return this.nodes.length > 0 && this.first() === this.last();
|
||||
},
|
||||
|
||||
isConvex: function(resolver) {
|
||||
if (!this.isClosed() || this.isDegenerate()) return null;
|
||||
|
||||
var nodes = _.uniq(resolver.childNodes(this)),
|
||||
coords = _.map(nodes, 'loc'),
|
||||
curr = 0, prev = 0;
|
||||
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
var o = coords[(i+1) % coords.length],
|
||||
a = coords[i],
|
||||
b = coords[(i+2) % coords.length],
|
||||
res = cross(o, a, b);
|
||||
|
||||
curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
|
||||
if (curr === 0) {
|
||||
continue;
|
||||
} else if (prev && curr !== prev) {
|
||||
return false;
|
||||
}
|
||||
prev = curr;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
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: 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 = _.map(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);
|
||||
|
||||
var json = {
|
||||
type: 'Polygon',
|
||||
coordinates: [_.map(nodes, 'loc')]
|
||||
};
|
||||
|
||||
if (!this.isClosed() && nodes.length) {
|
||||
json.coordinates[0].push(nodes[0].loc);
|
||||
}
|
||||
|
||||
var area = d3.geo.area(json);
|
||||
|
||||
// Heuristic for detecting counterclockwise winding order. Assumes
|
||||
// that OpenStreetMap polygons are not hemisphere-spanning.
|
||||
if (area > 2 * Math.PI) {
|
||||
json.coordinates[0] = json.coordinates[0].reverse();
|
||||
area = d3.geo.area(json);
|
||||
}
|
||||
|
||||
return isNaN(area) ? 0 : area;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function Relation() {
|
||||
if (!(this instanceof Relation)) {
|
||||
return (new Relation()).initialize(arguments);
|
||||
} else if (arguments.length) {
|
||||
this.initialize(arguments);
|
||||
}
|
||||
}
|
||||
Entity.relation = Relation;
|
||||
|
||||
Relation.prototype = Object.create(Entity.prototype);
|
||||
|
||||
Relation.creationOrder = function(a, b) {
|
||||
var aId = parseInt(Entity.id.toOSM(a.id), 10);
|
||||
var bId = parseInt(Entity.id.toOSM(b.id), 10);
|
||||
|
||||
if (aId < 0 || bId < 0) return aId - bId;
|
||||
return bId - aId;
|
||||
};
|
||||
|
||||
_.extend(Relation.prototype, {
|
||||
type: 'relation',
|
||||
members: [],
|
||||
|
||||
copy: function(resolver, copies) {
|
||||
if (copies[this.id])
|
||||
return copies[this.id];
|
||||
|
||||
var copy = Entity.prototype.copy.call(this, resolver, copies);
|
||||
|
||||
var members = this.members.map(function(member) {
|
||||
return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
|
||||
});
|
||||
|
||||
copy = copy.update({members: members});
|
||||
copies[this.id] = copy;
|
||||
|
||||
return copy;
|
||||
},
|
||||
|
||||
extent: function(resolver, memo) {
|
||||
return resolver.transient(this, 'extent', function() {
|
||||
if (memo && memo[this.id]) return Extent();
|
||||
memo = memo || {};
|
||||
memo[this.id] = true;
|
||||
|
||||
var extent = Extent();
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
var member = resolver.hasEntity(this.members[i].id);
|
||||
if (member) {
|
||||
extent._extend(member.extent(resolver, memo));
|
||||
}
|
||||
}
|
||||
return extent;
|
||||
});
|
||||
},
|
||||
|
||||
geometry: function(graph) {
|
||||
return graph.transient(this, 'geometry', function() {
|
||||
return this.isMultipolygon() ? 'area' : 'relation';
|
||||
});
|
||||
},
|
||||
|
||||
isDegenerate: function() {
|
||||
return this.members.length === 0;
|
||||
},
|
||||
|
||||
// Return an array of members, each extended with an 'index' property whose value
|
||||
// is the member index.
|
||||
indexedMembers: function() {
|
||||
var result = new Array(this.members.length);
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
result[i] = _.extend({}, this.members[i], {index: i});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
// Return the first member with the given role. A copy of the member object
|
||||
// is returned, extended with an 'index' property whose value is the member index.
|
||||
memberByRole: function(role) {
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
if (this.members[i].role === role) {
|
||||
return _.extend({}, this.members[i], {index: i});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Return the first member with the given id. A copy of the member object
|
||||
// is returned, extended with an 'index' property whose value is the member index.
|
||||
memberById: function(id) {
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
if (this.members[i].id === id) {
|
||||
return _.extend({}, this.members[i], {index: i});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Return the first member with the given id and role. A copy of the member object
|
||||
// is returned, extended with an 'index' property whose value is the member index.
|
||||
memberByIdAndRole: function(id, role) {
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
if (this.members[i].id === id && this.members[i].role === role) {
|
||||
return _.extend({}, this.members[i], {index: i});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addMember: function(member, index) {
|
||||
var members = this.members.slice();
|
||||
members.splice(index === undefined ? members.length : index, 0, member);
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
updateMember: function(member, index) {
|
||||
var members = this.members.slice();
|
||||
members.splice(index, 1, _.extend({}, members[index], member));
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
removeMember: function(index) {
|
||||
var members = this.members.slice();
|
||||
members.splice(index, 1);
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
removeMembersWithID: function(id) {
|
||||
var members = _.reject(this.members, function(m) { return m.id === id; });
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
// Wherever a member appears with id `needle.id`, replace it with a member
|
||||
// with id `replacement.id`, type `replacement.type`, and the original role,
|
||||
// unless a member already exists with that id and role. Return an updated
|
||||
// relation.
|
||||
replaceMember: function(needle, replacement) {
|
||||
if (!this.memberById(needle.id))
|
||||
return this;
|
||||
|
||||
var members = [];
|
||||
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
var member = this.members[i];
|
||||
if (member.id !== needle.id) {
|
||||
members.push(member);
|
||||
} else if (!this.memberByIdAndRole(replacement.id, member.role)) {
|
||||
members.push({id: replacement.id, type: replacement.type, role: member.role});
|
||||
}
|
||||
}
|
||||
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
asJXON: function(changeset_id) {
|
||||
var r = {
|
||||
relation: {
|
||||
'@id': this.osmId(),
|
||||
'@version': this.version || 0,
|
||||
member: _.map(this.members, function(member) {
|
||||
return { keyAttributes: { type: member.type, role: member.role, ref: Entity.id.toOSM(member.id) } };
|
||||
}),
|
||||
tag: _.map(this.tags, function(v, k) {
|
||||
return { keyAttributes: { k: k, v: v } };
|
||||
})
|
||||
}
|
||||
};
|
||||
if (changeset_id) r.relation['@changeset'] = changeset_id;
|
||||
return r;
|
||||
},
|
||||
|
||||
asGeoJSON: function(resolver) {
|
||||
return resolver.transient(this, 'GeoJSON', function () {
|
||||
if (this.isMultipolygon()) {
|
||||
return {
|
||||
type: 'MultiPolygon',
|
||||
coordinates: this.multipolygon(resolver)
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
properties: this.tags,
|
||||
features: this.members.map(function (member) {
|
||||
return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
|
||||
})
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
area: function(resolver) {
|
||||
return resolver.transient(this, 'area', function() {
|
||||
return d3.geo.area(this.asGeoJSON(resolver));
|
||||
});
|
||||
},
|
||||
|
||||
isMultipolygon: function() {
|
||||
return this.tags.type === 'multipolygon';
|
||||
},
|
||||
|
||||
isComplete: function(resolver) {
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
if (!resolver.hasEntity(this.members[i].id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
isRestriction: function() {
|
||||
return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
|
||||
},
|
||||
|
||||
// Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
|
||||
// where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
|
||||
//
|
||||
// This corresponds to the structure needed for rendering a multipolygon path using a
|
||||
// `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
|
||||
//
|
||||
// In the case of invalid geometries, this function will still return a result which
|
||||
// includes the nodes of all way members, but some Nds may be unclosed and some inner
|
||||
// rings not matched with the intended outer ring.
|
||||
//
|
||||
multipolygon: function(resolver) {
|
||||
var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
|
||||
inners = this.members.filter(function(m) { return 'inner' === m.role; });
|
||||
|
||||
outers = joinWays(outers, resolver);
|
||||
inners = joinWays(inners, resolver);
|
||||
|
||||
outers = outers.map(function(outer) { return _.map(outer.nodes, 'loc'); });
|
||||
inners = inners.map(function(inner) { return _.map(inner.nodes, 'loc'); });
|
||||
|
||||
var result = outers.map(function(o) {
|
||||
// Heuristic for detecting counterclockwise winding order. Assumes
|
||||
// that OpenStreetMap polygons are not hemisphere-spanning.
|
||||
return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
|
||||
});
|
||||
|
||||
function findOuter(inner) {
|
||||
var o, outer;
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = outers[o];
|
||||
if (polygonContainsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = outers[o];
|
||||
if (polygonIntersectsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < inners.length; i++) {
|
||||
var inner = inners[i];
|
||||
|
||||
if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
|
||||
inner = inner.reverse();
|
||||
}
|
||||
|
||||
var o = findOuter(inners[i]);
|
||||
if (o !== undefined)
|
||||
result[o].push(inners[i]);
|
||||
else
|
||||
result.push([inners[i]]); // Invalid geometry
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
function Node() {
|
||||
if (!(this instanceof Node)) {
|
||||
return (new Node()).initialize(arguments);
|
||||
} else if (arguments.length) {
|
||||
this.initialize(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
Entity.node = Node;
|
||||
|
||||
Node.prototype = Object.create(Entity.prototype);
|
||||
|
||||
_.extend(Node.prototype, {
|
||||
type: 'node',
|
||||
|
||||
extent: function() {
|
||||
return new Extent(this.loc);
|
||||
},
|
||||
|
||||
geometry: function(graph) {
|
||||
return graph.transient(this, 'geometry', function() {
|
||||
return graph.isPoi(this) ? 'point' : 'vertex';
|
||||
});
|
||||
},
|
||||
|
||||
move: function(loc) {
|
||||
return this.update({loc: loc});
|
||||
},
|
||||
|
||||
isIntersection: function(resolver) {
|
||||
return resolver.transient(this, 'isIntersection', function() {
|
||||
return resolver.parentWays(this).filter(function(parent) {
|
||||
return (parent.tags.highway ||
|
||||
parent.tags.waterway ||
|
||||
parent.tags.railway ||
|
||||
parent.tags.aeroway) &&
|
||||
parent.geometry(resolver) === 'line';
|
||||
}).length > 1;
|
||||
});
|
||||
},
|
||||
|
||||
isHighwayIntersection: function(resolver) {
|
||||
return resolver.transient(this, 'isHighwayIntersection', function() {
|
||||
return resolver.parentWays(this).filter(function(parent) {
|
||||
return parent.tags.highway && parent.geometry(resolver) === 'line';
|
||||
}).length > 1;
|
||||
});
|
||||
},
|
||||
|
||||
asJXON: function(changeset_id) {
|
||||
var r = {
|
||||
node: {
|
||||
'@id': this.osmId(),
|
||||
'@lon': this.loc[0],
|
||||
'@lat': this.loc[1],
|
||||
'@version': (this.version || 0),
|
||||
tag: _.map(this.tags, function(v, k) {
|
||||
return { keyAttributes: { k: k, v: v } };
|
||||
})
|
||||
}
|
||||
};
|
||||
if (changeset_id) r.node['@changeset'] = changeset_id;
|
||||
return r;
|
||||
},
|
||||
|
||||
asGeoJSON: function() {
|
||||
return {
|
||||
type: 'Point',
|
||||
coordinates: this.loc
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function Turn(turn) {
|
||||
if (!(this instanceof Turn))
|
||||
return new Turn(turn);
|
||||
@@ -155,14 +947,14 @@
|
||||
var splitIndex, wayA, wayB, indexA, indexB;
|
||||
if (isClosingNode) {
|
||||
splitIndex = Math.ceil(way.nodes.length / 2); // split at midpoint
|
||||
wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
|
||||
wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
wayA = Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
|
||||
wayB = Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
indexA = 1;
|
||||
indexB = way.nodes.length - 2;
|
||||
} else {
|
||||
splitIndex = _.indexOf(way.nodes, vertex.id, 1); // split at vertexid
|
||||
wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
|
||||
wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
wayA = Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
|
||||
wayB = Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
indexA = splitIndex - 1;
|
||||
indexB = splitIndex + 1;
|
||||
}
|
||||
|
||||
+5368
File diff suppressed because it is too large
Load Diff
@@ -247,40 +247,6 @@
|
||||
return d3.rebind(step, event, 'on');
|
||||
}
|
||||
|
||||
|
||||
function pointBox(point, context) {
|
||||
var rect = context.surfaceRect();
|
||||
point = context.projection(point);
|
||||
return {
|
||||
left: point[0] + rect.left - 30,
|
||||
top: point[1] + rect.top - 50,
|
||||
width: 60,
|
||||
height: 70
|
||||
};
|
||||
}
|
||||
|
||||
function pad(box, padding, context) {
|
||||
if (box instanceof Array) {
|
||||
var rect = context.surfaceRect();
|
||||
box = context.projection(box);
|
||||
box = {
|
||||
left: box[0] + rect.left,
|
||||
top: box[1] + rect.top
|
||||
};
|
||||
}
|
||||
return {
|
||||
left: box.left - padding,
|
||||
top: box.top - padding,
|
||||
width: (box.width || 0) + 2 * padding,
|
||||
height: (box.width || 0) + 2 * padding
|
||||
};
|
||||
}
|
||||
|
||||
function icon(name, svgklass) {
|
||||
return '<svg class="icon ' + (svgklass || '') + '">' +
|
||||
'<use xlink:href="' + name + '"></use></svg>';
|
||||
}
|
||||
|
||||
function navigation(context, reveal) {
|
||||
var event = d3.dispatch('done'),
|
||||
timeouts = [];
|
||||
@@ -607,9 +573,6 @@
|
||||
|
||||
exports.area = area;
|
||||
exports.line = line;
|
||||
exports.pad = pad;
|
||||
exports.pointBox = pointBox;
|
||||
exports.icon = icon;
|
||||
exports.navigation = navigation;
|
||||
exports.point = point;
|
||||
exports.startEditing = startEditing;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { joinWays } from '../geo/index';
|
||||
export function AddMember(relationId, member, memberIndex) {
|
||||
return function(graph) {
|
||||
var relation = graph.entity(relationId);
|
||||
@@ -6,7 +7,7 @@ export function AddMember(relationId, member, memberIndex) {
|
||||
var members = relation.indexedMembers();
|
||||
members.push(member);
|
||||
|
||||
var joined = iD.geo.joinWays(members, graph);
|
||||
var joined = joinWays(members, graph);
|
||||
for (var i = 0; i < joined.length; i++) {
|
||||
var segment = joined[i];
|
||||
for (var j = 0; j < segment.length && segment.length >= 2; j++) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { edgeEqual } from '../geo/index';
|
||||
export function AddMidpoint(midpoint, node) {
|
||||
return function(graph) {
|
||||
graph = graph.replace(node.move(midpoint.loc));
|
||||
@@ -8,7 +9,7 @@ export function AddMidpoint(midpoint, node) {
|
||||
|
||||
parents.forEach(function(way) {
|
||||
for (var i = 0; i < way.nodes.length - 1; i++) {
|
||||
if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
|
||||
if (edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
|
||||
graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
|
||||
|
||||
// Add only one midpoint on doubled-back segments,
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
export function Circularize(wayId, projection, maxAngle) {
|
||||
import { Node } from '../core/index';
|
||||
import { interp, euclideanDistance } from '../geo/index';
|
||||
|
||||
export function Circularize(wayId
|
||||
, projection, maxAngle) {
|
||||
maxAngle = (maxAngle || 20) * Math.PI / 180;
|
||||
|
||||
var action = function(graph) {
|
||||
@@ -12,8 +16,8 @@ export function Circularize(wayId, projection, maxAngle) {
|
||||
keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
|
||||
centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
|
||||
radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
|
||||
centroid = (points.length === 2) ? interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
|
||||
radius = d3.median(points, function(p) { return euclideanDistance(centroid, p); }),
|
||||
sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
|
||||
ids;
|
||||
|
||||
@@ -52,7 +56,7 @@ export function Circularize(wayId, projection, maxAngle) {
|
||||
}
|
||||
|
||||
// position this key node
|
||||
distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
|
||||
distance = euclideanDistance(centroid, keyPoints[i]);
|
||||
if (distance === 0) { distance = 1e-4; }
|
||||
keyPoints[i] = [
|
||||
centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
|
||||
@@ -92,7 +96,7 @@ export function Circularize(wayId, projection, maxAngle) {
|
||||
centroid[0] + Math.cos(angle) * radius,
|
||||
centroid[1] + Math.sin(angle) * radius]);
|
||||
|
||||
node = iD.Node({loc: loc});
|
||||
node = Node({loc: loc});
|
||||
graph = graph.replace(node);
|
||||
|
||||
nodes.splice(endNodeIndex + j, 0, node);
|
||||
@@ -166,7 +170,7 @@ export function Circularize(wayId, projection, maxAngle) {
|
||||
|
||||
// move interior nodes to the surface of the convex hull..
|
||||
for (var j = 1; j < indexRange; j++) {
|
||||
var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
|
||||
var point = interp(hull[i], hull[i+1], j / indexRange),
|
||||
node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
|
||||
graph = graph.replace(node);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
|
||||
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
|
||||
//
|
||||
import { Node } from '../core/index';
|
||||
export function Disconnect(nodeId, newNodeId) {
|
||||
var wayIds;
|
||||
|
||||
@@ -21,7 +22,7 @@ export function Disconnect(nodeId, newNodeId) {
|
||||
|
||||
connections.forEach(function(connection) {
|
||||
var way = graph.entity(connection.wayID),
|
||||
newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
|
||||
newNode = Node({id: newNodeId, loc: node.loc, tags: node.tags});
|
||||
|
||||
graph = graph.replace(newNode);
|
||||
if (connection.index === 0 && way.isArea()) {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { DeleteWay } from './delete_way';
|
||||
|
||||
// Join ways at the end node they share.
|
||||
//
|
||||
// This is the inverse of `iD.actions.Split`.
|
||||
@@ -8,6 +6,10 @@ import { DeleteWay } from './delete_way';
|
||||
// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
|
||||
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
|
||||
//
|
||||
import { joinWays } from '../geo/index';
|
||||
import { interestingTag } from '../core/index';
|
||||
import { DeleteWay } from './delete_way';
|
||||
|
||||
export function Join(ids) {
|
||||
|
||||
function groupEntitiesByGeometry(graph) {
|
||||
@@ -27,7 +29,7 @@ export function Join(ids) {
|
||||
}
|
||||
}
|
||||
|
||||
var joined = iD.geo.joinWays(ways, graph)[0];
|
||||
var joined = joinWays(ways, graph)[0];
|
||||
|
||||
survivor = survivor.update({nodes: _.map(joined.nodes, 'id')});
|
||||
graph = graph.replace(survivor);
|
||||
@@ -54,7 +56,7 @@ export function Join(ids) {
|
||||
if (ids.length < 2 || ids.length !== geometries.line.length)
|
||||
return 'not_eligible';
|
||||
|
||||
var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
|
||||
var joined = joinWays(ids.map(graph.entity, graph), graph);
|
||||
if (joined.length > 1)
|
||||
return 'not_adjacent';
|
||||
|
||||
@@ -73,7 +75,7 @@ export function Join(ids) {
|
||||
for (var k in way.tags) {
|
||||
if (!(k in tags)) {
|
||||
tags[k] = way.tags[k];
|
||||
} else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
|
||||
} else if (tags[k] && interestingTag(k) && tags[k] !== way.tags[k]) {
|
||||
conflicting = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { joinWays, polygonContainsPolygon } from '../geo/index';
|
||||
import { Relation } from '../core/index';
|
||||
|
||||
export function MergePolygon(ids, newRelationId) {
|
||||
|
||||
function groupEntities(graph) {
|
||||
@@ -25,7 +28,7 @@ export function MergePolygon(ids, newRelationId) {
|
||||
// Each element is itself an array of objects with an id property, and has a
|
||||
// locs property which is an array of the locations forming the polygon.
|
||||
var polygons = entities.multipolygon.reduce(function(polygons, m) {
|
||||
return polygons.concat(iD.geo.joinWays(m.members, graph));
|
||||
return polygons.concat(joinWays(m.members, graph));
|
||||
}, []).concat(entities.closedWay.map(function(d) {
|
||||
var member = [{id: d.id}];
|
||||
member.nodes = graph.childNodes(d);
|
||||
@@ -38,7 +41,7 @@ export function MergePolygon(ids, newRelationId) {
|
||||
var contained = polygons.map(function(w, i) {
|
||||
return polygons.map(function(d, n) {
|
||||
if (i === n) return null;
|
||||
return iD.geo.polygonContainsPolygon(
|
||||
return polygonContainsPolygon(
|
||||
_.map(d.nodes, 'loc'),
|
||||
_.map(w.nodes, 'loc'));
|
||||
});
|
||||
@@ -79,7 +82,7 @@ export function MergePolygon(ids, newRelationId) {
|
||||
|
||||
// Move all tags to one relation
|
||||
var relation = entities.multipolygon[0] ||
|
||||
iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
|
||||
Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
|
||||
|
||||
entities.multipolygon.slice(1).forEach(function(m) {
|
||||
relation = relation.mergeTags(m.tags);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DeleteMultiple } from './delete_multiple';
|
||||
import { Entity } from '../core/index';
|
||||
|
||||
export function MergeRemoteChanges(id, localGraph, remoteGraph, formatUser) {
|
||||
var option = 'safe', // 'safe', 'force_local', 'force_remote'
|
||||
@@ -96,14 +97,14 @@ export function MergeRemoteChanges(id, localGraph, remoteGraph, formatUser) {
|
||||
updates.replacements.push(remote);
|
||||
|
||||
} else if (option === 'force_local' && local) {
|
||||
target = iD.Entity(local);
|
||||
target = Entity(local);
|
||||
if (remote) {
|
||||
target = target.update({ version: remote.version });
|
||||
}
|
||||
updates.replacements.push(target);
|
||||
|
||||
} else if (option === 'safe' && local && remote && local.version !== remote.version) {
|
||||
target = iD.Entity(local, { version: remote.version });
|
||||
target = Entity(local, { version: remote.version });
|
||||
if (remote.visible) {
|
||||
target = mergeLocation(remote, target);
|
||||
} else {
|
||||
@@ -201,7 +202,7 @@ export function MergeRemoteChanges(id, localGraph, remoteGraph, formatUser) {
|
||||
base = graph.base().entities[id],
|
||||
local = localGraph.entity(id),
|
||||
remote = remoteGraph.entity(id),
|
||||
target = iD.Entity(local, { version: remote.version });
|
||||
target = Entity(local, { version: remote.version });
|
||||
|
||||
// delete/undelete
|
||||
if (!remote.visible) {
|
||||
|
||||
+24
-14
@@ -1,5 +1,15 @@
|
||||
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
|
||||
import {
|
||||
angle as getAngle,
|
||||
sphericalDistance,
|
||||
chooseEdge,
|
||||
pathLength,
|
||||
interp,
|
||||
pathIntersections
|
||||
} from '../geo/index';
|
||||
import { Node } from '../core/index';
|
||||
|
||||
export function Move(moveIds, tryDelta, projection, cache) {
|
||||
var delta = tryDelta;
|
||||
|
||||
@@ -114,7 +124,7 @@ export function Move(moveIds, tryDelta, projection, cache) {
|
||||
var key = wayId + '_' + nodeId,
|
||||
orig = cache.replacedVertex[key];
|
||||
if (!orig) {
|
||||
orig = iD.Node();
|
||||
orig = Node();
|
||||
cache.replacedVertex[key] = orig;
|
||||
cache.startLoc[orig.id] = cache.startLoc[nodeId];
|
||||
}
|
||||
@@ -128,21 +138,21 @@ export function Move(moveIds, tryDelta, projection, cache) {
|
||||
}
|
||||
orig = orig.move(end);
|
||||
|
||||
var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
|
||||
iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
|
||||
var angle = Math.abs(getAngle(orig, prev, projection) -
|
||||
getAngle(orig, next, projection)) * 180 / Math.PI;
|
||||
|
||||
// Don't add orig vertex if it would just make a straight line..
|
||||
if (angle > 175 && angle < 185) return graph;
|
||||
|
||||
// Don't add orig vertex if another point is already nearby (within 10m)
|
||||
if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
|
||||
iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
|
||||
if (sphericalDistance(prev.loc, orig.loc) < 10 ||
|
||||
sphericalDistance(orig.loc, next.loc) < 10) return graph;
|
||||
|
||||
// moving forward or backward along way?
|
||||
var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
|
||||
p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
|
||||
d1 = iD.geo.pathLength(p1),
|
||||
d2 = iD.geo.pathLength(p2),
|
||||
d1 = pathLength(p1),
|
||||
d2 = pathLength(p2),
|
||||
insertAt = (d1 < d2) ? movedIndex : nextIndex;
|
||||
|
||||
// moving around closed loop?
|
||||
@@ -169,17 +179,17 @@ export function Move(moveIds, tryDelta, projection, cache) {
|
||||
if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
|
||||
if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
|
||||
|
||||
var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
|
||||
edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
|
||||
var edge1 = !isEP1 && chooseEdge(nodes1, projection(vertex.loc), projection),
|
||||
edge2 = !isEP2 && chooseEdge(nodes2, projection(vertex.loc), projection),
|
||||
loc;
|
||||
|
||||
// snap vertex to nearest edge (or some point between them)..
|
||||
if (!isEP1 && !isEP2) {
|
||||
var epsilon = 1e-4, maxIter = 10;
|
||||
for (var i = 0; i < maxIter; i++) {
|
||||
loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
|
||||
edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
|
||||
edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
|
||||
loc = interp(edge1.loc, edge2.loc, 0.5);
|
||||
edge1 = chooseEdge(nodes1, projection(loc), projection);
|
||||
edge2 = chooseEdge(nodes2, projection(loc), projection);
|
||||
if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
|
||||
}
|
||||
} else if (!isEP1) {
|
||||
@@ -227,11 +237,11 @@ export function Move(moveIds, tryDelta, projection, cache) {
|
||||
function(loc) { return vecAdd(projection(loc), delta); }),
|
||||
unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
|
||||
unmovedPath = _.map(_.map(unmovedNodes, 'loc'), projection),
|
||||
hits = iD.geo.pathIntersections(movedPath, unmovedPath);
|
||||
hits = pathIntersections(movedPath, unmovedPath);
|
||||
|
||||
for (var i = 0; i < hits.length; i++) {
|
||||
if (_.isEqual(hits[i], end)) continue;
|
||||
var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
|
||||
var edge = chooseEdge(unmovedNodes, end, projection);
|
||||
delta = vecSub(projection(edge.loc), start);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DeleteNode } from './delete_node';
|
||||
|
||||
import { euclideanDistance } from '../geo/index';
|
||||
/*
|
||||
* Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
|
||||
*/
|
||||
@@ -86,7 +86,7 @@ export function Orthogonalize(wayId, projection) {
|
||||
q = subtractPoints(c, b),
|
||||
scale, dotp;
|
||||
|
||||
scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
|
||||
scale = 2 * Math.min(euclideanDistance(p, [0, 0]), euclideanDistance(q, [0, 0]));
|
||||
p = normalizePoint(p, 1.0);
|
||||
q = normalizePoint(q, 1.0);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Split } from './split';
|
||||
|
||||
import { inferRestriction } from '../geo/index';
|
||||
import { Relation, Way } from '../core/index';
|
||||
// Create a restriction relation for `turn`, which must have the following structure:
|
||||
//
|
||||
// {
|
||||
@@ -14,7 +15,7 @@ import { Split } from './split';
|
||||
// (The action does not check that these entities form a valid intersection.)
|
||||
//
|
||||
// If `restriction` is not provided, it is automatically determined by
|
||||
// iD.geo.inferRestriction.
|
||||
// inferRestriction.
|
||||
//
|
||||
// If necessary, the `from` and `to` ways are split. In these cases, `from.node`
|
||||
// and `to.node` are used to determine which portion of the split ways become
|
||||
@@ -35,7 +36,7 @@ export function RestrictTurn(turn, projection, restrictionId) {
|
||||
}
|
||||
|
||||
function split(toOrFrom) {
|
||||
var newID = toOrFrom.newID || iD.Way().id;
|
||||
var newID = toOrFrom.newID || Way().id;
|
||||
graph = Split(via.id, [newID])
|
||||
.limitWays([toOrFrom.way])(graph);
|
||||
|
||||
@@ -68,12 +69,12 @@ export function RestrictTurn(turn, projection, restrictionId) {
|
||||
to = split(turn.to)[0];
|
||||
}
|
||||
|
||||
return graph.replace(iD.Relation({
|
||||
return graph.replace(Relation({
|
||||
id: restrictionId,
|
||||
tags: {
|
||||
type: 'restriction',
|
||||
restriction: turn.restriction ||
|
||||
iD.geo.inferRestriction(
|
||||
inferRestriction(
|
||||
graph,
|
||||
turn.from,
|
||||
turn.via,
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { AddMember } from './add_member';
|
||||
import { sphericalDistance, isSimpleMultipolygonOuterMember} from '../geo/index';
|
||||
import { wrap as Wrap } from '../util/index';
|
||||
import { Way, Relation } from '../core/index';
|
||||
|
||||
// Split a way at the given node.
|
||||
//
|
||||
// Optionally, split only the given ways, if multiple ways share
|
||||
@@ -34,11 +38,11 @@ export function Split(nodeId, newWayIds) {
|
||||
idxB;
|
||||
|
||||
function wrap(index) {
|
||||
return iD.util.wrap(index, nodes.length);
|
||||
return Wrap(index, nodes.length);
|
||||
}
|
||||
|
||||
function dist(nA, nB) {
|
||||
return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
|
||||
return sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
|
||||
}
|
||||
|
||||
// calculate lengths
|
||||
@@ -68,11 +72,11 @@ export function Split(nodeId, newWayIds) {
|
||||
}
|
||||
|
||||
function split(graph, wayA, newWayId) {
|
||||
var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
|
||||
var wayB = Way({id: newWayId, tags: wayA.tags}),
|
||||
nodesA,
|
||||
nodesB,
|
||||
isArea = wayA.isArea(),
|
||||
isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
|
||||
isOuter = isSimpleMultipolygonOuterMember(wayA, graph);
|
||||
|
||||
if (wayA.isClosed()) {
|
||||
var nodes = wayA.nodes.slice(0, -1),
|
||||
@@ -123,7 +127,7 @@ export function Split(nodeId, newWayIds) {
|
||||
});
|
||||
|
||||
if (!isOuter && isArea) {
|
||||
var multipolygon = iD.Relation({
|
||||
var multipolygon = Relation({
|
||||
tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
|
||||
members: [
|
||||
{id: wayA.id, role: 'outer', type: 'way'},
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Entity } from './entity';
|
||||
import { Way } from './way';
|
||||
import { Relation } from './relation';
|
||||
import { Node } from './node';
|
||||
import { Extent } from '../geo/index';
|
||||
|
||||
export function Connection(useHttps) {
|
||||
if (typeof useHttps !== 'boolean') {
|
||||
@@ -384,7 +385,7 @@ export function Connection(useHttps) {
|
||||
|
||||
return {
|
||||
id: tile.toString(),
|
||||
extent: iD.geo.Extent(
|
||||
extent: Extent(
|
||||
projection.invert([x, y + ts]),
|
||||
projection.invert([x + ts, y]))
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getPrototypeOf } from '../util/index';
|
||||
|
||||
export function Graph(other, mutable) {
|
||||
if (!(this instanceof Graph)) return new Graph(other, mutable);
|
||||
|
||||
@@ -97,9 +99,9 @@ Graph.prototype = {
|
||||
|
||||
base: function() {
|
||||
return {
|
||||
'entities': iD.util.getPrototypeOf(this.entities),
|
||||
'parentWays': iD.util.getPrototypeOf(this._parentWays),
|
||||
'parentRels': iD.util.getPrototypeOf(this._parentRels)
|
||||
'entities': getPrototypeOf(this.entities),
|
||||
'parentWays': getPrototypeOf(this._parentWays),
|
||||
'parentRels': getPrototypeOf(this._parentRels)
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@ import { Entity } from './entity';
|
||||
import { Graph } from './graph';
|
||||
import { Difference } from './difference';
|
||||
import { Tree } from './tree';
|
||||
import { SessionMutex } from '../util/index';
|
||||
import { Loading } from '../ui/core/index';
|
||||
|
||||
export function History(context) {
|
||||
var stack, index, tree,
|
||||
imageryUsed = ['Bing'],
|
||||
dispatch = d3.dispatch('change', 'undone', 'redone'),
|
||||
lock = iD.util.SessionMutex('lock');
|
||||
lock = SessionMutex('lock');
|
||||
|
||||
function perform(actions) {
|
||||
actions = Array.prototype.slice.call(actions);
|
||||
@@ -291,7 +293,7 @@ export function History(context) {
|
||||
loadComplete = false;
|
||||
context.redrawEnable(false);
|
||||
|
||||
var loading = iD.ui.Loading(context).blocking(true);
|
||||
var loading = Loading(context).blocking(true);
|
||||
context.container().call(loading);
|
||||
|
||||
var childNodesLoaded = function(err, result) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// iD.Node = iD.Entity.node;
|
||||
import { Entity } from './entity';
|
||||
import { Extent } from '../geo/index';
|
||||
|
||||
export function Node() {
|
||||
if (!(this instanceof Node)) {
|
||||
return (new Node()).initialize(arguments);
|
||||
@@ -16,7 +18,7 @@ _.extend(Node.prototype, {
|
||||
type: 'node',
|
||||
|
||||
extent: function() {
|
||||
return new iD.geo.Extent(this.loc);
|
||||
return new Extent(this.loc);
|
||||
},
|
||||
|
||||
geometry: function(graph) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Entity } from './entity';
|
||||
import { Extent, joinWays, polygonContainsPolygon, polygonIntersectsPolygon } from '../geo/index';
|
||||
|
||||
export function Relation() {
|
||||
if (!(this instanceof Relation)) {
|
||||
@@ -41,11 +42,11 @@ _.extend(Relation.prototype, {
|
||||
|
||||
extent: function(resolver, memo) {
|
||||
return resolver.transient(this, 'extent', function() {
|
||||
if (memo && memo[this.id]) return iD.geo.Extent();
|
||||
if (memo && memo[this.id]) return Extent();
|
||||
memo = memo || {};
|
||||
memo[this.id] = true;
|
||||
|
||||
var extent = iD.geo.Extent();
|
||||
var extent = Extent();
|
||||
for (var i = 0; i < this.members.length; i++) {
|
||||
var member = resolver.hasEntity(this.members[i].id);
|
||||
if (member) {
|
||||
@@ -224,8 +225,8 @@ _.extend(Relation.prototype, {
|
||||
var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
|
||||
inners = this.members.filter(function(m) { return 'inner' === m.role; });
|
||||
|
||||
outers = iD.geo.joinWays(outers, resolver);
|
||||
inners = iD.geo.joinWays(inners, resolver);
|
||||
outers = joinWays(outers, resolver);
|
||||
inners = joinWays(inners, resolver);
|
||||
|
||||
outers = outers.map(function(outer) { return _.map(outer.nodes, 'loc'); });
|
||||
inners = inners.map(function(inner) { return _.map(inner.nodes, 'loc'); });
|
||||
@@ -241,13 +242,13 @@ _.extend(Relation.prototype, {
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = outers[o];
|
||||
if (iD.geo.polygonContainsPolygon(outer, inner))
|
||||
if (polygonContainsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = outers[o];
|
||||
if (iD.geo.polygonIntersectsPolygon(outer, inner))
|
||||
if (polygonIntersectsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
import { Entity } from './entity';
|
||||
import { oneWayTags } from './tags';
|
||||
import { cross, Extent } from '../geo/index';
|
||||
|
||||
export function Way() {
|
||||
if (!(this instanceof Way)) {
|
||||
return (new Way()).initialize(arguments);
|
||||
@@ -34,7 +36,7 @@ _.extend(Way.prototype, {
|
||||
|
||||
extent: function(resolver) {
|
||||
return resolver.transient(this, 'extent', function() {
|
||||
var extent = iD.geo.Extent();
|
||||
var extent = Extent();
|
||||
for (var i = 0; i < this.nodes.length; i++) {
|
||||
var node = resolver.hasEntity(this.nodes[i]);
|
||||
if (node) {
|
||||
@@ -113,7 +115,7 @@ _.extend(Way.prototype, {
|
||||
var o = coords[(i+1) % coords.length],
|
||||
a = coords[i],
|
||||
b = coords[(i+2) % coords.length],
|
||||
res = iD.geo.cross(o, a, b);
|
||||
res = cross(o, a, b);
|
||||
|
||||
curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
|
||||
if (curr === 0) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { angle as getAngle } from './index';
|
||||
import { Way } from '../core/index';
|
||||
|
||||
export function Turn(turn) {
|
||||
if (!(this instanceof Turn))
|
||||
@@ -40,14 +41,14 @@ export function Intersection(graph, vertexId) {
|
||||
var splitIndex, wayA, wayB, indexA, indexB;
|
||||
if (isClosingNode) {
|
||||
splitIndex = Math.ceil(way.nodes.length / 2); // split at midpoint
|
||||
wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
|
||||
wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
wayA = Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
|
||||
wayB = Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
indexA = 1;
|
||||
indexB = way.nodes.length - 2;
|
||||
} else {
|
||||
splitIndex = _.indexOf(way.nodes, vertex.id, 1); // split at vertexid
|
||||
wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
|
||||
wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
wayA = Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
|
||||
wayB = Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
|
||||
indexA = splitIndex - 1;
|
||||
indexB = splitIndex + 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import * as actions from './actions/index';
|
||||
import * as geo from './geo/index';
|
||||
|
||||
export { Connection } from './core/connection';
|
||||
export { Difference } from './core/difference';
|
||||
export { Entity } from './core/entity';
|
||||
export { Graph } from './core/graph';
|
||||
export { History } from './core/history';
|
||||
export { Node } from './core/node';
|
||||
export { Relation } from './core/relation';
|
||||
export { oneWayTags, pavedTags, interestingTag } from './core/tags';
|
||||
export { Tree } from './core/tree';
|
||||
export { Way } from './core/way';
|
||||
|
||||
export {
|
||||
actions,
|
||||
geo
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
var fs = require('fs');
|
||||
var args = require('minimist')(process.argv.slice(2));
|
||||
var global = [];
|
||||
function readFiles(dirname, outdir) {
|
||||
var filenames = fs.readdirSync(dirname);
|
||||
filenames.forEach(function(filename) {
|
||||
var fileData = fs.readFileSync(dirname + filename).toString().split('\n');
|
||||
processFile(fileData, outdir + filename);
|
||||
});
|
||||
}
|
||||
var POSSIBLE_MODULES = [ 'actions', 'geo', 'modes', 'util', 'core', 'behavior' ];
|
||||
function findData(data) {
|
||||
var modules = { 'actions': [], 'geo': [], 'modes': [], 'util': [], 'core': [], 'behavior': [] };
|
||||
var cores = [ 'Entity', 'Way', 'Relation', 'Node', 'Graph', 'Tree', 'Difference', 'History' ];
|
||||
var ret = data.map(function(lineArg) {
|
||||
var line = lineArg;
|
||||
POSSIBLE_MODULES.forEach(function(mod) {
|
||||
cores.forEach(function(c) {
|
||||
while (line.indexOf('iD.' + c) > -1 ) {
|
||||
var start = line.indexOf('iD.' + c);
|
||||
var prefix = 3;
|
||||
line = line.slice(0, start) + line.slice(start + prefix);
|
||||
if (modules.core.indexOf(c) === -1) {
|
||||
modules.core.push(c);
|
||||
console.log(c);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
while (line.indexOf('iD.' + mod + '.') > -1 ) {
|
||||
var start = line.indexOf('iD.' + mod + '.');
|
||||
var prefix = 3 + mod.length + 1;
|
||||
var end = line.indexOf('(', start);
|
||||
var foo = line.slice(start + prefix, end);
|
||||
if (modules[mod].indexOf(foo) === -1) {
|
||||
modules[mod].push(foo);
|
||||
}
|
||||
line = line.slice(0, start) + line.slice(start + prefix);
|
||||
}
|
||||
});
|
||||
return line;
|
||||
});
|
||||
POSSIBLE_MODULES.forEach(function(mod) {
|
||||
if (modules[mod].length > 0) {
|
||||
var importStuff = modules[mod].join(', ');
|
||||
ret.unshift(`import { ${importStuff} } from '../${mod}/index';`);
|
||||
/*eslint-disable */
|
||||
/*eslint-enable */
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function processFile(fd, name) {
|
||||
global.push({
|
||||
name: name,
|
||||
data: fd
|
||||
});
|
||||
}
|
||||
|
||||
readFiles(args.dir, args.out);
|
||||
|
||||
|
||||
global.forEach(function (f) {
|
||||
var processedData = findData(f.data);
|
||||
fs.writeFile(f.name, processedData.join('\n'), function (e) {if (e) console.log(e);});
|
||||
});
|
||||
+1
-3
@@ -41,10 +41,8 @@
|
||||
<script src='../js/lib/osmauth.js'></script>
|
||||
|
||||
<script src='../js/id/id.js'></script>
|
||||
<script src='../js/lib/id/actions.js'></script>
|
||||
<script src='../js/lib/id/core.js'></script>
|
||||
<script src='../js/lib/id/index.js'></script>
|
||||
<script src='../js/lib/id/behavior.js'></script>
|
||||
<script src='../js/lib/id/geo.js'></script>
|
||||
<script src='../js/lib/id/modes.js'></script>
|
||||
<script src='../js/lib/id/operations.js'></script>
|
||||
<script src='../js/lib/id/presets.js'></script>
|
||||
|
||||
Reference in New Issue
Block a user