Skip to content
Browse files

Bring in changes from blog post

  • Loading branch information...
1 parent d964d7f commit 0ac770de3bc354bae1bf902c5291c60b7ddde8e0 @jfirebaugh jfirebaugh committed
Showing with 89 additions and 48 deletions.
  1. +89 −48 ARCHITECTURE.md
View
137 ARCHITECTURE.md
@@ -19,35 +19,41 @@ which implements map panning and zooming.
## Core
-The iD *core* implements the OSM data types, a graph of OSM object's
-relationships to each other, an undo/redo history for changes made during
-editing, and a couple of important auxiliary classes. It aims to be generic
-enough to be used by other JavaScript-based tools for OpenStreetMap.
-
-Briefly, the OSM data model includes three basic data types: nodes, ways, and
-relations. A _node_ is a point type, having a single geographic coordinate. A
-_way_ is an ordered list of nodes. And a _relation_ groups together nodes,
-ways, and other relations to provide free-form higher-level structures. Each
-of these three types has _tags_: an associative array of key-value pairs which
+The iD *core* implements OSM data types, a graph of OSM objects'
+relationships to one another, an undo/redo history for changes made during
+editing, and a couple of important auxiliary classes. It eventually aims
+to be a reusable, modular library to kickstart other JavaScript-based
+tools for OpenStreetMap.
+
+The OSM data model includes three basic data types: nodes, ways, and
+relations.
+
+* A _node_ is a point type, having a single geographic coordinate.
+* A _way_ is an ordered list of nodes.
+* A _relation_ groups together nodes, ways, and other relations to provide
+ free-form higher-level structures.
+
+Each of these three types has _tags_: an associative array of key-value pairs which
describe the object.
In iD, these three types are implemented by `iD.Node`, `iD.Way` and
-`iD.Relation`. These three classes inherit from a common base, `iD.Entity`
-(the only use of classical inheritance in iD). Generically, we refer to a
+`iD.Relation`. These three classes inherit from a common base, `iD.Entity`.
+This is the only use of classical inheritance in iD, but it's justified
+by the common functionality of the types. Generically, we refer to a
node, way or relation as an _entity_.
-Every entity has an _ID_ either assigned by the OSM database, or, for an
-entity that is newly created, constructed as a proxy consisting of a negative
-numeral. IDs from the OSM database as treated as opaque strings; no
+Every entity has an _ID_ either assigned by the OSM database or
+a negative, local identifier assigned by iD for newly-created objects.
+IDs from the OSM database as treated as opaque strings; no
[assumptions](http://lists.openstreetmap.org/pipermail/dev/2013-February/026495.html)
are made of them other than that they can be compared for identity and do not
-begin with a minus sign (and thus will not conflict with proxy IDs). In fact,
-in the OSM database the three types of entities have separate ID spaces; a
-node can have the same ID as a way, for instance. Because it is useful to
-store heterogeneous entities in the same datastructure, iD ensures that every
-entity has a fully-unique ID by prefixing each OSM ID with the first letter of
-the entity type. For example, a way with OSM ID 123456 is represented as
-'w123456' within iD.
+begin with a minus sign (and thus will not conflict with proxy IDs). The three
+types of entities have separate ID spaces: a
+node can have the same numeric ID as a way or a relation. Instead of segregating
+ways, nodes, and other entities into different datastructures,
+iD internally uses fully-unique IDs generated by prefixing
+each OSM ID with the first letter of the entity type. For example, a way
+with OSM ID 123456 is represented as 'w123456' within iD.
iD entities are *immutable*: once constructed, an `Entity` object cannot
change. Tags cannot be updated; nodes cannot be added or removed from ways,
@@ -56,8 +62,9 @@ entity: if your code has a reference to one, it is safe to store it and use it
later, knowing that it cannot have been changed outside of your control. It
also makes it possible to implement the entity graph (described below) as an
efficient [persistent data
-structure](http://en.wikipedia.org/wiki/Persistent_data_structure). But
-obviously, iD is an editor, and must allow entities to change somehow. The
+structure](http://en.wikipedia.org/wiki/Persistent_data_structure).
+
+Since iD is an editor, it must allow for new versions of entities. The
solution is that all edits produce new copies of anything that changes. At the
entity level, this takes the form of methods such as `iD.Node#move`, which
returns a new node object that has the same ID and tags as the original, but a
@@ -65,46 +72,81 @@ different coordinate. More generically, `iD.Entity#update` returns a new
entity of the same type and ID as the original but with specified properties
such as `nodes`, `tags`, or `members` replaced.
+![](http://farm9.staticflickr.com/8087/8508309757_ccf5b6f09b_o.png)
+
Entities are related to one another: ways have many nodes and relations have
-many members. In order to render a map of a certain area, iD needs a
+many members. To render a map of a certain area, iD needs a
datastructure to hold all the entities in that area and traverse these
relationships. `iD.Graph` provides this functionality. The core of a graph is
a map between IDs and the associated entities; given an ID, the graph can give
you the entity. Like entities, a graph is immutable: adding, replacing, or
removing an entity produces a new graph, and the original is unchanged.
-Because entities are immutable, the original and new graphs can share
-references to entities that have not changed, keeping memory use to a minimum.
-If you are familiar with how git works internally, this persistent data
-structure approach is very similar.
-
-The last major component of the core is `iD.History`, which tracks the changes
-made in an editing session and provide undo/redo capabilities. Here, the
+Because entities are immutable, the original and new graphs can minimize
+memory use by sharing references to entities that have not changed instead of
+copying the entire graph.
+This persistent data structure approach is similar to the internals of
+the [git](http://git-scm.com/) revision control system.
+
+The final major component of the core is `iD.History`, which tracks the changes
+made in an editing session and provides undo/redo capabilities. Here, the
immutable nature of the core types really pays off: the history is a simple
stack of graphs, each representing the state of the data at a particular point
in editing. The graph at the top of the stack is the current state, off which
all rendering is based. To undo the last change, this graph is popped off the
-stack, and the map is re-rendered based on the new top of the stack. Contrast
-this to a mutable graph as used in JOSM and Potlatch: every command that
-changes the graph must implement an equal and opposite undo command that
-restores the graph to the previous state.
+stack, and the map is re-rendered based on the new top of the stack.
+
+This approach constitutes one of the main differences between iD's approach
+to data and that of [JOSM](http://josm.openstreetmap.de/) and
+[Potlatch 2](http://wiki.openstreetmap.org/wiki/Potlatch_2).
+Instead of changing a single copy of local data and having to implement
+an 'undo' for each specific action, actions in iD do not need to be aware
+of history and the undo system.
-Finally, we have the auxiliary classes `iD.Difference` and `iD.Tree`. The first
-encapsulates the difference between two graphs, and knows how to calculate the
+Finally, we have the auxiliary classes `iD.Difference` and `iD.Tree`.
+
+`iD.Difference` encapsulates the difference between two graphs, and knows how to calculate the
set of entities that were created, modified, or deleted, and need to be redrawn.
-The second provides a means of calculating which entities, out of all that have
-been downloaded, are currently visible. In order to do this very quickly as you
-pan or zoom the map, it uses an [R-tree](http://en.wikipedia.org/wiki/R-tree).
+
+```js
+var a = iD.Graph(), b = iD.Graph();
+// (fill a & b with data)
+var difference = iD.Difference(a, b);
+
+// returns entities created between and b
+difference.created();
+```
+
+`iD.Tree` calculates the set of downloaded entities that are visible in the
+current map view. To calculate this quickly during map
+interaction, it uses an [R-tree](http://en.wikipedia.org/wiki/R-tree).
+
+```js
+var graph = iD.Graph();
+// (load OSM data into graph)
+
+// this tree indexes the contents of the graph
+var tree = iD.Tree(graph);
+
+// quickly pull all features that intersect with an extent
+var features = tree.intersects(
+ iD.geo.Extent([0, 0], [2, 2]), tree.graph());
+```
## Actions
In iD, an _action_ is a function that accepts a graph as input and returns a
-modified graph as output. Actions typically need other inputs as well; for
+new, modified graph as output. Actions typically need other inputs as well; for
example, `iD.actions.DeleteNode` also requires the ID of a node to delete. The
additional input is passed to the action's constructor:
-```
-var action = iD.actions.DeleteNode('n123456'); // construct the action var
-newGraph = action(oldGraph); // apply the action
+```js
+// construct the action: this returns a function that remembers the
+// value `n123456` in a closure so that when it's called, it runs
+// the specified action on the graph
+var action = iD.actions.DeleteNode('n123456');
+
+// apply the action, yielding a new graph. oldGraph is untouched.
+newGraph = action(oldGraph);
```
iD provides actions for all the typical things an editor needs to do: add a
@@ -120,9 +162,8 @@ is a member.
As you can imagine, implementing all these details requires an expert
knowledge of the OpenStreetMap data model. It is our hope that JavaScript
-based tools for OpenStreetMap can reuse the implementations provided by iD in
-other contexts, significantly reducing the work necessary to create a robust
-tool.
+based tools for OpenStreetMap can reuse the iD's core implementation,
+significantly reducing the work necessary to create a robust tool.
## Modes

0 comments on commit 0ac770d

Please sign in to comment.
Something went wrong with that request. Please try again.