Merge branch 'master' of github.com:systemed/iD

This commit is contained in:
Tom MacWright
2012-12-05 16:17:20 -05:00
30 changed files with 2061 additions and 119 deletions

View File

@@ -13,6 +13,7 @@ all: \
.INTERMEDIATE iD.js: \
js/lib/d3.v3.js \
js/lib/d3.geo.tile.js \
js/lib/d3.keybinding.js \
js/lib/d3.one.js \
js/lib/d3.size.js \
js/lib/d3.typeahead.js \
@@ -26,6 +27,7 @@ all: \
js/id/oauth.js \
js/id/taginfo.js \
js/id/util.js \
js/id/actions.js \
js/id/actions/*.js \
js/id/modes.js \
js/id/modes/*.js \

1797
img/source/design.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 KiB

View File

@@ -40,7 +40,18 @@
<script src='js/id/ui/loading.js'></script>
<script src='js/id/ui/userpanel.js'></script>
<script src='js/id/actions/actions.js'></script>
<script src='js/id/actions.js'></script>
<script src='js/id/actions/add_node.js'></script>
<script src='js/id/actions/add_way_node.js'></script>
<script src='js/id/actions/change_tags.js'></script>
<script src='js/id/actions/change_way_direction.js'></script>
<script src="js/id/actions/delete_node.js"></script>
<script src="js/id/actions/delete_way.js"></script>
<script src='js/id/actions/move.js'></script>
<script src='js/id/actions/noop.js'></script>
<script src='js/id/actions/remove_relation_entity.js'></script>
<script src='js/id/actions/remove_way_node.js'></script>
<script src='js/id/actions/start_way.js'></script>
<script src='js/id/modes.js'></script>
<script src='js/id/modes/add_area.js'></script>

1
js/id/actions.js Normal file
View File

@@ -0,0 +1 @@
iD.actions = {};

View File

@@ -1,80 +0,0 @@
iD.actions = {};
iD.actions.noop = function() {
return function(graph) {
return graph;
};
};
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/AddCommand.java
iD.actions.addNode = function(node) {
return function(graph) {
return graph.replace(node, 'added a place');
};
};
iD.actions.startWay = function(way) {
return function(graph) {
return graph.replace(way, 'started a road');
};
};
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
iD.actions.remove = function(node) {
return function(graph) {
return graph.remove(node, 'removed a feature');
};
};
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
iD.actions.addWayNode = function(way, node, index) {
return function(graph) {
var nodes = way.nodes.slice();
nodes.splice(index || nodes.length, 0, node.id);
return graph.replace(way.update({nodes: nodes})).replace(node, 'added to a road');
};
};
iD.actions.removeWayNode = function(way, node) {
return function(graph) {
var nodes = _.without(way.nodes, node.id);
return graph.replace(way.update({nodes: nodes}), 'removed from a road');
};
};
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
iD.actions.changeWayDirection = function(way) {
return function(graph) {
return graph.replace(way.update({
nodes: way.nodes.slice()
}), 'changed way direction');
};
};
iD.actions.changeTags = function(node, tags) {
return function(graph) {
return graph.replace(node.update({
tags: tags
}), 'changed tags');
};
};
// 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
iD.actions.move = function(entity, loc) {
return function(graph) {
return graph.replace(entity.update({loc: loc}), 'moved an element');
};
};
iD.actions.addTemporary = function(node) {
return function(graph) {
return graph.replace(node);
};
};
iD.actions.removeTemporary = function(node) {
return function(graph) {
return graph.remove(node);
};
};

View File

@@ -0,0 +1,6 @@
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/AddCommand.java
iD.actions.addNode = function(node) {
return function(graph) {
return graph.replace(node, 'added a place');
};
};

View File

@@ -0,0 +1,8 @@
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
iD.actions.addWayNode = function(way, node, index) {
return function(graph) {
var nodes = way.nodes.slice();
nodes.splice(index || nodes.length, 0, node.id);
return graph.replace(way.update({nodes: nodes})).replace(node, 'added to a road');
};
};

View File

@@ -0,0 +1,7 @@
iD.actions.changeTags = function(node, tags) {
return function(graph) {
return graph.replace(node.update({
tags: tags
}), 'changed tags');
};
};

View File

@@ -0,0 +1,8 @@
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
iD.actions.changeWayDirection = function(way) {
return function(graph) {
return graph.replace(way.update({
nodes: way.nodes.slice()
}), 'changed way direction');
};
};

View File

@@ -0,0 +1,16 @@
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
iD.actions.DeleteNode = function(node) {
return function(graph) {
graph.parentWays(node.id)
.forEach(function(parent) {
graph = iD.actions.removeWayNode(parent, node)(graph);
});
graph.parentRelations(node.id)
.forEach(function(parent) {
graph = iD.actions.removeRelationEntity(parent, node)(graph);
});
return graph.remove(node, 'removed a node');
};
};

View File

@@ -0,0 +1,21 @@
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
iD.actions.DeleteWay = function(way) {
return function(graph) {
graph.parentRelations(way.id)
.forEach(function(parent) {
graph = iD.actions.removeRelationEntity(parent, way)(graph);
});
way.nodes.forEach(function (id) {
var node = graph.entity(id);
graph = iD.actions.removeWayNode(way, node)(graph);
if (!graph.parentWays(id).length && !graph.parentRelations(id).length) {
graph = graph.remove(node);
}
});
return graph.remove(way, 'removed a way');
};
};

7
js/id/actions/move.js Normal file
View File

@@ -0,0 +1,7 @@
// 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
iD.actions.move = function(entity, loc) {
return function(graph) {
return graph.replace(entity.update({loc: loc}), 'moved an element');
};
};

5
js/id/actions/noop.js Normal file
View File

@@ -0,0 +1,5 @@
iD.actions.noop = function() {
return function(graph) {
return graph;
};
};

View File

@@ -0,0 +1,6 @@
iD.actions.removeRelationEntity = function(relation, entity) {
return function(graph) {
var members = _.without(relation.members, entity.id);
return graph.replace(relation.update({members: members}), 'removed from a relation');
};
};

View File

@@ -0,0 +1,6 @@
iD.actions.removeWayNode = function(way, node) {
return function(graph) {
var nodes = _.without(way.nodes, node.id);
return graph.replace(way.update({nodes: nodes}), 'removed from a road');
};
};

View File

@@ -0,0 +1,5 @@
iD.actions.startWay = function(way) {
return function(graph) {
return graph.replace(way, 'started a road');
};
};

View File

@@ -12,6 +12,9 @@ iD.Entity = function(a, b, c) {
if (iD.debug) {
Object.freeze(this);
Object.freeze(this.tags);
if (this.nodes) Object.freeze(this.nodes);
if (this.members) Object.freeze(this.members);
}
};
@@ -30,11 +33,11 @@ iD.Entity.prototype = {
};
iD.Node = function(attrs) {
return iD.Entity({tags: {}}, attrs || {}, {type: 'node'});
return iD.Entity(attrs || {}, {type: 'node'});
};
iD.Way = function(attrs) {
return iD.Entity({tags: {}, nodes: []}, attrs || {}, {type: 'way'});
return iD.Entity({nodes: []}, attrs || {}, {type: 'way'});
};
iD.Way.isOneWay = function(d) {
@@ -50,5 +53,5 @@ iD.Way.isArea = function(d) {
};
iD.Relation = function(attrs) {
return iD.Entity({tags: {}}, attrs || {}, {type: 'relation'});
return iD.Entity({members: []}, attrs || {}, {type: 'relation'});
};

View File

@@ -1,7 +1,15 @@
iD.Graph = function(entities, annotation) {
if (!(this instanceof iD.Graph)) return new iD.Graph(entities, annotation);
this.entities = entities || {};
if (_.isArray(entities)) {
this.entities = {};
for (var i = 0; i < entities.length; i++) {
this.entities[entities[i].id] = entities[i];
}
} else {
this.entities = entities || {};
}
this.annotation = annotation;
if (iD.debug) {
@@ -15,11 +23,17 @@ iD.Graph.prototype = {
return this.entities[id];
},
parents: function(id) {
parentWays: function(id) {
// This is slow and a bad hack.
return _.filter(this.entities, function(e) {
if (e.type !== 'way') return false;
return e.nodes.indexOf(id) !== -1;
return e.type === 'way' && e.nodes.indexOf(id) !== -1;
});
},
parentRelations: function(id) {
// This is slow and a bad hack.
return _.filter(this.entities, function(e) {
return e.type === 'relation' && e.members.indexOf(id) !== -1;
});
},

View File

@@ -22,7 +22,7 @@ iD.modes.AddRoad = function() {
node = datum;
var id = datum.id;
var parents = mode.history.graph().parents(id);
var parents = mode.history.graph().parentWays(id);
if (parents.length) {
if (parents[0].nodes[0] === id) {
way = parents[0];

View File

@@ -27,8 +27,7 @@ iD.modes.DrawArea = function(way_id) {
mode.history.replace(iD.actions.addWayNode(way,
mode.history.graph().entity(way.nodes[0])));
delete way.tags.elastic;
mode.history.perform(iD.actions.changeTags(way, way.tags));
mode.history.perform(iD.actions.changeTags(way, _.omit(way.tags, 'elastic')));
// End by clicking on own tail
return mode.controller.enter(iD.modes.Select(way));

View File

@@ -37,8 +37,7 @@ iD.modes.DrawRoad = function(way_id, direction) {
mode.history.graph().entity(lastNode), index));
}
delete way.tags.elastic;
mode.history.perform(iD.actions.changeTags(way, way.tags));
mode.history.perform(iD.actions.changeTags(way, _.omit(way.tags, 'elastic')));
// End by clicking on own tail
return mode.controller.enter(iD.modes.Select(way));

View File

@@ -15,7 +15,7 @@ iD.modes.Select = function (entity) {
if (!dragging) {
dragging = iD.util.trueObj([entity.id].concat(
_.pluck(mode.history.graph().parents(entity.id), 'id')));
_.pluck(mode.history.graph().parentWays(entity.id), 'id')));
mode.history.perform(iD.actions.noop());
}
@@ -33,13 +33,13 @@ iD.modes.Select = function (entity) {
});
function remove() {
// Remove this node from any ways that is a member of
mode.history.graph().parents(entity.id)
.filter(function(d) { return d.type === 'way'; })
.forEach(function(parent) {
mode.history.perform(iD.actions.removeWayNode(parent, entity));
});
mode.history.perform(iD.actions.remove(entity));
switch (entity.type) {
case 'way':
mode.history.perform(iD.actions.DeleteWay(entity));
case 'node':
mode.history.perform(iD.actions.DeleteNode(entity));
}
mode.controller.exit();
}

View File

@@ -30,7 +30,7 @@ iD.Map = function() {
}
dragging = iD.util.trueObj([entity.id].concat(
_.pluck(history.graph().parents(entity.id), 'id')));
_.pluck(history.graph().parentWays(entity.id), 'id')));
history.perform(iD.actions.noop());
}

View File

@@ -42,7 +42,18 @@
<script src='../js/id/ui/loading.js'></script>
<script src='../js/id/ui/userpanel.js'></script>
<script src='../js/id/actions/actions.js'></script>
<script src='../js/id/actions.js'></script>
<script src='../js/id/actions/add_node.js'></script>
<script src='../js/id/actions/add_way_node.js'></script>
<script src='../js/id/actions/change_tags.js'></script>
<script src='../js/id/actions/change_way_direction.js'></script>
<script src="../js/id/actions/delete_node.js"></script>
<script src="../js/id/actions/delete_way.js"></script>
<script src='../js/id/actions/move.js'></script>
<script src='../js/id/actions/noop.js'></script>
<script src='../js/id/actions/remove_relation_entity.js'></script>
<script src='../js/id/actions/remove_way_node.js'></script>
<script src='../js/id/actions/start_way.js'></script>
<script src='../js/id/modes.js'></script>
<script src='../js/id/modes/add_area.js'></script>
@@ -72,6 +83,8 @@
<!-- include spec files here... -->
<script src="spec/actions/add_way_node.js"></script>
<script src="spec/actions/delete_node.js"></script>
<script src="spec/actions/delete_way.js"></script>
<script src="spec/actions/remove_way_node.js"></script>
<script src="spec/format/geojson.js"></script>
<script src="spec/format/xml.js"></script>

View File

@@ -26,6 +26,8 @@
<!-- include spec files here... -->
<script src="spec/actions/add_way_node.js"></script>
<script src="spec/actions/delete_node.js"></script>
<script src="spec/actions/delete_way.js"></script>
<script src="spec/actions/remove_way_node.js"></script>
<script src="spec/format/geojson.js"></script>
<script src="spec/format/xml.js"></script>

View File

@@ -0,0 +1,24 @@
describe("iD.actions.DeleteNode", function () {
it("removes the node from the graph", function () {
var node = iD.Node(),
action = iD.actions.DeleteNode(node),
graph = action(iD.Graph([node]));
expect(graph.entity(node.id)).to.be.undefined;
});
it("removes the node from parent ways", function () {
var node = iD.Node(),
way = iD.Way({nodes: [node.id]}),
action = iD.actions.DeleteNode(node),
graph = action(iD.Graph([node, way]));
expect(graph.entity(way.id).nodes).not.to.contain(node.id);
});
it("removes the node from parent relations", function () {
var node = iD.Node(),
relation = iD.Relation({members: [node.id]}),
action = iD.actions.DeleteNode(node),
graph = action(iD.Graph([node, relation]));
expect(graph.entity(relation.id).members).not.to.contain(node.id);
});
});

View File

@@ -0,0 +1,36 @@
describe("iD.actions.DeleteWay", function () {
it("removes the way from the graph", function () {
var way = iD.Way(),
action = iD.actions.DeleteWay(way),
graph = action(iD.Graph([way]));
expect(graph.entity(way.id)).to.be.undefined;
});
it("removes a way from parent relations", function () {
var way = iD.Way(),
relation = iD.Relation({members: [way.id]}),
action = iD.actions.DeleteWay(way),
graph = action(iD.Graph([way, relation]));
expect(graph.entity(relation.id).members).not.to.contain(way.id);
});
it("deletes member nodes not referenced by another parent", function () {
var node = iD.Node(),
way = iD.Way({nodes: [node.id]}),
action = iD.actions.DeleteWay(way),
graph = action(iD.Graph([node, way]));
expect(graph.entity(node.id)).to.be.undefined;
});
it("does not delete member nodes referenced by another parent", function () {
var node = iD.Node(),
way1 = iD.Way({nodes: [node.id]}),
way2 = iD.Way({nodes: [node.id]}),
action = iD.actions.DeleteWay(way1),
graph = action(iD.Graph([node, way1, way2]));
expect(graph.entity(node.id)).not.to.be.undefined;
});
it("does not delete member nodes with interesting tags");
it("registers member nodes with interesting tags as POIs");
});

View File

@@ -131,6 +131,14 @@ describe('Relation', function () {
expect(iD.Relation({id: 'r1234'}).modified()).not.to.be.ok;
});
it("defaults members to an empty array", function () {
expect(iD.Relation().members).to.eql([]);
});
it("sets members as specified", function () {
expect(iD.Relation({members: ["n-1"]}).members).to.eql(["n-1"]);
});
it("defaults tags to an empty object", function () {
expect(iD.Relation().tags).to.eql({});
});

View File

@@ -1,21 +1,19 @@
describe('Graph', function() {
describe('iD.Graph', function() {
it("can be constructed with an entities Object", function () {
var entity = iD.Entity(),
graph = iD.Graph({'n-1': entity});
expect(graph.entity('n-1')).to.equal(entity);
});
describe('Construction and access', function() {
it('entity', function() {
var entities = { 'n-1': {
type: 'node',
loc: [-80, 30],
id: 'n-1'
}
};
var graph = iD.Graph(entities, 'first graph');
expect(graph.entity('n-1')).to.equal(entities['n-1']);
});
it("can be constructed with an entities Array", function () {
var entity = iD.Entity(),
graph = iD.Graph([entity]);
expect(graph.entity(entity.id)).to.equal(entity);
});
it('annotation', function() {
var graph = iD.Graph({}, 'first graph');
expect(graph.annotation).to.equal('first graph');
});
it('can be constructed with an annotation', function() {
var graph = iD.Graph({}, 'first graph');
expect(graph.annotation).to.equal('first graph');
});
describe('operations', function() {
@@ -50,6 +48,26 @@ describe('Graph', function() {
});
});
describe("#parentWays", function() {
it("returns an array of ways that contain the given node id", function () {
var node = iD.Node({id: "n1"}),
way = iD.Way({id: "w1", nodes: ["n1"]}),
graph = iD.Graph({n1: node, w1: way});
expect(graph.parentWays("n1")).to.eql([way]);
expect(graph.parentWays("n2")).to.eql([]);
});
});
describe("#parentRelations", function() {
it("returns an array of relations that contain the given entity id", function () {
var node = iD.Node({id: "n1"}),
relation = iD.Relation({id: "r1", members: ["n1"]}),
graph = iD.Graph({n1: node, r1: relation});
expect(graph.parentRelations("n1")).to.eql([relation]);
expect(graph.parentRelations("n2")).to.eql([]);
});
});
describe("#fetch", function () {
it("replaces node ids with references", function () {
var node = iD.Node({id: "n1"}),