NOTES.md 11.6 KB
Newer Older
Tom MacWright's avatar
Tom MacWright committed
1 2 3 4
## Browser Features caniuse

* [CORS](http://caniuse.com/#feat=cors)
* [SVG](http://caniuse.com/#feat=svg)
Tom MacWright's avatar
Tom MacWright committed
5
* [CSS 3D Transforms](http://caniuse.com/#feat=transforms3d): No IE9, No Opera
Tom MacWright's avatar
Tom MacWright committed
6 7 8
* [localStorage](http://caniuse.com/#feat=namevalue-storage)
* [hashchange event](http://caniuse.com/#feat=hashchange)

Tom MacWright's avatar
Tom MacWright committed
9 10
## Interactions

Tom MacWright's avatar
Tom MacWright committed
11 12 13 14 15 16 17
Way drawing strategy:

* START: Click to start way
* END: Click on own node
* END: Click on self-segment
* END: Escape key

18 19 20
* [Adding a new road in MapZen](http://www.youtube.com/watch?v=tdpDm6MiUEM)
* [Editing turn restrictions](http://www.youtube.com/watch?v=DyHorSJFbyg)

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
### Continuing Ways

Continuing ways is a tricky problem: for instance, fixing unclosed areas by
completing the loop.

**Potlatch** does this by clicking on the way (since it does
not show handles by default), showing handles, and if you click on the start
or end, it immediately starts drawing. If you escape out of that draw, you can
then edit the node itself.

**JOSM**, since it has an `add` mode, just selects the node if you click a start
or end. You need to be in draw mode, and then clicking on a start/end node
extends the way.

_However_ JOSM also does a little trick where if you have a node selected and
then enter draw mode, it'll start either a new way or a continuation of the way,
depending on whether it's the start or the end.

39 40 41
Also what happens if you click on a point that happens to be the start or end
of more than one way?

42 43 44 45
## Relations and Turn Restrictions

http://wiki.openstreetmap.org/wiki/Relation:restriction

Tom MacWright's avatar
Tom MacWright committed
46 47 48 49 50 51 52
## Pathological conditions

* Ways with one node
* Relations which contain themselves (circular references)
* Nodes with no tags and no way attached
* Ways which contain only nodes that are subsets of the nodes of other ways
* Paths with intersecting boundaries (invalid geometries)
53
* Nodes with tags that repeat what the way says
Tom MacWright's avatar
Tom MacWright committed
54

Tom MacWright's avatar
Tom MacWright committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68
## Code Layout

This follows a similar layout to d3: each module of d3 has a file with its exact
name, like

```javascript
// format.js

iD.format = {};
```

And the parts of that module are in separate files that implement `iD.format.XML`
and so on.

69 70
## The Graph

71
The data model of OSM is something like
72 73 74 75 76 77

    root -> relations (-> relations) -> ways -> nodes
       \                             \> nodes
        \-  ways -> nodes
         \- nodes

78 79 80 81 82 83
In English:

* Relations have (ways, nodes, relations)
* Ways have (nodes)
* Nodes have ()

84 85 86 87 88 89 90 91
iD implements a [persistent data structure](http://en.wikipedia.org/wiki/Persistent_data_structure)
over the OSM data model. Instead of updating the graph in place when edits are made and storing enough
information about the change so that it can be undone in place, changes produce a new version of the
graph data structure, and the previous version is left untouced. "Undo" is accomplished simply by
reverting to the previous version.

The persistent data structure approach also takes advantage of the fact that the typical change modifies
only a small portion of the graph. The unchanged majority of the graph is shared between revisions,
John Firebaugh's avatar
John Firebaugh committed
92
keeping memory use to a minimum. For example, the `iD.actions.RemoveWayNode` action removes a single
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
node from a way. It produces new versions of three objects:

* The Array of nodes in the way.
* The way itself. The new version references the new Array of nodes.
* The Graph. The new version references the new way.

The previous versions of these three objects are retained. Since the previous version of the Graph
continues to reference the previous version of the way and its nodes, the action can be undone by
restoring this version. Meanwhile, both versions of the Graph share references to all the other
objects. Since these objects are never themselves mutated, this is safe.

In concrete terms, this approach dictates the following rule: all methods that produce a change in
the state of the data model objects (Entity, Graph) or their constituent parts (e.g. nodes Array,
tags Object) must return a new instance of the appropriate type, leaving the current instance
unchanged.

109 110
## UI

111
Rendering and UI code generally follows a [convention for reusable elements established by d3](http://bost.ocks.org/mike/chart/).
112 113 114 115 116 117 118 119 120 121

Constructor functions typically return a function decorated with additional properties. The function
can be called with `this` set to a d3 selection in order to set up the HTML structure. This is usually
done via [selection.call](https://github.com/mbostock/d3/wiki/Selections#wiki-call).

Accessors are implemented as a unified getter/setter function. When called with no arguments,
it acts as a getter; when called with an argument it acts as a setter and returns self, for
chaining. Accessors are preferable to constructor arguments; constructors typically take zero
arguments.

122
## Performance
123

124 125
See blog post: http://mapbox.com/osmdev/2012/11/20/getting-serious-about-svg/

126
Main performance concerns of iD:
127

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
### Panning & zooming performance of the map

SVG redraws are costly, especially when they require all features to
be reprojected.

Approaches:

* Using CSS transforms for intermediate map states, and then redrawing when
  map movement stops
* "In-between" projecting features to make reprojection cheaper

### Memory overhead of objects

Many things will be stored by iD. With the graph structure in place, we'll
be storing much more.

Tom MacWright's avatar
Tom MacWright committed
144 145 146 147
We also need to worry about **memory leaks**, which have been a big problem
in Potlatch 2. Storing OSM data and versions leads to a lot of object-referencing
in Javascript.

148 149 150 151 152 153 154 155
## Connection, Graph, Map

The Map is a display and manipulation element. It should have minimal particulars
of how exactly to store or retrieve data. It gets data from Connection and
asks for it from Graph.

Graph stores all of the objects and all of the versions of those objects.
Connection requests objects over HTTP, parses them, and provides them to Graph.
156 157 158 159 160 161 162 163

## loaded

The `.loaded` member of nodes and ways is because of [relations](http://wiki.openstreetmap.org/wiki/Relation),
which refer to elements, so we want to have real references of those
elements, but we don't have the data yet. Thus when the Connection
encounters a new object but has a non-loaded representation of it,
the non-loaded version is replaced.
Tom MacWright's avatar
Tom MacWright committed
164 165 166

## Prior Art

Tom MacWright's avatar
Tom MacWright committed
167 168 169 170 171 172 173 174
Editors:

* [Potlatch 2](http://wiki.openstreetmap.org/wiki/Potlatch_2)
* [JOSM](http://josm.openstreetmap.de/)
* [Nokia Here Maps](http://here.net/mapcreator/6.806948216077587,-58.14884979189826,20,0,0?scope=)
* [Waze](http://www.waze.com/)
* [Google Map Maker](http://www.google.com/mapmaker)

Tom MacWright's avatar
Tom MacWright committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
JOSM and Potlatch 2 appear to implement versioning in the same way, but having
an undo stack:

```java
// src/org/openstreetmap/josm/actions/MoveNodeAction.java
Main.main.undoRedo.add(new MoveCommand(n, coordinates));

// src/org/openstreetmap/josm/command/MoveCommand.java

/**
 * List of all old states of the objects.
 */
private List<OldState> oldState = new LinkedList<OldState>();

@Override public boolean executeCommand() {
// ...
}
@Override public void undoCommand() {
// ...
}
```
Tom MacWright's avatar
Tom MacWright committed
196 197 198 199 200 201 202 203 204 205 206 207 208

## Transforms Performance

There are two kinds of transforms: SVG and CSS. CSS transforms of SVG elements
are less efficient that SVG transforms of SVG elements. `translate` notation
has equivalent performance to `matrix` notation.

* [svg swarm with svg transform matrix](http://bl.ocks.org/d/4074697/)
* [svg swarm with svg transform translate](http://bl.ocks.org/d/4074808/)
* [svg swarm with css translate](http://bl.ocks.org/d/4074632/)

SVG transforms are a roughly 2x speedup relative to CSS - 16fps vs 32fps in
Google Chrome Beta.
Tom MacWright's avatar
Tom MacWright committed
209

Tom MacWright's avatar
Tom MacWright committed
210 211 212 213 214 215 216
* [svg swarm with css on html](http://bl.ocks.org/4081364)

However, using CSS transforms with HTML elements has vastly different and
better performance than using them with SVG elements. For this reason, iD
transforms a map-container element rather than a `g` element on panning
movements.

Tom MacWright's avatar
Tom MacWright committed
217 218 219 220 221 222 223 224 225 226 227
### Transforms in browsers

Matrix transforms are significantly slower than `translate` in webkit but
nearly equivalent in Firefox. Chrome is about 4x faster than Firefox with
transforms.

However, matrix transforms can also represent scale, and so they should be compared
with transform + scale. If you add an identity scale (`scale(1, 1)`), then
matrix and `translate scale` performance is similar in Chrome, though matrix
still lags significantly in Safari and Firefox.

Tom MacWright's avatar
Tom MacWright committed
228 229 230 231 232 233 234 235 236 237 238
## SVG point rounding performance

Rounding points in SVG gives a ~20% speedup.

* http://bl.ocks.org/4081369 ~18fps
* http://bl.ocks.org/4081356 ~22fps (about 20% faster)

And this is not just the effect of less `d` data:

* http://bl.ocks.org/4089090 ~18fps

Tom MacWright's avatar
Tom MacWright committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
## SVG Corner Cases

One-way streets need markers to indicate that they're one-way. Unfortunately
SVG [line markers](http://www.svgbasics.com/markers.html) are based strictly
off of vertices, so won't handle this case properly.

* textPath demo http://bl.ocks.org/4078870
* line markers http://bl.ocks.org/4079441

One way to resolve this is by using textPath with a glyph, like a gt sign or
triangle character if available. This has a few concerns:

* performance of textPath is known to suck in some cases. For simple cases, it is fine
* can we be absolutely sure about direction of text?
* glyphs need to be available. are webfonts svg-okay?
Tom MacWright's avatar
Tom MacWright committed
254 255 256 257 258

Or more importantly, we need to calculate the pixel length of a linestring,
calculate the width of a glyph, and do the necessary math so that it fills enough
of the line without overflowing.

259 260
See the [textPath element](http://www.w3.org/TR/SVG/text.html#TextPathElement) and its quirks.

Tom MacWright's avatar
Tom MacWright committed
261 262 263 264
See:

* [getComputedTextLength](http://www.w3.org/TR/SVG/text.html#__svg__SVGTextContentElement__getComputedTextLength)
* [getTotalLength](http://www.w3.org/TR/SVG/paths.html#__svg__SVGPathElement__getTotalLength)
Tom MacWright's avatar
Tom MacWright committed
265 266 267 268 269 270 271 272 273 274 275 276 277

## Authenticating

The [OAuth](http://oauth.net/) endpoint of OpenStreetMap does support
[CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
which is great and allows iD to do browser-side authentication. This requires some hacks,
mainly that a cookie is used to persist the token_secret between pageloads.

## Making Edits

    PUT /api/0.6/changeset/create
    POST /api/0.6/changeset/135324/upload
    PUT /api/0.6/changeset/135324/close
Tom MacWright's avatar
Tom MacWright committed
278 279 280 281 282 283 284 285 286 287 288

## Browser problems that affect iD

See also: [Kothic browser bugs](https://github.com/kothic/kothic-js/wiki/Browser-bugs).

**one-way streets** use glyphs and textPaths. letter-spacing is not supported
in Firefox but is in webkit so we need to use spaces.

And trailing spaces are not included in getComputedTextLength:

* https://bugzilla.mozilla.org/show_bug.cgi?id=346694
Tom MacWright's avatar
Tom MacWright committed
289 290 291 292 293 294

webkit doesn't let querySelectorAll select camelcase elements:

* https://bugs.webkit.org/show_bug.cgi?id=46800
* https://bugs.webkit.org/show_bug.cgi?id=83438
* https://github.com/mbostock/d3/issues/925
Tom MacWright's avatar
Tom MacWright committed
295

296 297 298 299 300 301 302
Firefox does not fire a `blur` event when an element is removed from the DOM,
unlike WebKit browsers.

Firefox does not support [the focusout event](https://bugzilla.mozilla.org/show_bug.cgi?id=687787).

Opera does not support `pointer-events` on HTML elements, only SVG elements.

303 304
IE10 does not support `navigator.language`, only `navigator.userLanguage`

Tom MacWright's avatar
Tom MacWright committed
305 306 307 308 309 310
## Transients

The graph supports `transient`, which is storage for non-versioned mutable
properties _about_ entities that are stored outside of entities. For instance,
`extent` is about an entity, but can be invalidated and stored without getting
a new graph.
John Firebaugh's avatar
John Firebaugh committed
311 312 313 314 315 316 317 318 319 320 321 322

## Data sources

*deprecated.json*
from http://wiki.openstreetmap.org/wiki/Deprecated_features
TODO: deal with deprecated 'class' tag
does not deal with landuse=wood because of indecision
we will not care about http://taginfo.openstreetmap.org/tags/bicycle_parking=sheffield

*discarded.json*
entirely discarded tags
https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/XMLConnection.as#L24