Merge remote-tracking branch 'systemed/master' into presets
Conflicts: css/app.css index.html js/id/connection.js js/id/modes/select.js js/id/ui/inspector.js js/id/util.js test/spec/modes/add_point.js
@@ -0,0 +1,203 @@
|
||||
## d3
|
||||
|
||||
[d3](http://d3js.org/) is the primary library used by iD. It is used for
|
||||
rendering the map data as well as many sorts of general DOM manipulation tasks
|
||||
for which jQuery would often be used.
|
||||
|
||||
Notable features of d3 that are used by iD include
|
||||
[d3.xhr](https://github.com/mbostock/d3/wiki/Requests#wiki-d3_xhr), which is
|
||||
used to make the API requests to download data from openstreetmap.org and save
|
||||
changes;
|
||||
[d3.dispatch](https://github.com/mbostock/d3/wiki/Internals#wiki-d3_dispatch),
|
||||
which provides a callback-based [Observer
|
||||
pattern](http://en.wikipedia.org/wiki/Observer_pattern) between different
|
||||
parts of iD;
|
||||
[d3.geo.path](https://github.com/mbostock/d3/wiki/Geo-Paths#wiki-path), which
|
||||
generates SVG paths for lines and areas; and
|
||||
[d3.behavior.zoom](https://github.com/mbostock/d3/wiki/Zoom-Behavior#wiki-zoom),
|
||||
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, and an undo/redo history for changes made during
|
||||
editing. 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
|
||||
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
|
||||
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
|
||||
[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.
|
||||
|
||||
iD entities are *immutable*: once constructed, an `Entity` object cannot
|
||||
change. Tags cannot be updated; nodes cannot be added or removed from ways,
|
||||
and so on. Immutability makes it easier to reason about the behavior of an
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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 final component of the core is comprised of `iD.History` and
|
||||
`iD.Difference`, which track the changes made in an editing session and
|
||||
provide 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.
|
||||
|
||||
## 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
|
||||
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 ```
|
||||
|
||||
iD provides actions for all the typical things an editor needs to do: add a
|
||||
new entity, split a way in two, connect the vertices of two ways together, and
|
||||
so on. In addition to performing the basic work needed to accomplish these
|
||||
things, an action typically contains a significant amount of logic for keeping
|
||||
the relationships between entities logical and consistent. For example, an
|
||||
action as apparently simple as `DeleteNode`, in addition to removing the node
|
||||
from the graph, needs to do two other things: remove the node from any ways in
|
||||
which it is a member (which in turn requires deleting parent ways that are
|
||||
left with just a single node), and removing it from any relations of which it
|
||||
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.
|
||||
|
||||
## Modes
|
||||
|
||||
With _modes_, we shift gears from abstract data types and algorithms to the
|
||||
parts of the architecture that implement the user interface for iD. Modes are
|
||||
manifested in the interface by the four buttons at the top left:
|
||||
|
||||

|
||||
|
||||
The modality of existing OSM editors runs the gamut from Potlatch 2, which is
|
||||
almost entirely modeless, to JOSM, which sports half a dozen modes out of the
|
||||
box and has many more provided by plugins. iD seeks a middle ground: too few
|
||||
modes can leave new users unsure where to start, while too many can be
|
||||
overwhelming.
|
||||
|
||||
iD's user-facing modes consist of a base "Browse" mode, in which you can move
|
||||
around the map and select and edit entities, and three geometrically-oriented
|
||||
drawing modes: Point, Line, and Area. In the code, these are broken down a
|
||||
little bit more. There are separate modes for when an entity is selected
|
||||
(`iD.modes.Select`) versus when nothing is selected (`iD.modes.Browse`), and
|
||||
each of the geometric modes is split into one mode for starting to draw an
|
||||
object and one mode for continuing an existing object (with the exception of
|
||||
`iD.modes.AddPoint`, which is a single-step operation for obvious reasons).
|
||||
|
||||
The code interface for each mode consists of a pair of methods: `enter` and
|
||||
`exit`. In the `enter` method, a mode sets up all the behavior that should be
|
||||
present when that mode is active. This typically means binding callbacks to
|
||||
DOM events that will be triggered on map elements, installing keybindings, and
|
||||
showing certain parts of the interface like the inspector in `Select` mode.
|
||||
The `exit` mode does the opposite, removing the behavior installed by the
|
||||
`enter` method. Together the two methods ensure that modes are self-contained
|
||||
and exclusive: each mode knows exactly the behavior that is specific to that
|
||||
mode, and exactly one mode's behavior is active at any time.
|
||||
|
||||
## Behavior
|
||||
|
||||
Certain behaviors are common to more than one mode. For example, iD indicates
|
||||
interactive map elements by drawing a halo around them when you hover over
|
||||
them, and this behavior is common to both the browse and draw modes. Instead
|
||||
of duplicating the code to implement this behavior in all these modes, we
|
||||
extract it to `iD.behavior.Hover`.
|
||||
|
||||
_Behaviors_ take their inspiration from [d3's
|
||||
behaviors](https://github.com/mbostock/d3/wiki/Behaviors). Like d3's `zoom`
|
||||
and `drag`, each iD behavior is a function that takes as input a d3 selection
|
||||
(assumed to consist of a single element) and installs the DOM event bindings
|
||||
necessary to implement the behavior. The `Hover` behavior, for example,
|
||||
installs bindings for the `mouseover` and `mouseout` events that add and
|
||||
remove a `hover` class from map elements.
|
||||
|
||||
Because certain behaviors are appropriate to some but not all modes, we need
|
||||
the ability to remove a behavior when entering a mode where it is not
|
||||
appropriate. (This is functionality [not yet
|
||||
provided](https://github.com/mbostock/d3/issues/894) by d3's own behaviors.)
|
||||
Each behavior implements an `off` function that "uninstalls" the behavior.
|
||||
This is very similar to the `exit` method of a mode, and in fact many modes do
|
||||
little else but uninstall behaviors in their `exit` methods.
|
||||
|
||||
## Operations
|
||||
|
||||
_Operations_ wrap actions, providing their user-interface: tooltips, key
|
||||
bindings, and the logic that determines whether an action can be validly
|
||||
performed given the current map state and selection. Each operation is
|
||||
constructed with the list of IDs which are currently selected and a `context`
|
||||
object which provides access to the history and other important parts of iD's
|
||||
internal state. After being constructed, an operation can be queried as to
|
||||
whether or not it should be made available (i.e., show up in the context menu)
|
||||
and if so, if it should be enabled.
|
||||
|
||||

|
||||
|
||||
We make a distinction between availability and enabled state for the sake of
|
||||
learnability: most operations are available so long as an entity of the
|
||||
appropriate type is selected. Even if it remains disabled for other reasons
|
||||
(e.g. because you can't split a way on its start or end vertex), a new user
|
||||
can still learn that "this is something I can do to this type of thing", and a
|
||||
tooltip can provide an explanation of what that operation does and the
|
||||
conditions under which it is enabled.
|
||||
|
||||
To execute an operation, call it as a function, with no arguments. The typical
|
||||
operation will perform the appropriate action, creating a new undo state in
|
||||
the history, and then enter the appropriate mode. For example,
|
||||
`iD.operations.Split` performs `iD.actions.Split`, then enters
|
||||
`iD.modes.Select` with the resulting ways selected.
|
||||
|
||||
## Rendering and other UI
|
||||
@@ -0,0 +1,101 @@
|
||||
# Contributing to iD
|
||||
|
||||
Thinking of contributing to iD? High five! Here are some basics for our habits
|
||||
so that you can write code that fits in perfectly.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
We'd love to hear what you think about iD, about any specific problems or
|
||||
concerns you have. Here's a quick list of things to consider:
|
||||
|
||||
Please [search for your issue before filing it: many bugs and improvements have already been reported](https://github.com/systemed/iD/issues/search?q=)
|
||||
|
||||
To report a bug:
|
||||
|
||||
* Write specifically what browser (type and version, like Firefox 22), OS, and browser extensions you have installed
|
||||
* Write steps to replicate the error: when did it happen? What did you expect to happen? What happened instead?
|
||||
* Please keep bug reports professional and straightforward: trust us, we share your dismay at software breaking.
|
||||
* If you can, [enable web developer extensions](http://macwright.org/enable-web-developer-extensions/) and report the
|
||||
Javascript error message.
|
||||
|
||||
When in doubt, be over-descriptive of the bug and how you discovered it.
|
||||
|
||||
To request a feature:
|
||||
|
||||
* If the feature is available in some other software (like Potlatch), link to that software and the implementation.
|
||||
We care about prior art.
|
||||
* Understand that iD is meant to be a simple editor and doesn't aim to be
|
||||
as complete or complicated as JOSM or similar.
|
||||
|
||||
## Javascript
|
||||
|
||||
We use the [Airbnb style for Javascript](https://github.com/airbnb/javascript) with
|
||||
only one difference:
|
||||
|
||||
**4 space soft tabs always for Javascript, not 2.**
|
||||
|
||||
No aligned `=`, no aligned arguments, spaces are either indents or the 1
|
||||
space between expressions. No hard tabs, ever.
|
||||
|
||||
Javascript code should pass through [JSHint](http://www.jshint.com/) with no
|
||||
warnings.
|
||||
|
||||
## HTML
|
||||
|
||||
There isn't much HTML in iD, but what there is is similar to JS: 4 spaces
|
||||
always, indented by the level of the tree:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<div></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## CSS
|
||||
|
||||
Just like HTML and Javascript, 4 space soft tabs always.
|
||||
|
||||
```css
|
||||
.radial-menu-tooltip {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
```
|
||||
|
||||
We write vanilla CSS with no preprocessing step. Since iD targets modern browsers,
|
||||
feel free to use newer features wisely.
|
||||
|
||||
## Tests
|
||||
|
||||
Test your code and make sure it passes. Our testing harness requires [node.js](http://nodejs.org/)
|
||||
and a few modules:
|
||||
|
||||
1. [Install node.js](http://nodejs.org/) - 'Install' will download a package for your OS
|
||||
2. Go to the directory where you have checked out `iD`
|
||||
3. Run `npm install`
|
||||
4. Run `npm test` to see whether your tests pass or fail.
|
||||
|
||||
## Licensing
|
||||
|
||||
iD is under the [WTFPL](http://www.wtfpl.net/). Some of the libraries it uses
|
||||
are under different licenses. If you're contributing to iD, you're contributing
|
||||
WTFPL code.
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
Let's say that you've thought of a great improvement to iD - a change that
|
||||
turns everything red (please do not do this, we like colors other than red).
|
||||
|
||||
In your local copy, make a branch for this change:
|
||||
|
||||
git checkout -b make-red
|
||||
|
||||
Make your changes to source files. By source files we mean the files in `js/`.
|
||||
the `iD.js` and `iD.min.js` files in this project are autogenerated - don't edit
|
||||
them.
|
||||
|
||||
So let's say you've changed `js/ui/confirm.js`.
|
||||
|
||||
1. Run `jshint js/id` to make sure your code is clean
|
||||
2. Run tests with `npm test`
|
||||
3. Commit your changes with an informative commit message
|
||||
4. [Submit a pull request](https://help.github.com/articles/using-pull-requests) to the `systemed/iD` project.
|
||||
@@ -30,6 +30,9 @@ all: \
|
||||
js/id/connection.js \
|
||||
js/id/oauth.js \
|
||||
js/id/services/*.js \
|
||||
data/data.js \
|
||||
data/imagery.js \
|
||||
data/deprecated.js \
|
||||
js/id/util.js \
|
||||
js/id/geo.js \
|
||||
js/id/geo/*.js \
|
||||
@@ -41,8 +44,7 @@ all: \
|
||||
js/id/modes/*.js \
|
||||
js/id/operations.js \
|
||||
js/id/operations/*.js \
|
||||
js/id/controller.js \
|
||||
js/id/graph/*.js \
|
||||
js/id/core/*.js \
|
||||
js/id/renderer/*.js \
|
||||
js/id/svg.js \
|
||||
js/id/svg/*.js \
|
||||
@@ -50,8 +52,8 @@ all: \
|
||||
js/id/ui/*.js \
|
||||
js/id/validate.js \
|
||||
js/id/end.js \
|
||||
locale/locale.js \
|
||||
locale/en.js
|
||||
js/lib/locale.js \
|
||||
locale/*.js
|
||||
|
||||
iD.js: Makefile
|
||||
@rm -f $@
|
||||
|
||||
@@ -2,31 +2,30 @@
|
||||
|
||||
[](https://travis-ci.org/systemed/iD)
|
||||
|
||||
[](http://geowiki.com/iD/)
|
||||
|
||||
[Try the online demo of the most recent code.](http://geowiki.com/iD/) and
|
||||
[open issues for bugs and ideas!](https://github.com/systemed/iD/issues)
|
||||
[](http://ideditor.com/)
|
||||
|
||||
## Basics
|
||||
|
||||
* iD is a JavaScript [OpenStreetMap](http://www.openstreetmap.org/) editor.
|
||||
* It's intentionally simple. It lets you do the most basic tasks while
|
||||
not breaking other people's data.
|
||||
* We support modern browsers. Data is rendered with [d3](http://d3js.org/).
|
||||
* It supports modern browsers. Data is rendered with [d3](http://d3js.org/).
|
||||
|
||||
## Participate!
|
||||
|
||||
* [Read NOTES.md, our ongoing dev journal](https://github.com/systemed/iD/blob/master/NOTES.md)
|
||||
* Fork this project. We eagerly accept pull requests.
|
||||
* See [open issues in the issue tracker if you're looking for something to do](https://github.com/systemed/iD/issues?state=open)
|
||||
* [Try out the latest stable release](http://geowiki.com/iD/)
|
||||
* [Read up on Contributing and the code style of iD](CONTRIBUTING.md)
|
||||
* See [open issues in the issue tracker](https://github.com/systemed/iD/issues?state=open) if you're looking for something to do
|
||||
|
||||
To run the code locally, just fork this project and run it from a local webserver.
|
||||
With a Mac, you can enable Web Sharing and drop this in your website directory.
|
||||
## Installation
|
||||
|
||||
If you have Python handy, just `cd` into `iD` and run
|
||||
To run the current development version, fork this project and serve it locally.
|
||||
If you have Python handy, just `cd` into the project root directory and run
|
||||
|
||||
python -m SimpleHTTPServer
|
||||
|
||||
Or, with a Mac, you can enable Web Sharing and clone iD into your website directory.
|
||||
|
||||
Come on in, the water's lovely. More help? Ping RichardF, tmcw, or jfire on IRC
|
||||
(`irc.oftc.net`, in `#osm-dev` or `#osm`), on the OSM mailing lists or at
|
||||
richard@systemeD.net.
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<script src='js/id/ui/commit.js'></script>
|
||||
<script src='js/id/ui/success.js'></script>
|
||||
<script src='js/id/ui/loading.js'></script>
|
||||
<script src='js/id/ui/userpanel.js'></script>
|
||||
<script src='js/id/ui/account.js'></script>
|
||||
<script src='js/id/ui/layerswitcher.js'></script>
|
||||
<script src='js/id/ui/contributors.js'></script>
|
||||
<script src='js/id/ui/geocoder.js'></script>
|
||||
@@ -79,18 +79,17 @@
|
||||
<script src='js/id/actions/change_tags.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/disconnect.js'></script>
|
||||
<script src='js/id/actions/move_node.js'></script>
|
||||
<script src='js/id/actions/move_way.js'></script>
|
||||
<script src='js/id/actions/circularize.js'></script>
|
||||
<script src='js/id/actions/noop.js'></script>
|
||||
<script src='js/id/actions/reverse_way.js'></script>
|
||||
<script src='js/id/actions/split_way.js'></script>
|
||||
<script src='js/id/actions/unjoin_node.js'></script>
|
||||
<script src='js/id/actions/reverse.js'></script>
|
||||
<script src='js/id/actions/split.js'></script>
|
||||
|
||||
<script src='js/id/behavior.js'></script>
|
||||
<script src='js/id/behavior/add_way.js'></script>
|
||||
<script src='js/id/behavior/drag.js'></script>
|
||||
<script src='js/id/behavior/drag_midpoint.js'></script>
|
||||
<script src='js/id/behavior/drag_node.js'></script>
|
||||
<script src='js/id/behavior/draw.js'></script>
|
||||
<script src='js/id/behavior/draw_way.js'></script>
|
||||
@@ -115,12 +114,12 @@
|
||||
<script src='js/id/operations/split.js'></script>
|
||||
<script src='js/id/operations/unjoin.js'></script>
|
||||
|
||||
<script src='js/id/graph/entity.js'></script>
|
||||
<script src='js/id/graph/graph.js'></script>
|
||||
<script src='js/id/graph/history.js'></script>
|
||||
<script src='js/id/graph/node.js'></script>
|
||||
<script src='js/id/graph/relation.js'></script>
|
||||
<script src='js/id/graph/way.js'></script>
|
||||
<script src='js/id/core/entity.js'></script>
|
||||
<script src='js/id/core/graph.js'></script>
|
||||
<script src='js/id/core/history.js'></script>
|
||||
<script src='js/id/core/node.js'></script>
|
||||
<script src='js/id/core/relation.js'></script>
|
||||
<script src='js/id/core/way.js'></script>
|
||||
|
||||
<script src='js/id/controller.js'></script>
|
||||
<script src='js/id/validate.js'></script>
|
||||
|
||||
@@ -19,10 +19,11 @@ body {
|
||||
}
|
||||
|
||||
.limiter {
|
||||
position: relative;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
div, textarea, input, span, ul, li, ol, a, button {
|
||||
div, textarea, input, form, span, ul, li, ol, a, button {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
@@ -93,7 +94,6 @@ a:hover {
|
||||
color:#597be7;
|
||||
}
|
||||
|
||||
|
||||
textarea,
|
||||
input[type=text],
|
||||
input[type=url],
|
||||
@@ -124,7 +124,7 @@ input[type=text]:focus {
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
padding:4px 10px;
|
||||
padding:5px 10px;
|
||||
height:30px;
|
||||
resize: none;
|
||||
}
|
||||
@@ -185,10 +185,12 @@ ul li { list-style: none;}
|
||||
ul.toggle-list li a {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
border-top: 1px solid white;
|
||||
padding: 5px 10px;
|
||||
display:block;
|
||||
border-top: 1px solid rgba(0, 0, 0, .5);
|
||||
border-top: 1px solid #ccc;
|
||||
white-space:nowrap;
|
||||
text-overflow:ellipsis;
|
||||
overflow:hidden;
|
||||
}
|
||||
ul.toggle-list li a:hover { background-color: #ececec;}
|
||||
|
||||
@@ -225,7 +227,7 @@ ul.link-list li:last-child {
|
||||
|
||||
.fillD {
|
||||
background:rgba(0,0,0,.8);
|
||||
color: #a9a9a9;
|
||||
color: #6C6C6C;
|
||||
}
|
||||
|
||||
|
||||
@@ -268,16 +270,17 @@ button:hover {
|
||||
background-color: #ececec;
|
||||
}
|
||||
|
||||
button.col3:hover {
|
||||
background: #bde5aa;
|
||||
}
|
||||
|
||||
button.active {
|
||||
cursor:url(../img/cursor-pointing.png) 6 1, auto;
|
||||
}
|
||||
|
||||
button.active:not([disabled]) {
|
||||
background: #6bc641;
|
||||
button.disabled {
|
||||
background: #6c6c6c;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
button.active:not([disabled]):not(.disabled) {
|
||||
background: #7092ff;
|
||||
}
|
||||
|
||||
button.minor {
|
||||
@@ -322,7 +325,7 @@ button.centered {
|
||||
border-radius:0 4px 4px 0;
|
||||
}
|
||||
|
||||
button.Browse .label { display: none;}
|
||||
button.browse .label { display: none;}
|
||||
|
||||
button.action {
|
||||
background: #7092ff;
|
||||
@@ -349,8 +352,7 @@ button.save .count {
|
||||
button.save.has-count .count {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
top: 5px;
|
||||
background: rgba(255, 255, 255, .5);
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
@@ -453,8 +455,8 @@ button[disabled] .icon.browse { background-position: 0px -40px;}
|
||||
button[disabled] .icon.add-point { background-position: -20px -40px;}
|
||||
button[disabled] .icon.add-line { background-position: -40px -40px;}
|
||||
button[disabled] .icon.add-area { background-position: -60px -40px;}
|
||||
button[disabled] .icon.undo { background-position: -80px -40px;}
|
||||
button[disabled] .icon.redo { background-position: -100px -40px;}
|
||||
button.disabled .icon.undo { background-position: -80px -40px;}
|
||||
button.disabled .icon.redo { background-position: -100px -40px;}
|
||||
button[disabled] .apply.icon { background-position: -120px -40px;}
|
||||
button[disabled] .save.icon { background-position: -140px -40px;}
|
||||
button[disabled] .close.icon { background-position: -160px -40px;}
|
||||
@@ -479,7 +481,7 @@ button[disabled] .icon.nearby { background-position: -340px -40px;}
|
||||
.icon-operation-circularize { background-position: -20px -140px;}
|
||||
.icon-operation-straighten { background-position: -40px -140px;}
|
||||
.icon-operation-split { background-position: -60px -140px;}
|
||||
.icon-operation-unjoin { background-position: -80px -140px;}
|
||||
.icon-operation-disconnect { background-position: -80px -140px;}
|
||||
.icon-operation-reverse { background-position: -100px -140px;}
|
||||
.icon-operation-move { background-position: -120px -140px;}
|
||||
.icon-operation-merge { background-position: -140px -140px;}
|
||||
@@ -643,14 +645,15 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
|
||||
}
|
||||
|
||||
.map-control > button.active:hover {
|
||||
background: #6bc641;
|
||||
background: #7092ff;
|
||||
}
|
||||
|
||||
.map-overlay {
|
||||
width: 150px;
|
||||
position:absolute;
|
||||
left:40px;
|
||||
top:0;
|
||||
right: 75%;
|
||||
max-width: 260px;
|
||||
min-width: 210px;
|
||||
position: fixed;
|
||||
left: 40px;
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -685,10 +688,8 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
|
||||
font-size:10px;
|
||||
padding:0 5px 3px 5px;
|
||||
background: white;
|
||||
border:0;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
.layerswitcher-control .adjustments button:hover {
|
||||
@@ -725,9 +726,9 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
|
||||
.layerswitcher-control .nudge {
|
||||
text-indent: -9999px;
|
||||
overflow: hidden;
|
||||
width:20px;
|
||||
width:16.6666%;
|
||||
border-radius: 0;
|
||||
margin-right:1px;
|
||||
border-right: 1px solid rgba(0, 0, 0, .5);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -770,7 +771,7 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
|
||||
}
|
||||
|
||||
.layerswitcher-control .reset {
|
||||
width: 45px;
|
||||
width: 33.3333%;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
@@ -804,8 +805,8 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
|
||||
|
||||
.layerswitcher-control li:hover .select-box,
|
||||
.layerswitcher-control li.selected .select-box {
|
||||
border: 2px solid #6bc641;
|
||||
background: rgba(107, 198, 65, .5);
|
||||
border: 2px solid #7092ff;
|
||||
background: rgba(89, 123, 231, .5);
|
||||
opacity: .5;
|
||||
}
|
||||
.layerswitcher-control li.selected:hover .select-box,
|
||||
@@ -822,14 +823,29 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
|
||||
|
||||
/* Geocoder */
|
||||
|
||||
.geocode-control {
|
||||
.geocode-control, .geocode-control form {
|
||||
top:150px;
|
||||
}
|
||||
|
||||
.geocode-control form {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.geocode-control input {
|
||||
width: 140px;
|
||||
border: 1px solid #ccc;
|
||||
margin: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.geocode-control div.content {
|
||||
z-index: 100;
|
||||
top: 190px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.geocode-control div.content span {
|
||||
display: inline-block;
|
||||
border-bottom: 1px solid #333;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* Geolocator */
|
||||
@@ -876,6 +892,10 @@ img.tile {
|
||||
-o-transform-origin:0 0;
|
||||
}
|
||||
|
||||
#surface {
|
||||
position: static;
|
||||
}
|
||||
|
||||
#tile-g {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -904,17 +924,17 @@ img.tile {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
#user-list a:not(:last-child):after {
|
||||
.user-list a:not(:last-child):after {
|
||||
content: ', ';
|
||||
}
|
||||
|
||||
/* Account Information */
|
||||
|
||||
.user-container {
|
||||
.account {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.user-container .logout {
|
||||
.account .logout {
|
||||
margin-left:10px;
|
||||
border-left: 1px solid white;
|
||||
padding-left: 10px;
|
||||
@@ -1054,7 +1074,6 @@ div.typeahead a:first-child {
|
||||
|
||||
.modal-splash {
|
||||
width: 33.3333%;
|
||||
left: 33.3333%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@@ -1120,11 +1139,6 @@ div.typeahead a:first-child {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-section .buttons {
|
||||
padding-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal-section img.wiki-image {
|
||||
max-width: 100%;
|
||||
max-height: 300px;
|
||||
@@ -1188,7 +1202,7 @@ a.success-action {
|
||||
}
|
||||
|
||||
.notice .zoom-to:hover {
|
||||
background: #bde5aa;
|
||||
background: #d8e1ff;
|
||||
}
|
||||
|
||||
.notice .zoom-to .icon {
|
||||
@@ -1307,7 +1321,6 @@ a.success-action {
|
||||
color: #222;
|
||||
font-size: 10px;
|
||||
padding: 0px 7px;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
@@ -1383,6 +1396,13 @@ a.success-action {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.lasso-box {
|
||||
fill-opacity:0.2;
|
||||
fill: #bde5aa;
|
||||
stroke: #000;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
/* Media Queries
|
||||
------------------------------------------------------- */
|
||||
|
||||
@@ -1428,7 +1448,7 @@ a.success-action {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.combobox {
|
||||
div.combobox {
|
||||
width:155px;
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/* tiles */
|
||||
img.tile {
|
||||
position:absolute;
|
||||
transform-origin:0 0;
|
||||
-ms-transform-origin:0 0;
|
||||
-webkit-transform-origin:0 0;
|
||||
-moz-transform-origin:0 0;
|
||||
-o-transform-origin:0 0;
|
||||
-webkit-user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
-webkit-transition: opacity 200ms linear;
|
||||
transition: opacity 200ms linear;
|
||||
-moz-transition: opacity 200ms linear;
|
||||
}
|
||||
|
||||
img.tile-loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* base styles */
|
||||
path {
|
||||
fill: none;
|
||||
@@ -9,6 +32,10 @@ g.point circle {
|
||||
fill:#fff;
|
||||
}
|
||||
|
||||
g.point image {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
g.point .shadow {
|
||||
fill: none;
|
||||
pointer-events: all;
|
||||
@@ -17,21 +44,27 @@ g.point .shadow {
|
||||
-moz-transition: fill 100ms linear;
|
||||
}
|
||||
.behavior-hover g.point.hover:not(.selected) .shadow {
|
||||
fill: #E96666;
|
||||
fill-opacity: 0.3;
|
||||
fill: #f6634f;
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
g.point.selected .shadow {
|
||||
fill: #E96666;
|
||||
fill: #f6634f;
|
||||
fill-opacity: 0.7;
|
||||
}
|
||||
|
||||
g.point.active, g.point.active * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* vertices */
|
||||
|
||||
g.vertex .fill {
|
||||
fill:white;
|
||||
}
|
||||
|
||||
g.vertex .stroke {
|
||||
stroke:#333;
|
||||
stroke:black;
|
||||
stroke-opacity: .5;
|
||||
stroke-width:2;
|
||||
fill:white;
|
||||
}
|
||||
@@ -101,28 +134,40 @@ g.vertex.shared .fill {
|
||||
g.vertex .shadow {
|
||||
fill: none;
|
||||
pointer-events: all;
|
||||
stroke-width: 10;
|
||||
stroke-width: 20;
|
||||
-webkit-transition: -webkit-transform 100ms linear;
|
||||
transition: transform 100ms linear;
|
||||
-moz-transition: fill 100ms linear;
|
||||
}
|
||||
.behavior-hover g.vertex.hover:not(.selected) .shadow {
|
||||
fill: #E96666;
|
||||
fill: #f6634f;
|
||||
fill-opacity: 0.3;
|
||||
}
|
||||
}
|
||||
g.vertex.selected .shadow {
|
||||
fill: #E96666;
|
||||
fill-opacity: 0.7;
|
||||
fill: #f6634f;
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
|
||||
/* midpoints */
|
||||
|
||||
.mode-draw-area g.midpoint,
|
||||
.mode-draw-line g.midpoint,
|
||||
.mode-add-area g.midpoint,
|
||||
.mode-add-line g.midpoint,
|
||||
.mode-add-point g.midpoint,
|
||||
.behavior-drag-node g.midpoint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
g.midpoint .fill {
|
||||
fill:#aaa;
|
||||
fill:#ddd;
|
||||
stroke:black;
|
||||
stroke-opacity: .5;
|
||||
opacity: .5;
|
||||
}
|
||||
.behavior-hover g.midpoint .fill.hover:not(.selected) {
|
||||
fill:#fff;
|
||||
stroke:#000;
|
||||
fill:white;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
g.midpoint .shadow {
|
||||
@@ -134,7 +179,7 @@ g.midpoint .shadow {
|
||||
-moz-transition: fill 100ms linear;
|
||||
}
|
||||
.behavior-hover g.midpoint .shadow.hover:not(.selected) {
|
||||
fill:#E96666;
|
||||
fill:#f6634f;
|
||||
fill-opacity: 0.3;
|
||||
}
|
||||
|
||||
@@ -146,13 +191,8 @@ path.line {
|
||||
}
|
||||
|
||||
path.stroke {
|
||||
stroke: #222;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
path.stroke,
|
||||
path.casing {
|
||||
shape-rendering: optimizeSpeed;
|
||||
stroke: black;
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
path.shadow {
|
||||
@@ -162,105 +202,82 @@ path.shadow {
|
||||
}
|
||||
|
||||
.behavior-hover path.shadow.hover:not(.selected) {
|
||||
stroke: #E96666;
|
||||
stroke: #f6634f;
|
||||
stroke-opacity: 0.3;
|
||||
}
|
||||
|
||||
path.shadow.selected {
|
||||
stroke: #E96666;
|
||||
stroke: #f6634f;
|
||||
stroke-opacity: 0.7;
|
||||
}
|
||||
|
||||
path.area.stroke,
|
||||
path.multipolygon {
|
||||
path.line.member-type-multipolygon.stroke {
|
||||
stroke-width:2;
|
||||
stroke:#fff;
|
||||
}
|
||||
path.area.fill,
|
||||
path.multipolygon {
|
||||
fill:#fff;
|
||||
fill-opacity:0.3;
|
||||
}
|
||||
|
||||
path.multipolygon {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
path.area.fill.member-type-multipolygon {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
path.area.stroke.selected {
|
||||
path.area.stroke.selected,
|
||||
path.line.member-type-multipolygon.stroke.selected {
|
||||
stroke-width:4 !important;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-natural,
|
||||
path.multipolygon.tag-natural {
|
||||
stroke: #ADD6A5;
|
||||
path.area.stroke {
|
||||
stroke:#fff;
|
||||
}
|
||||
path.area.fill {
|
||||
fill:#fff;
|
||||
fill-opacity:0.3;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
path.stroke.tag-natural {
|
||||
stroke: #b6e199;
|
||||
stroke-width:1;
|
||||
}
|
||||
path.area.fill.tag-natural,
|
||||
path.multipolygon.tag-natural {
|
||||
fill: #ADD6A5;
|
||||
path.fill.tag-natural {
|
||||
fill: #b6e199;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-natural-water,
|
||||
path.multipolygon.tag-natural-water {
|
||||
stroke: #6382FF;
|
||||
path.stroke.tag-natural-water {
|
||||
stroke: #77d3de;
|
||||
}
|
||||
path.area.fill.tag-natural-water,
|
||||
path.multipolygon.tag-natural-water {
|
||||
fill: #ADBEFF;
|
||||
path.fill.tag-natural-water {
|
||||
fill: #77d3de;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-building,
|
||||
path.multipolygon.tag-building {
|
||||
stroke: #9E176A;
|
||||
path.stroke.tag-building {
|
||||
stroke: #e06e5f;
|
||||
stroke-width: 1;
|
||||
}
|
||||
path.area.fill.tag-building,
|
||||
path.multipolygon.tag-building {
|
||||
fill: #ff6ec7;
|
||||
path.fill.tag-building {
|
||||
fill: #e06e5f;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-landuse,
|
||||
path.area.stroke.tag-natural-wood,
|
||||
path.area.stroke.tag-natural-tree,
|
||||
path.area.stroke.tag-natural-grassland,
|
||||
path.area.stroke.tag-leisure-park,
|
||||
path.multipolygon.tag-landuse,
|
||||
path.multipolygon.tag-natural-wood,
|
||||
path.multipolygon.tag-natural-tree,
|
||||
path.multipolygon.tag-natural-grassland,
|
||||
path.multipolygon.tag-leisure-park {
|
||||
stroke: #006B34;
|
||||
path.stroke.tag-landuse,
|
||||
path.stroke.tag-natural-wood,
|
||||
path.stroke.tag-natural-tree,
|
||||
path.stroke.tag-natural-grassland,
|
||||
path.stroke.tag-leisure-park {
|
||||
stroke: #8cd05f;
|
||||
stroke-width: 1;
|
||||
}
|
||||
path.area.fill.tag-landuse,
|
||||
path.area.fill.tag-natural-wood,
|
||||
path.area.fill.tag-natural-tree,
|
||||
path.area.fill.tag-natural-grassland,
|
||||
path.area.fill.tag-leisure-park,
|
||||
path.multipolygon.tag-landuse,
|
||||
path.multipolygon.tag-natural-wood,
|
||||
path.multipolygon.tag-natural-tree,
|
||||
path.multipolygon.tag-natural-grassland,
|
||||
path.multipolygon.tag-leisure-park {
|
||||
fill: #189E59;
|
||||
path.fill.tag-landuse,
|
||||
path.fill.tag-natural-wood,
|
||||
path.fill.tag-natural-tree,
|
||||
path.fill.tag-natural-grassland,
|
||||
path.fill.tag-leisure-park {
|
||||
fill: #8cd05f;
|
||||
fill-opacity: 0.2;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-amenity-parking,
|
||||
path.multipolygon.tag-amenity-parking {
|
||||
stroke: #beb267;
|
||||
path.stroke.tag-amenity-parking {
|
||||
stroke: #aaa;
|
||||
stroke-width: 1;
|
||||
}
|
||||
path.area.fill.tag-amenity-parking,
|
||||
path.multipolygon.tag-amenity-parking {
|
||||
fill: #edecc0;
|
||||
path.fill.tag-amenity-parking {
|
||||
fill: #aaa;
|
||||
}
|
||||
|
||||
path.multipolygon.tag-boundary {
|
||||
path.fill.tag-boundary {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
@@ -291,56 +308,57 @@ svg[data-zoom="16"] path.stroke.tag-highway {
|
||||
path.stroke.tag-highway-motorway,
|
||||
path.stroke.tag-highway-motorway_link,
|
||||
path.stroke.tag-construction-motorway {
|
||||
stroke:#809bc0;
|
||||
stroke:#58a9ed;
|
||||
}
|
||||
|
||||
path.casing.tag-highway-motorway,
|
||||
path.casing.tag-highway-motorway_link,
|
||||
path.casing.tag-construction-motorway {
|
||||
stroke:#506077;
|
||||
stroke:#2c5476;
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-trunk,
|
||||
path.stroke.tag-highway-trunk_link,
|
||||
path.stroke.tag-construction-trunk {
|
||||
stroke:#97d397;
|
||||
stroke:#8cd05f;
|
||||
}
|
||||
path.casing.tag-highway-trunk,
|
||||
path.casing.tag-highway-trunk_link,
|
||||
path.casing.tag-construction-trunk {
|
||||
stroke:#477147;
|
||||
stroke:#46682f;
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-primary,
|
||||
path.stroke.tag-highway-primary_link,
|
||||
path.stroke.tag-construction-primary {
|
||||
stroke:#ec989a;
|
||||
stroke:#e06d5f;
|
||||
}
|
||||
path.casing.tag-highway-primary,
|
||||
path.casing.tag-highway-primary_link,
|
||||
path.casing.tag-construction-primary {
|
||||
stroke:#8d4346;
|
||||
stroke:#70372f;
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-secondary,
|
||||
path.stroke.tag-highway-secondary_link,
|
||||
path.stroke.tag-construction-secondary {
|
||||
stroke:#fecc8b;
|
||||
stroke:#eab056;
|
||||
}
|
||||
path.casing.tag-highway-secondary,
|
||||
path.casing.tag-highway-secondary_link,
|
||||
path.casing.tag-construction-secondary {
|
||||
stroke:#a37b48;
|
||||
stroke:#75582b;
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-tertiary,
|
||||
path.stroke.tag-highway-tertiary_link,
|
||||
path.stroke.tag-construction-tertiary {
|
||||
stroke:#ffffb3;
|
||||
stroke:#ffff7e;
|
||||
}
|
||||
path.casing.tag-highway-tertiary,
|
||||
path.casing.tag-highway-tertiary_link,
|
||||
path.casing.tag-construction-tertiary {
|
||||
stroke:#bbb;
|
||||
stroke:#7f7f3f;
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-unclassified,
|
||||
@@ -377,7 +395,7 @@ path.stroke.tag-highway-pedestrian {
|
||||
shapeRendering: auto;
|
||||
}
|
||||
path.casing.tag-highway-pedestrian {
|
||||
stroke:#84C382;
|
||||
stroke:#8cd05f;
|
||||
stroke-width:6 !important;
|
||||
}
|
||||
|
||||
@@ -450,17 +468,17 @@ svg[data-zoom="16"] path.casing.tag-highway-bridleway {
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-footway {
|
||||
stroke: #996600;
|
||||
stroke: #ae8681;
|
||||
}
|
||||
path.stroke.tag-highway-cycleway {
|
||||
stroke: #69f;
|
||||
stroke: #58a9ed;
|
||||
}
|
||||
path.stroke.tag-highway-bridleway {
|
||||
stroke: green;
|
||||
stroke: #e06d5f;
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-steps {
|
||||
stroke: #ff6257;
|
||||
stroke: #81d25c;
|
||||
stroke-width: 4;
|
||||
stroke-linecap: butt;
|
||||
stroke-dasharray: 3, 3;
|
||||
@@ -472,7 +490,7 @@ path.casing.tag-highway-steps {
|
||||
|
||||
path.casing.tag-bridge-yes {
|
||||
stroke-width: 14;
|
||||
stroke: #000;
|
||||
stroke: #333;
|
||||
}
|
||||
|
||||
path.stroke.tag-highway-construction,
|
||||
@@ -516,12 +534,16 @@ path.casing.tag-railway-subway {
|
||||
|
||||
/* waterways */
|
||||
|
||||
path.fill.tag-waterway {
|
||||
fill: #77d3de;
|
||||
}
|
||||
|
||||
path.stroke.tag-waterway {
|
||||
stroke: #10539a;
|
||||
stroke: #77d3de;
|
||||
stroke-width: 2;
|
||||
}
|
||||
path.casing.tag-waterway {
|
||||
stroke: #6AA2FF;
|
||||
stroke: #77d3de;
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
@@ -540,11 +562,11 @@ svg[data-zoom="16"] path.casing.tag-waterway-river {
|
||||
}
|
||||
|
||||
path.stroke.tag-waterway-ditch {
|
||||
stroke: #10539a;
|
||||
stroke: #6591ff;
|
||||
stroke-width: 1;
|
||||
}
|
||||
path.casing.tag-waterway-ditch {
|
||||
stroke: #999692;
|
||||
stroke: #6591ff;
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
@@ -573,17 +595,22 @@ path.casing.tag-boundary {
|
||||
|
||||
path.casing.tag-boundary-protected_area,
|
||||
path.casing.tag-boundary-national_park {
|
||||
stroke: #4D9849;
|
||||
stroke: #b0e298;
|
||||
}
|
||||
|
||||
|
||||
text {
|
||||
font-size:10px;
|
||||
pointer-events: none;
|
||||
color: #222;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.oneway .textpath {
|
||||
pointer-events: none;
|
||||
font-size: 7px;
|
||||
baseline-shift: 2px;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
text.tag-oneway {
|
||||
@@ -611,7 +638,7 @@ text.pathlabel,
|
||||
text.pointlabel {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
fill: black;
|
||||
fill: #333;
|
||||
text-anchor: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -628,6 +655,12 @@ text.pointlabel {
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
|
||||
/* Opera doesn't support dominant-baseline. See #715 */
|
||||
.opera .pathlabel .textpath {
|
||||
baseline-shift: -33%;
|
||||
dominant-baseline: auto;
|
||||
}
|
||||
|
||||
.pointlabel-halo,
|
||||
.linelabel-halo,
|
||||
.arealabel-halo {
|
||||
@@ -637,7 +670,8 @@ text.pointlabel {
|
||||
|
||||
|
||||
text.point {
|
||||
font-size: 9px;
|
||||
font-size: 10px;
|
||||
baseline-shift: 2px;
|
||||
}
|
||||
|
||||
/* Cursors */
|
||||
@@ -666,9 +700,7 @@ text.point {
|
||||
}
|
||||
|
||||
.mode-select .area,
|
||||
.mode-browse .area,
|
||||
.mode-select .multipolygon,
|
||||
.mode-browse .multipolygon {
|
||||
.mode-browse .area {
|
||||
cursor: url(../img/cursor-select-area.png), pointer;
|
||||
}
|
||||
|
||||
@@ -681,7 +713,6 @@ text.point {
|
||||
.vertex:active,
|
||||
.line:active,
|
||||
.area:active,
|
||||
.multipolygon:active,
|
||||
.midpoint:active,
|
||||
.mode-select .selected {
|
||||
cursor: url(../img/cursor-select-acting.png), pointer;
|
||||
@@ -697,14 +728,16 @@ text.point {
|
||||
.mode-draw-line .behavior-hover .way,
|
||||
.mode-draw-area .behavior-hover .way,
|
||||
.mode-add-line .behavior-hover .way,
|
||||
.mode-add-area .behavior-hover .way {
|
||||
.mode-add-area .behavior-hover .way,
|
||||
.behavior-drag-node.behavior-hover .way {
|
||||
cursor:url(../img/cursor-draw-connect-line.png) 9 9, auto;
|
||||
}
|
||||
|
||||
.mode-draw-line .behavior-hover .vertex,
|
||||
.mode-draw-area .behavior-hover .vertex,
|
||||
.mode-add-line .behavior-hover .vertex,
|
||||
.mode-add-area .behavior-hover .vertex {
|
||||
.mode-add-area .behavior-hover .vertex,
|
||||
.behavior-drag-node.behavior-hover .vertex {
|
||||
cursor:url(../img/cursor-draw-connect-vertex.png) 9 9, auto;
|
||||
}
|
||||
|
||||
@@ -715,16 +748,19 @@ text.point {
|
||||
/* Modes */
|
||||
|
||||
.mode-draw-line .vertex.active,
|
||||
.mode-draw-area .vertex.active {
|
||||
.mode-draw-area .vertex.active,
|
||||
.behavior-drag-node .vertex.active {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mode-draw-line .way.active,
|
||||
.mode-draw-area .way.active {
|
||||
.mode-draw-area .way.active,
|
||||
.behavior-drag-node .active {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Ensure drawing doesn't interact with area fills. */
|
||||
.mode-add-point .area,
|
||||
.mode-draw-line .area,
|
||||
.mode-draw-area .area,
|
||||
.mode-add-line .area,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
iD.data = {};
|
||||
@@ -0,0 +1,112 @@
|
||||
// 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
|
||||
iD.data.deprecated = [
|
||||
{
|
||||
old: { barrier: 'wire_fence' },
|
||||
replace: {
|
||||
barrier: 'fence',
|
||||
fence_type: 'chain'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { barrier: 'wood_fence' },
|
||||
replace: {
|
||||
barrier: 'fence',
|
||||
fence_type: 'wood'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'ford' },
|
||||
replace: {
|
||||
ford: 'yes'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'ford' },
|
||||
replace: {
|
||||
ford: 'yes'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'ford' },
|
||||
replace: {
|
||||
ford: 'yes'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'stile' },
|
||||
replace: {
|
||||
barrier: 'stile'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'incline' },
|
||||
replace: {
|
||||
highway: 'road',
|
||||
incline: 'up'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'incline_steep' },
|
||||
replace: {
|
||||
highway: 'road',
|
||||
incline: 'up'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'unsurfaced' },
|
||||
replace: {
|
||||
highway: 'road',
|
||||
incline: 'unpaved'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { highway: 'unsurfaced' },
|
||||
replace: {
|
||||
highway: 'road',
|
||||
incline: 'unpaved'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { landuse: 'wood' },
|
||||
replace: {
|
||||
highway: 'road',
|
||||
incline: 'unpaved'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { natural: 'marsh' },
|
||||
replace: {
|
||||
natural: 'wetland',
|
||||
wetland: 'marsh'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { shop: 'organic' },
|
||||
replace: {
|
||||
shop: 'supermarket',
|
||||
organic: 'only'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { power_source: '*' },
|
||||
replace: {
|
||||
'generator:source': '$1'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { power_rating: '*' },
|
||||
replace: {
|
||||
'generator:output': '$1'
|
||||
}
|
||||
},
|
||||
{
|
||||
old: { bicycle_parking: 'organic' },
|
||||
replace: {
|
||||
shop: 'supermarket',
|
||||
organic: 'only'
|
||||
}
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,10 @@
|
||||
// entirely discarded tags
|
||||
iD.data.discarded = [
|
||||
'tiger:upload_uuid',
|
||||
'tiger:tlid',
|
||||
'tiger:source',
|
||||
'tiger:separated',
|
||||
'geobase:datasetName',
|
||||
'geobase:uuid',
|
||||
'sub_sea:type'
|
||||
];
|
||||
@@ -0,0 +1,609 @@
|
||||
iD.data.imagery = [
|
||||
{
|
||||
"name": "Bing aerial imagery",
|
||||
"template": "http://ecn.t{t}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z",
|
||||
"description": "Satellite imagery.",
|
||||
"scaleExtent": [
|
||||
0,
|
||||
20
|
||||
],
|
||||
"subdomains": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3"
|
||||
],
|
||||
"default": "yes",
|
||||
"sourcetag": "Bing",
|
||||
"logo": "bing_maps.png",
|
||||
"logo_url": "http://www.bing.com/maps",
|
||||
"terms_url": "http://opengeodata.org/microsoft-imagery-details"
|
||||
},
|
||||
{
|
||||
"name": "MapBox Satellite",
|
||||
"template": "http://{t}.tiles.mapbox.com/v3/openstreetmap.map-4wvf9l0l/{z}/{x}/{y}.png",
|
||||
"description": "Satellite and aerial imagery",
|
||||
"scaleExtent": [
|
||||
0,
|
||||
16
|
||||
],
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"terms_url": "http://mapbox.com/tos/"
|
||||
},
|
||||
{
|
||||
"name": "OpenStreetMap",
|
||||
"template": "http://{t}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
"description": "The default OpenStreetMap layer.",
|
||||
"scaleExtent": [
|
||||
0,
|
||||
18
|
||||
],
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " TIGER 2012 Roads Overlay",
|
||||
"template": "http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-124.81,
|
||||
24.055
|
||||
],
|
||||
[
|
||||
-66.865,
|
||||
49.386
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " TIGER 2012 Roads Overlay",
|
||||
"template": "http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-179.754,
|
||||
50.858
|
||||
],
|
||||
[
|
||||
-129.899,
|
||||
71.463
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " TIGER 2012 Roads Overlay",
|
||||
"template": "http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-174.46,
|
||||
18.702
|
||||
],
|
||||
[
|
||||
-154.516,
|
||||
26.501
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Topographic Maps",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_scanned_topos/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-125.991,
|
||||
24.005
|
||||
],
|
||||
[
|
||||
-65.988,
|
||||
50.009
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Topographic Maps",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_scanned_topos/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-160.579,
|
||||
18.902
|
||||
],
|
||||
[
|
||||
-154.793,
|
||||
22.508
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Topographic Maps",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_scanned_topos/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-178.001,
|
||||
51.255
|
||||
],
|
||||
[
|
||||
-130.004,
|
||||
71.999
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Large Scale Aerial Imagery",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_large_scale/{z}/{x}/{y}.jpg",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-124.819,
|
||||
24.496
|
||||
],
|
||||
[
|
||||
-66.931,
|
||||
49.443
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "British Columbia bc_mosaic",
|
||||
"template": "http://{t}.imagery.paulnorman.ca/tiles/bc_mosaic/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-123.441,
|
||||
48.995
|
||||
],
|
||||
[
|
||||
-121.346,
|
||||
50.426
|
||||
]
|
||||
],
|
||||
"sourcetag": "bc_mosaic",
|
||||
"terms_url": "http://imagery.paulnorman.ca/tiles/about.html"
|
||||
},
|
||||
{
|
||||
"name": "OS OpenData Streetview",
|
||||
"template": "http://os.openstreetmap.org/sv/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-8.72,
|
||||
49.86
|
||||
],
|
||||
[
|
||||
1.84,
|
||||
60.92
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS_OpenData_StreetView"
|
||||
},
|
||||
{
|
||||
"name": "OS OpenData Locator",
|
||||
"template": "http://tiles.itoworld.com/os_locator/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS_OpenData_Locator"
|
||||
},
|
||||
{
|
||||
"name": "OS 1:25k historic (OSM)",
|
||||
"template": "http://ooc.openstreetmap.org/os1/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS 1:25k"
|
||||
},
|
||||
{
|
||||
"name": "OS 1:25k historic (NLS)",
|
||||
"template": "http://geo.nls.uk/mapdata2/os/25000/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS 1:25k",
|
||||
"logo": "icons/logo_nls70-nq8.png",
|
||||
"logo_url": "http://geo.nls.uk/maps/"
|
||||
},
|
||||
{
|
||||
"name": "OS 7th Series historic (OSM)",
|
||||
"template": "http://ooc.openstreetmap.org/os7/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS7"
|
||||
},
|
||||
{
|
||||
"name": "OS 7th Series historic (NLS)",
|
||||
"template": "http://geo.nls.uk/mapdata2/os/seventh/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS7",
|
||||
"logo": "icons/logo_nls70-nq8.png",
|
||||
"logo_url": "http://geo.nls.uk/maps/"
|
||||
},
|
||||
{
|
||||
"name": "OS New Popular Edition historic",
|
||||
"template": "http://ooc.openstreetmap.org/npe/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-5.8,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
55.8
|
||||
]
|
||||
],
|
||||
"sourcetag": "NPE"
|
||||
},
|
||||
{
|
||||
"name": "OS Scottish Popular historic",
|
||||
"template": "http://ooc.openstreetmap.org/npescotland/tiles/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-7.8,
|
||||
54.5
|
||||
],
|
||||
[
|
||||
-1.1,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "NPE"
|
||||
},
|
||||
{
|
||||
"name": "Surrey aerial",
|
||||
"template": "http://gravitystorm.dev.openstreetmap.org/surrey/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-0.856,
|
||||
51.071
|
||||
],
|
||||
[
|
||||
0.062,
|
||||
51.473
|
||||
]
|
||||
],
|
||||
"sourcetag": "Surrey aerial"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - GeoEye Jan 13",
|
||||
"template": "http://gravitystorm.dev.openstreetmap.org/imagery/haiti/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti GeoEye"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - GeoEye Jan 13+",
|
||||
"template": "http://maps.nypl.org/tilecache/1/geoeye/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti GeoEye"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - DigitalGlobe",
|
||||
"template": "http://maps.nypl.org/tilecache/1/dg_crisis/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti DigitalGlobe"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - Street names",
|
||||
"template": "http://hypercube.telascience.org/tiles/1.0.0/haiti-city/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti streetnames"
|
||||
},
|
||||
{
|
||||
"name": "NAIP",
|
||||
"template": "http://cube.telascience.org/tilecache/tilecache.py/NAIP_ALL/{z}/{x}/{y}.png",
|
||||
"description": "National Agriculture Imagery Program",
|
||||
"extent": [
|
||||
[
|
||||
-125.8,
|
||||
24.2
|
||||
],
|
||||
[
|
||||
-62.3,
|
||||
49.5
|
||||
]
|
||||
],
|
||||
"sourcetag": "NAIP"
|
||||
},
|
||||
{
|
||||
"name": "NAIP",
|
||||
"template": "http://cube.telascience.org/tilecache/tilecache.py/NAIP_ALL/{z}/{x}/{y}.png",
|
||||
"description": "National Agriculture Imagery Program",
|
||||
"extent": [
|
||||
[
|
||||
-168.5,
|
||||
55.3
|
||||
],
|
||||
[
|
||||
-140,
|
||||
71.5
|
||||
]
|
||||
],
|
||||
"sourcetag": "NAIP"
|
||||
},
|
||||
{
|
||||
"name": "Ireland - NLS Historic Maps",
|
||||
"template": "http://geo.nls.uk/maps/ireland/gsgs4136/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-10.71,
|
||||
51.32
|
||||
],
|
||||
[
|
||||
-5.37,
|
||||
55.46
|
||||
]
|
||||
],
|
||||
"sourcetag": "NLS Historic Maps",
|
||||
"logo": "icons/logo_nls70-nq8.png",
|
||||
"logo_url": "http://geo.nls.uk/maps/"
|
||||
},
|
||||
{
|
||||
"name": "Denmark - Fugro Aerial Imagery",
|
||||
"template": "http://tile.openstreetmap.dk/fugro2005/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
7.81,
|
||||
54.44
|
||||
],
|
||||
[
|
||||
15.49,
|
||||
57.86
|
||||
]
|
||||
],
|
||||
"sourcetag": "Fugro (2005)"
|
||||
},
|
||||
{
|
||||
"name": "Denmark - Stevns Kommune",
|
||||
"template": "http://tile.openstreetmap.dk/stevns/2009/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
12.09144,
|
||||
55.23403
|
||||
],
|
||||
[
|
||||
12.47712,
|
||||
55.43647
|
||||
]
|
||||
],
|
||||
"sourcetag": "Stevns Kommune (2009)"
|
||||
},
|
||||
{
|
||||
"name": "Austria - geoimage.at",
|
||||
"template": "http://geoimage.openstreetmap.at/4d80de696cd562a63ce463a58a61488d/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
9.36,
|
||||
46.33
|
||||
],
|
||||
[
|
||||
17.28,
|
||||
49.09
|
||||
]
|
||||
],
|
||||
"sourcetag": "geoimage.at"
|
||||
},
|
||||
{
|
||||
"name": "Russia - Kosmosnimki.ru IRS Satellite",
|
||||
"template": "http://irs.gis-lab.info/?layers=irs&request=GetTile&z={z}&x={x}&y={y}",
|
||||
"extent": [
|
||||
[
|
||||
19.02,
|
||||
40.96
|
||||
],
|
||||
[
|
||||
77.34,
|
||||
70.48
|
||||
]
|
||||
],
|
||||
"sourcetag": "Kosmosnimki.ru IRS"
|
||||
},
|
||||
{
|
||||
"name": "Belarus - Kosmosnimki.ru SPOT4 Satellite",
|
||||
"template": "http://irs.gis-lab.info/?layers=spot&request=GetTile&z={z}&x={x}&y={y}",
|
||||
"extent": [
|
||||
[
|
||||
23.16,
|
||||
51.25
|
||||
],
|
||||
[
|
||||
32.83,
|
||||
56.19
|
||||
]
|
||||
],
|
||||
"sourcetag": "Kosmosnimki.ru SPOT4"
|
||||
},
|
||||
{
|
||||
"name": "Australia - Geographic Reference Image",
|
||||
"template": "http://agri.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
96,
|
||||
-44
|
||||
],
|
||||
[
|
||||
168,
|
||||
-9
|
||||
]
|
||||
],
|
||||
"sourcetag": "AGRI"
|
||||
},
|
||||
{
|
||||
"name": "Switzerland - Canton Aargau - AGIS 25cm 2011",
|
||||
"template": "http://tiles.poole.ch/AGIS/OF2011/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
7.69,
|
||||
47.13
|
||||
],
|
||||
[
|
||||
8.48,
|
||||
47.63
|
||||
]
|
||||
],
|
||||
"sourcetag": "AGIS OF2011"
|
||||
},
|
||||
{
|
||||
"name": "Switzerland - Canton Solothurn - SOGIS 2007",
|
||||
"template": "http://mapproxy.sosm.ch:8080/tiles/sogis2007/EPSG900913/{z}/{x}/{y}.png?origin=nw",
|
||||
"extent": [
|
||||
[
|
||||
7.33,
|
||||
47.06
|
||||
],
|
||||
[
|
||||
8.04,
|
||||
47.5
|
||||
]
|
||||
],
|
||||
"sourcetag": "Orthofoto 2007 WMS Solothurn"
|
||||
},
|
||||
{
|
||||
"name": "Poland - Media-Lab fleet GPS masstracks",
|
||||
"template": "http://masstracks.media-lab.com.pl/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
14,
|
||||
48.9
|
||||
],
|
||||
[
|
||||
24.2,
|
||||
55
|
||||
]
|
||||
],
|
||||
"sourcetag": "masstracks"
|
||||
},
|
||||
{
|
||||
"name": "South Africa - CD:NGI Aerial",
|
||||
"template": "http://{t}.aerial.openstreetmap.org.za/ngi-aerial/{z}/{x}/{y}.jpg",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
17.64,
|
||||
-34.95
|
||||
],
|
||||
[
|
||||
32.87,
|
||||
-22.05
|
||||
]
|
||||
],
|
||||
"sourcetag": "ngi-aerial"
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,609 @@
|
||||
[
|
||||
{
|
||||
"name": "Bing aerial imagery",
|
||||
"template": "http://ecn.t{t}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z",
|
||||
"description": "Satellite imagery.",
|
||||
"scaleExtent": [
|
||||
0,
|
||||
20
|
||||
],
|
||||
"subdomains": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3"
|
||||
],
|
||||
"default": "yes",
|
||||
"sourcetag": "Bing",
|
||||
"logo": "bing_maps.png",
|
||||
"logo_url": "http://www.bing.com/maps",
|
||||
"terms_url": "http://opengeodata.org/microsoft-imagery-details"
|
||||
},
|
||||
{
|
||||
"name": "MapBox Satellite",
|
||||
"template": "http://{t}.tiles.mapbox.com/v3/openstreetmap.map-4wvf9l0l/{z}/{x}/{y}.png",
|
||||
"description": "Satellite and aerial imagery",
|
||||
"scaleExtent": [
|
||||
0,
|
||||
16
|
||||
],
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"terms_url": "http://mapbox.com/tos/"
|
||||
},
|
||||
{
|
||||
"name": "OpenStreetMap",
|
||||
"template": "http://{t}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
"description": "The default OpenStreetMap layer.",
|
||||
"scaleExtent": [
|
||||
0,
|
||||
18
|
||||
],
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " TIGER 2012 Roads Overlay",
|
||||
"template": "http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-124.81,
|
||||
24.055
|
||||
],
|
||||
[
|
||||
-66.865,
|
||||
49.386
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " TIGER 2012 Roads Overlay",
|
||||
"template": "http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-179.754,
|
||||
50.858
|
||||
],
|
||||
[
|
||||
-129.899,
|
||||
71.463
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " TIGER 2012 Roads Overlay",
|
||||
"template": "http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-174.46,
|
||||
18.702
|
||||
],
|
||||
[
|
||||
-154.516,
|
||||
26.501
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Topographic Maps",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_scanned_topos/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-125.991,
|
||||
24.005
|
||||
],
|
||||
[
|
||||
-65.988,
|
||||
50.009
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Topographic Maps",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_scanned_topos/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-160.579,
|
||||
18.902
|
||||
],
|
||||
[
|
||||
-154.793,
|
||||
22.508
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Topographic Maps",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_scanned_topos/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-178.001,
|
||||
51.255
|
||||
],
|
||||
[
|
||||
-130.004,
|
||||
71.999
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": " USGS Large Scale Aerial Imagery",
|
||||
"template": "http://{t}.tile.openstreetmap.us/usgs_large_scale/{z}/{x}/{y}.jpg",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-124.819,
|
||||
24.496
|
||||
],
|
||||
[
|
||||
-66.931,
|
||||
49.443
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "British Columbia bc_mosaic",
|
||||
"template": "http://{t}.imagery.paulnorman.ca/tiles/bc_mosaic/{z}/{x}/{y}.png",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
-123.441,
|
||||
48.995
|
||||
],
|
||||
[
|
||||
-121.346,
|
||||
50.426
|
||||
]
|
||||
],
|
||||
"sourcetag": "bc_mosaic",
|
||||
"terms_url": "http://imagery.paulnorman.ca/tiles/about.html"
|
||||
},
|
||||
{
|
||||
"name": "OS OpenData Streetview",
|
||||
"template": "http://os.openstreetmap.org/sv/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-8.72,
|
||||
49.86
|
||||
],
|
||||
[
|
||||
1.84,
|
||||
60.92
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS_OpenData_StreetView"
|
||||
},
|
||||
{
|
||||
"name": "OS OpenData Locator",
|
||||
"template": "http://tiles.itoworld.com/os_locator/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS_OpenData_Locator"
|
||||
},
|
||||
{
|
||||
"name": "OS 1:25k historic (OSM)",
|
||||
"template": "http://ooc.openstreetmap.org/os1/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS 1:25k"
|
||||
},
|
||||
{
|
||||
"name": "OS 1:25k historic (NLS)",
|
||||
"template": "http://geo.nls.uk/mapdata2/os/25000/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS 1:25k",
|
||||
"logo": "icons/logo_nls70-nq8.png",
|
||||
"logo_url": "http://geo.nls.uk/maps/"
|
||||
},
|
||||
{
|
||||
"name": "OS 7th Series historic (OSM)",
|
||||
"template": "http://ooc.openstreetmap.org/os7/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS7"
|
||||
},
|
||||
{
|
||||
"name": "OS 7th Series historic (NLS)",
|
||||
"template": "http://geo.nls.uk/mapdata2/os/seventh/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-9,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "OS7",
|
||||
"logo": "icons/logo_nls70-nq8.png",
|
||||
"logo_url": "http://geo.nls.uk/maps/"
|
||||
},
|
||||
{
|
||||
"name": "OS New Popular Edition historic",
|
||||
"template": "http://ooc.openstreetmap.org/npe/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-5.8,
|
||||
49.8
|
||||
],
|
||||
[
|
||||
1.9,
|
||||
55.8
|
||||
]
|
||||
],
|
||||
"sourcetag": "NPE"
|
||||
},
|
||||
{
|
||||
"name": "OS Scottish Popular historic",
|
||||
"template": "http://ooc.openstreetmap.org/npescotland/tiles/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-7.8,
|
||||
54.5
|
||||
],
|
||||
[
|
||||
-1.1,
|
||||
61.1
|
||||
]
|
||||
],
|
||||
"sourcetag": "NPE"
|
||||
},
|
||||
{
|
||||
"name": "Surrey aerial",
|
||||
"template": "http://gravitystorm.dev.openstreetmap.org/surrey/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-0.856,
|
||||
51.071
|
||||
],
|
||||
[
|
||||
0.062,
|
||||
51.473
|
||||
]
|
||||
],
|
||||
"sourcetag": "Surrey aerial"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - GeoEye Jan 13",
|
||||
"template": "http://gravitystorm.dev.openstreetmap.org/imagery/haiti/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti GeoEye"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - GeoEye Jan 13+",
|
||||
"template": "http://maps.nypl.org/tilecache/1/geoeye/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti GeoEye"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - DigitalGlobe",
|
||||
"template": "http://maps.nypl.org/tilecache/1/dg_crisis/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti DigitalGlobe"
|
||||
},
|
||||
{
|
||||
"name": "Haiti - Street names",
|
||||
"template": "http://hypercube.telascience.org/tiles/1.0.0/haiti-city/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
-74.5,
|
||||
17.95
|
||||
],
|
||||
[
|
||||
-71.58,
|
||||
20.12
|
||||
]
|
||||
],
|
||||
"sourcetag": "Haiti streetnames"
|
||||
},
|
||||
{
|
||||
"name": "NAIP",
|
||||
"template": "http://cube.telascience.org/tilecache/tilecache.py/NAIP_ALL/{z}/{x}/{y}.png",
|
||||
"description": "National Agriculture Imagery Program",
|
||||
"extent": [
|
||||
[
|
||||
-125.8,
|
||||
24.2
|
||||
],
|
||||
[
|
||||
-62.3,
|
||||
49.5
|
||||
]
|
||||
],
|
||||
"sourcetag": "NAIP"
|
||||
},
|
||||
{
|
||||
"name": "NAIP",
|
||||
"template": "http://cube.telascience.org/tilecache/tilecache.py/NAIP_ALL/{z}/{x}/{y}.png",
|
||||
"description": "National Agriculture Imagery Program",
|
||||
"extent": [
|
||||
[
|
||||
-168.5,
|
||||
55.3
|
||||
],
|
||||
[
|
||||
-140,
|
||||
71.5
|
||||
]
|
||||
],
|
||||
"sourcetag": "NAIP"
|
||||
},
|
||||
{
|
||||
"name": "Ireland - NLS Historic Maps",
|
||||
"template": "http://geo.nls.uk/maps/ireland/gsgs4136/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
-10.71,
|
||||
51.32
|
||||
],
|
||||
[
|
||||
-5.37,
|
||||
55.46
|
||||
]
|
||||
],
|
||||
"sourcetag": "NLS Historic Maps",
|
||||
"logo": "icons/logo_nls70-nq8.png",
|
||||
"logo_url": "http://geo.nls.uk/maps/"
|
||||
},
|
||||
{
|
||||
"name": "Denmark - Fugro Aerial Imagery",
|
||||
"template": "http://tile.openstreetmap.dk/fugro2005/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
7.81,
|
||||
54.44
|
||||
],
|
||||
[
|
||||
15.49,
|
||||
57.86
|
||||
]
|
||||
],
|
||||
"sourcetag": "Fugro (2005)"
|
||||
},
|
||||
{
|
||||
"name": "Denmark - Stevns Kommune",
|
||||
"template": "http://tile.openstreetmap.dk/stevns/2009/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
12.09144,
|
||||
55.23403
|
||||
],
|
||||
[
|
||||
12.47712,
|
||||
55.43647
|
||||
]
|
||||
],
|
||||
"sourcetag": "Stevns Kommune (2009)"
|
||||
},
|
||||
{
|
||||
"name": "Austria - geoimage.at",
|
||||
"template": "http://geoimage.openstreetmap.at/4d80de696cd562a63ce463a58a61488d/{z}/{x}/{y}.jpg",
|
||||
"extent": [
|
||||
[
|
||||
9.36,
|
||||
46.33
|
||||
],
|
||||
[
|
||||
17.28,
|
||||
49.09
|
||||
]
|
||||
],
|
||||
"sourcetag": "geoimage.at"
|
||||
},
|
||||
{
|
||||
"name": "Russia - Kosmosnimki.ru IRS Satellite",
|
||||
"template": "http://irs.gis-lab.info/?layers=irs&request=GetTile&z={z}&x={x}&y={y}",
|
||||
"extent": [
|
||||
[
|
||||
19.02,
|
||||
40.96
|
||||
],
|
||||
[
|
||||
77.34,
|
||||
70.48
|
||||
]
|
||||
],
|
||||
"sourcetag": "Kosmosnimki.ru IRS"
|
||||
},
|
||||
{
|
||||
"name": "Belarus - Kosmosnimki.ru SPOT4 Satellite",
|
||||
"template": "http://irs.gis-lab.info/?layers=spot&request=GetTile&z={z}&x={x}&y={y}",
|
||||
"extent": [
|
||||
[
|
||||
23.16,
|
||||
51.25
|
||||
],
|
||||
[
|
||||
32.83,
|
||||
56.19
|
||||
]
|
||||
],
|
||||
"sourcetag": "Kosmosnimki.ru SPOT4"
|
||||
},
|
||||
{
|
||||
"name": "Australia - Geographic Reference Image",
|
||||
"template": "http://agri.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
96,
|
||||
-44
|
||||
],
|
||||
[
|
||||
168,
|
||||
-9
|
||||
]
|
||||
],
|
||||
"sourcetag": "AGRI"
|
||||
},
|
||||
{
|
||||
"name": "Switzerland - Canton Aargau - AGIS 25cm 2011",
|
||||
"template": "http://tiles.poole.ch/AGIS/OF2011/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
7.69,
|
||||
47.13
|
||||
],
|
||||
[
|
||||
8.48,
|
||||
47.63
|
||||
]
|
||||
],
|
||||
"sourcetag": "AGIS OF2011"
|
||||
},
|
||||
{
|
||||
"name": "Switzerland - Canton Solothurn - SOGIS 2007",
|
||||
"template": "http://mapproxy.sosm.ch:8080/tiles/sogis2007/EPSG900913/{z}/{x}/{y}.png?origin=nw",
|
||||
"extent": [
|
||||
[
|
||||
7.33,
|
||||
47.06
|
||||
],
|
||||
[
|
||||
8.04,
|
||||
47.5
|
||||
]
|
||||
],
|
||||
"sourcetag": "Orthofoto 2007 WMS Solothurn"
|
||||
},
|
||||
{
|
||||
"name": "Poland - Media-Lab fleet GPS masstracks",
|
||||
"template": "http://masstracks.media-lab.com.pl/{z}/{x}/{y}.png",
|
||||
"extent": [
|
||||
[
|
||||
14,
|
||||
48.9
|
||||
],
|
||||
[
|
||||
24.2,
|
||||
55
|
||||
]
|
||||
],
|
||||
"sourcetag": "masstracks"
|
||||
},
|
||||
{
|
||||
"name": "South Africa - CD:NGI Aerial",
|
||||
"template": "http://{t}.aerial.openstreetmap.org.za/ngi-aerial/{z}/{x}/{y}.jpg",
|
||||
"subdomains": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"extent": [
|
||||
[
|
||||
17.64,
|
||||
-34.95
|
||||
],
|
||||
[
|
||||
32.87,
|
||||
-22.05
|
||||
]
|
||||
],
|
||||
"sourcetag": "ngi-aerial"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,233 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<imagery>
|
||||
<set>
|
||||
<name>Bing aerial imagery</name>
|
||||
<url>http://ecn.t${0|1|2|3}.tiles.virtualearth.net/tiles/a$quadkey.jpeg?g=587&mkt=en-gb&n=z</url>
|
||||
<scheme>microsoft</scheme>
|
||||
<sourcetag>Bing</sourcetag>
|
||||
<attribution_url>http://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/0,0?zl=1&mapVersion=v1&key=Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU&include=ImageryProviders&output=xml</attribution_url>
|
||||
<logo>bing_maps.png</logo>
|
||||
<logo_url>http://www.bing.com/maps</logo_url>
|
||||
<terms_url>http://opengeodata.org/microsoft-imagery-details</terms_url>
|
||||
<default>yes</default>
|
||||
</set>
|
||||
<set>
|
||||
<name>MapBox Satellite</name>
|
||||
<url>http://${a|b|c}.tiles.mapbox.com/v3/openstreetmap.map-4wvf9l0l/$z/$x/$y.png</url>
|
||||
<terms_url>http://mapbox.com/tos/</terms_url>
|
||||
</set>
|
||||
<set>
|
||||
<name>MapQuest Open Aerial</name>
|
||||
<url>http://oatile1.mqcdn.com/tiles/1.0.0/sat/$z/$x/$y.jpg</url>
|
||||
<terms_url>http://developer.mapquest.com/web/products/open/map#terms</terms_url>
|
||||
</set>
|
||||
<set>
|
||||
<name>OSM - Mapnik</name>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.org/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set>
|
||||
<name>OSM - OpenCycleMap</name>
|
||||
<url>http://tile.opencyclemap.org/cycle/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set>
|
||||
<name>OSM - MapQuest</name>
|
||||
<url>http://otile1.mqcdn.com/tiles/1.0.0/osm/$z/$x/$y.jpg</url>
|
||||
</set>
|
||||
<set minlat="24.055" minlon="-124.810" maxlat="49.386" maxlon="-66.865">
|
||||
<name>OSM - Tiger Edited Map</name>
|
||||
<type>900913</type>
|
||||
<url>http://tiger-osm.mapquest.com/tiles/1.0.0/tiger/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="50.858" minlon="-179.754" maxlat="71.463" maxlon="-129.899">
|
||||
<name>OSM - Tiger Edited Map</name>
|
||||
<type>900913</type>
|
||||
<url>http://tiger-osm.mapquest.com/tiles/1.0.0/tiger/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="18.702" minlon="-174.460" maxlat="26.501" maxlon="-154.516">
|
||||
<name>OSM - Tiger Edited Map</name>
|
||||
<type>900913</type>
|
||||
<url>http://tiger-osm.mapquest.com/tiles/1.0.0/tiger/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="24.055" minlon="-124.810" maxlat="49.386" maxlon="-66.865">
|
||||
<name>OSM US TIGER 2012 Roads Overlay</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.us/tiger2012_roads_expanded/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="50.858" minlon="-179.754" maxlat="71.463" maxlon="-129.899">
|
||||
<name>OSM US TIGER 2012 Roads Overlay</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.us/tiger2012_roads_expanded/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="18.702" minlon="-174.460" maxlat="26.501" maxlon="-154.516">
|
||||
<name>OSM US TIGER 2012 Roads Overlay</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.us/tiger2012_roads_expanded/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="24.005" minlon="-125.991" maxlat="50.009" maxlon="-65.988">
|
||||
<name>OSM US USGS Topographic Maps</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.us/usgs_scanned_topos/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="18.902" minlon="-160.579" maxlat="22.508" maxlon="-154.793">
|
||||
<name>OSM US USGS Topographic Maps</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.us/usgs_scanned_topos/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="51.255" minlon="-178.001" maxlat="71.999" maxlon="-130.004">
|
||||
<name>OSM US USGS Topographic Maps</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.us/usgs_scanned_topos/$z/$x/$y.png</url>
|
||||
</set>
|
||||
<set minlat="24.496" minlon="-124.819" maxlat="49.443" maxlon="-66.931">
|
||||
<name>OSM US USGS Large Scale Aerial Imagery</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c}.tile.openstreetmap.us/usgs_large_scale/$z/$x/$y.jpg</url>
|
||||
</set>
|
||||
<set minlat="48.995" minlon="-123.441" maxlat="50.426" maxlon="-121.346">
|
||||
<name>British Columbia bc_mosaic</name>
|
||||
<type>900913</type>
|
||||
<url>http://${a|b|c|d}.imagery.paulnorman.ca/tiles/bc_mosaic/$z/$x/$y.png</url>
|
||||
<terms_url>http://imagery.paulnorman.ca/tiles/about.html</terms_url>
|
||||
<sourcetag>bc_mosaic</sourcetag>
|
||||
</set>
|
||||
<set minlat="49.86" minlon="-8.72" maxlat="60.92" maxlon="1.84">
|
||||
<name>OS OpenData Streetview</name>
|
||||
<url>http://os.openstreetmap.org/sv/$z/$x/$y.png</url>
|
||||
<sourcetag>OS_OpenData_StreetView</sourcetag>
|
||||
</set>
|
||||
<set minlat="49.8" minlon="-9" maxlat="61.1" maxlon="1.9">
|
||||
<name>OS OpenData Locator</name>
|
||||
<url>http://tiles.itoworld.com/os_locator/$z/$x/$y.png</url>
|
||||
<sourcetag>OS_OpenData_Locator</sourcetag>
|
||||
<sourcekey>source:name</sourcekey>
|
||||
</set>
|
||||
<set minlat="49.8" minlon="-9" maxlat="61.1" maxlon="1.9">
|
||||
<name>OS 1:25k historic (OSM)</name>
|
||||
<url>http://ooc.openstreetmap.org/os1/$z/$x/$y.jpg</url>
|
||||
<sourcetag>OS 1:25k</sourcetag>
|
||||
</set>
|
||||
<set minlat="49.8" minlon="-9" maxlat="61.1" maxlon="1.9">
|
||||
<name>OS 1:25k historic (NLS)</name>
|
||||
<scheme>tms</scheme>
|
||||
<url>http://geo.nls.uk/mapdata2/os/25000/$z/$x/$y.png</url>
|
||||
<logo>icons/logo_nls70-nq8.png</logo>
|
||||
<logo_url>http://geo.nls.uk/maps/</logo_url>
|
||||
<sourcetag>OS 1:25k</sourcetag>
|
||||
</set>
|
||||
<set minlat="49.8" minlon="-9" maxlat="61.1" maxlon="1.9">
|
||||
<name>OS 7th Series historic (OSM)</name>
|
||||
<url>http://ooc.openstreetmap.org/os7/$z/$x/$y.jpg</url>
|
||||
<sourcetag>OS7</sourcetag>
|
||||
</set>
|
||||
<set minlat="49.8" minlon="-9" maxlat="61.1" maxlon="1.9">
|
||||
<name>OS 7th Series historic (NLS)</name>
|
||||
<scheme>tms</scheme>
|
||||
<url>http://geo.nls.uk/mapdata2/os/seventh/$z/$x/$y.png</url>
|
||||
<logo>icons/logo_nls70-nq8.png</logo>
|
||||
<logo_url>http://geo.nls.uk/maps/</logo_url>
|
||||
<sourcetag>OS7</sourcetag>
|
||||
</set>
|
||||
<set minlat="49.8" minlon="-5.8" maxlat="55.8" maxlon="1.9">
|
||||
<name>OS New Popular Edition historic</name>
|
||||
<url>http://ooc.openstreetmap.org/npe/$z/$x/$y.png</url>
|
||||
<sourcetag>NPE</sourcetag>
|
||||
</set>
|
||||
<set minlat="54.5" minlon="-7.8" maxlat="61.1" maxlon="-1.1">
|
||||
<name>OS Scottish Popular historic</name>
|
||||
<url>http://ooc.openstreetmap.org/npescotland/tiles/$z/$x/$y.jpg</url>
|
||||
<sourcetag>NPE</sourcetag>
|
||||
</set>
|
||||
<set minlat="51.071" minlon="-0.856" maxlat="51.473" maxlon="0.062">
|
||||
<name>Surrey aerial</name>
|
||||
<url>http://gravitystorm.dev.openstreetmap.org/surrey/$z/$x/$y.png</url>
|
||||
<sourcetag>Surrey aerial</sourcetag>
|
||||
</set>
|
||||
<set minlat="17.95" minlon="-74.5" maxlat="20.12" maxlon="-71.58">
|
||||
<name>Haiti - GeoEye Jan 13</name>
|
||||
<url>http://gravitystorm.dev.openstreetmap.org/imagery/haiti/$z/$x/$y.jpg</url>
|
||||
<sourcetag>Haiti GeoEye</sourcetag>
|
||||
</set>
|
||||
<set minlat="17.95" minlon="-74.5" maxlat="20.12" maxlon="-71.58">
|
||||
<name>Haiti - GeoEye Jan 13+</name>
|
||||
<url>http://maps.nypl.org/tilecache/1/geoeye/$z/$x/$y.jpg</url>
|
||||
<sourcetag>Haiti GeoEye</sourcetag>
|
||||
</set>
|
||||
<set minlat="17.95" minlon="-74.5" maxlat="20.12" maxlon="-71.58">
|
||||
<name>Haiti - DigitalGlobe</name>
|
||||
<url>http://maps.nypl.org/tilecache/1/dg_crisis/$z/$x/$y.jpg</url>
|
||||
<sourcetag>Haiti DigitalGlobe</sourcetag>
|
||||
</set>
|
||||
<set minlat="17.95" minlon="-74.5" maxlat="20.12" maxlon="-71.58">
|
||||
<name>Haiti - Street names</name>
|
||||
<url>http://hypercube.telascience.org/tiles/1.0.0/haiti-city/$z/$x/$y.jpg</url>
|
||||
<sourcetag>Haiti streetnames</sourcetag>
|
||||
</set>
|
||||
<set minlat="24.2" minlon="-125.8" maxlat="49.5" maxlon="-62.3">
|
||||
<name>National Agriculture Imagery Program</name>
|
||||
<url>http://cube.telascience.org/tilecache/tilecache.py/NAIP_ALL/$z/$x/$y.png</url>
|
||||
<sourcetag>NAIP</sourcetag>
|
||||
</set>
|
||||
<set minlat="55.3" minlon="-168.5" maxlat="71.5" maxlon="-140">
|
||||
<name>National Agriculture Imagery Program</name>
|
||||
<url>http://cube.telascience.org/tilecache/tilecache.py/NAIP_ALL/$z/$x/$y.png</url>
|
||||
<sourcetag>NAIP</sourcetag>
|
||||
</set>
|
||||
<set minlat="51.32" minlon="-10.71" maxlat="55.46" maxlon="-5.37">
|
||||
<name>Ireland - NLS Historic Maps</name>
|
||||
<scheme>tms</scheme>
|
||||
<sourcetag>NLS Historic Maps</sourcetag>
|
||||
<url>http://geo.nls.uk/maps/ireland/gsgs4136/$z/$x/$y.png</url>
|
||||
<logo>icons/logo_nls70-nq8.png</logo>
|
||||
<logo_url>http://geo.nls.uk/maps/</logo_url>
|
||||
</set>
|
||||
<set minlat="54.44" minlon="7.81" maxlat="57.86" maxlon="15.49">
|
||||
<name>Denmark - Fugro Aerial Imagery</name>
|
||||
<url>http://tile.openstreetmap.dk/fugro2005/$z/$x/$y.jpg</url>
|
||||
<sourcetag>Fugro (2005)</sourcetag>
|
||||
</set>
|
||||
<set minlat="55.23403" minlon="12.09144" maxlat="55.43647" maxlon="12.47712">
|
||||
<name>Denmark - Stevns Kommune</name>
|
||||
<url>http://tile.openstreetmap.dk/stevns/2009/$z/$x/$y.jpg</url>
|
||||
<sourcetag>Stevns Kommune (2009)</sourcetag>
|
||||
</set>
|
||||
<set minlat="46.33" minlon="9.36" maxlat="49.09" maxlon="17.28">
|
||||
<name>Austria - geoimage.at</name>
|
||||
<url>http://geoimage.openstreetmap.at/4d80de696cd562a63ce463a58a61488d/$z/$x/$y.jpg</url>
|
||||
<sourcetag>geoimage.at</sourcetag>
|
||||
</set>
|
||||
<set minlon="19.02" minlat="40.96" maxlon="77.34" maxlat="70.48">
|
||||
<name>Russia - Kosmosnimki.ru IRS Satellite</name>
|
||||
<url>http://irs.gis-lab.info/?layers=irs&request=GetTile&z=$z&x=$x&y=$y</url>
|
||||
<sourcetag>Kosmosnimki.ru IRS</sourcetag>
|
||||
</set>
|
||||
<set minlon="23.16" minlat="51.25" maxlon="32.83" maxlat="56.19">
|
||||
<name>Belarus - Kosmosnimki.ru SPOT4 Satellite</name>
|
||||
<url>http://irs.gis-lab.info/?layers=spot&request=GetTile&z=$z&x=$x&y=$y</url>
|
||||
<sourcetag>Kosmosnimki.ru SPOT4</sourcetag>
|
||||
</set>
|
||||
<set minlon="96" minlat="-44" maxlon="168" maxlat="-9">
|
||||
<name>Australia - Geographic Reference Image</name>
|
||||
<url>http://agri.openstreetmap.org/$z/$x/$y.png</url>
|
||||
<sourcetag>AGRI</sourcetag>
|
||||
</set>
|
||||
<set minlat="47.13" minlon="7.69" maxlat="47.63" maxlon="8.48">
|
||||
<name>Switzerland - Canton Aargau - AGIS 25cm 2011</name>
|
||||
<url>http://tiles.poole.ch/AGIS/OF2011/$z/$x/$y.png</url>
|
||||
<sourcetag>AGIS OF2011</sourcetag>
|
||||
</set>
|
||||
<set minlat="47.06" minlon="7.33" maxlat="47.5" maxlon="8.04">
|
||||
<name>Switzerland - Canton Solothurn - SOGIS 2007</name>
|
||||
<url>http://mapproxy.sosm.ch:8080/tiles/sogis2007/EPSG900913/$z/$x/$y.png?origin=nw</url>
|
||||
<sourcetag>Orthofoto 2007 WMS Solothurn</sourcetag>
|
||||
</set>
|
||||
<set minlat="48.9" minlon="14" maxlat="55" maxlon="24.2">
|
||||
<name>Poland - Media-Lab fleet GPS masstracks</name>
|
||||
<url>http://masstracks.media-lab.com.pl/$z/$x/$y.png</url>
|
||||
<sourcetag>masstracks</sourcetag>
|
||||
</set>
|
||||
<set minlat="-34.95" minlon="17.64" maxlat="-22.05" maxlon="32.87">
|
||||
<name>South Africa - CD:NGI Aerial</name>
|
||||
<url>http://${a|b|c}.aerial.openstreetmap.org.za/ngi-aerial/$z/$x/$y.jpg</url>
|
||||
<sourcetag>ngi-aerial</sourcetag>
|
||||
</set>
|
||||
</imagery>
|
||||
@@ -0,0 +1,87 @@
|
||||
var fs = require('fs'),
|
||||
cheerio = require('cheerio');
|
||||
|
||||
$ = cheerio.load(fs.readFileSync('imagery.xml'));
|
||||
|
||||
var imagery = [];
|
||||
|
||||
// CENSORSHIP! No, these are just layers that essentially duplicate other layers
|
||||
// or which have no clear use case.
|
||||
var censor = {
|
||||
'MapQuest Open Aerial': true,
|
||||
'OSM - OpenCycleMap': true,
|
||||
'OSM - MapQuest': true
|
||||
};
|
||||
|
||||
var replace = {
|
||||
'OSM - Mapnik': 'OpenStreetMap',
|
||||
'National Agriculture Imagery Program': 'NAIP'
|
||||
};
|
||||
|
||||
var description = {
|
||||
'MapBox Satellite': 'Satellite and aerial imagery',
|
||||
'OpenStreetMap': 'The default OpenStreetMap layer.',
|
||||
'OSM US TIGER 2012 Roads Overlay': 'Public domain road data from the US Government.',
|
||||
'Bing aerial imagery': 'Satellite imagery.',
|
||||
'NAIP': 'National Agriculture Imagery Program'
|
||||
};
|
||||
|
||||
var scaleExtent = {
|
||||
'MapBox Satellite': [0, 16],
|
||||
'OpenStreetMap': [0, 18],
|
||||
'OSM US TIGER 2012 Roads Overlay': [0, 17],
|
||||
'Bing aerial imagery': [0, 20]
|
||||
};
|
||||
|
||||
$('set').each(function(i) {
|
||||
var elem = $(this);
|
||||
|
||||
var im = {
|
||||
name: $(this).find('name').first().text(),
|
||||
template: $(this).find('url').first().text()
|
||||
};
|
||||
|
||||
// no luck with mapquest servers currently...
|
||||
if (im.template.match(/mapquest/g)) return;
|
||||
if (censor[im.name]) return;
|
||||
|
||||
im.name = im.name.replace('OSM US', '');
|
||||
|
||||
if (replace[im.name]) im.name = replace[im.name];
|
||||
|
||||
if (description[im.name]) im.description = description[im.name];
|
||||
|
||||
if (scaleExtent[im.name]) im.scaleExtent = scaleExtent[im.name];
|
||||
|
||||
var subdomains = [];
|
||||
|
||||
im.template = im.template
|
||||
.replace('$quadkey', '{u}')
|
||||
.replace(/\$(\w)/g, function(m) {
|
||||
return '{' + m[1] + '}';
|
||||
})
|
||||
.replace(/\$\{([^}.]+)\}/g, function(m) {
|
||||
subdomains = m.slice(2, m.length - 1).split('|');
|
||||
return '{t}';
|
||||
});
|
||||
|
||||
if (subdomains.length) im.subdomains = subdomains;
|
||||
|
||||
if (elem.attr('minlat')) {
|
||||
im.extent = [
|
||||
[+elem.attr('minlon'),
|
||||
+elem.attr('minlat')],
|
||||
[+elem.attr('maxlon'),
|
||||
+elem.attr('maxlat')]];
|
||||
}
|
||||
|
||||
['default', 'sourcetag', 'logo', 'logo_url', 'terms_url'].forEach(function(a) {
|
||||
if (elem.find(a).length) {
|
||||
im[a] = elem.find(a).first().text();
|
||||
}
|
||||
});
|
||||
imagery.push(im);
|
||||
});
|
||||
|
||||
fs.writeFileSync('imagery.json', JSON.stringify(imagery, null, 4));
|
||||
fs.writeFileSync('imagery.js', 'iD.data.imagery = ' + JSON.stringify(imagery, null, 4) + ';');
|
||||
|
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 158 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 338 B |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
@@ -9,8 +9,8 @@
|
||||
|
||||
<!-- mobile devices -->
|
||||
<meta name='viewport' content='initial-scale=1.0 maximum-scale=1.0'>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name='apple-mobile-web-app-capable' content='yes' />
|
||||
<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />
|
||||
|
||||
<script src='js/lib/lodash.js'></script>
|
||||
<script src='js/lib/d3.v3.js'></script>
|
||||
@@ -34,19 +34,24 @@
|
||||
<script src='js/id/presetdata.js'></script>
|
||||
<script src='js/id/services/taginfo.js'></script>
|
||||
|
||||
<script src='data/data.js'></script>
|
||||
<script src='data/deprecated.js'></script>
|
||||
<script src='data/imagery.js'></script>
|
||||
<script src='data/discarded.js'></script>
|
||||
|
||||
<script src="js/id/geo.js"></script>
|
||||
<script src="js/id/geo/extent.js"></script>
|
||||
|
||||
<script src='js/id/renderer/background.js'></script>
|
||||
<script src='js/id/renderer/background_source.js'></script>
|
||||
<script src='js/id/renderer/map.js'></script>
|
||||
<script src='js/id/renderer/layers.js'></script>
|
||||
|
||||
<script src="js/id/svg.js"></script>
|
||||
<script src="js/id/svg/areas.js"></script>
|
||||
<script src="js/id/svg/lines.js"></script>
|
||||
<script src="js/id/svg/member_classes.js"></script>
|
||||
<script src="js/id/svg/midpoints.js"></script>
|
||||
<script src="js/id/svg/multipolygons.js"></script>
|
||||
<script src="js/id/svg/points.js"></script>
|
||||
<script src="js/id/svg/surface.js"></script>
|
||||
<script src="js/id/svg/tag_classes.js"></script>
|
||||
@@ -54,15 +59,18 @@
|
||||
<script src="js/id/svg/labels.js"></script>
|
||||
|
||||
<script src="js/id/ui.js"></script>
|
||||
<script src='js/id/ui/attribution.js'></script>
|
||||
<script src='js/id/ui/radial_menu.js'></script>
|
||||
<script src='js/id/ui/inspector.js'></script>
|
||||
<script src='js/id/ui/modal.js'></script>
|
||||
<script src='js/id/ui/cmd.js'></script>
|
||||
<script src='js/id/ui/confirm.js'></script>
|
||||
<script src='js/id/ui/commit.js'></script>
|
||||
<script src='js/id/ui/success.js'></script>
|
||||
<script src='js/id/ui/loading.js'></script>
|
||||
<script src='js/id/ui/userpanel.js'></script>
|
||||
<script src='js/id/ui/account.js'></script>
|
||||
<script src='js/id/ui/layerswitcher.js'></script>
|
||||
<script src='js/id/ui/modes.js'></script>
|
||||
<script src='js/id/ui/contributors.js'></script>
|
||||
<script src='js/id/ui/geocoder.js'></script>
|
||||
<script src='js/id/ui/geolocate.js'></script>
|
||||
@@ -70,33 +78,45 @@
|
||||
<script src='js/id/ui/flash.js'></script>
|
||||
<script src='js/id/ui/save.js'></script>
|
||||
<script src='js/id/ui/splash.js'></script>
|
||||
<script src='js/id/ui/restore.js'></script>
|
||||
<script src='js/id/ui/tag_reference.js'></script>
|
||||
<script src='js/id/ui/key_reference.js'></script>
|
||||
<script src='js/id/ui/preset.js'></script>
|
||||
<script src='js/id/ui/presetsearch.js'></script>
|
||||
<script src='js/id/ui/presetfavs.js'></script>
|
||||
<script src='js/id/ui/lasso.js'></script>
|
||||
<script src='js/id/ui/source_switch.js'></script>
|
||||
<script src='js/id/ui/toggle.js'></script>
|
||||
<script src='js/id/ui/undo_redo.js'></script>
|
||||
<script src='js/id/ui/zoom.js'></script>
|
||||
|
||||
<script src='js/id/actions.js'></script>
|
||||
<script src="js/id/actions/add_midpoint.js"></script>
|
||||
<script src='js/id/actions/add_entity.js'></script>
|
||||
<script src='js/id/actions/add_vertex.js'></script>
|
||||
<script src='js/id/actions/change_tags.js'></script>
|
||||
<script src='js/id/actions/connect.js'></script>
|
||||
<script src='js/id/actions/delete_multiple.js'></script>
|
||||
<script src='js/id/actions/delete_node.js'></script>
|
||||
<script src="js/id/actions/delete_relation.js"></script>
|
||||
<script src="js/id/actions/delete_way.js"></script>
|
||||
<script src='js/id/actions/disconnect.js'></script>
|
||||
<script src='js/id/actions/join.js'></script>
|
||||
<script src='js/id/actions/merge.js'></script>
|
||||
<script src='js/id/actions/move_node.js'></script>
|
||||
<script src='js/id/actions/move_way.js'></script>
|
||||
<script src='js/id/actions/circularize.js'></script>
|
||||
<script src='js/id/actions/orthogonalize.js'></script>
|
||||
<script src='js/id/actions/noop.js'></script>
|
||||
<script src='js/id/actions/reverse_way.js'></script>
|
||||
<script src='js/id/actions/split_way.js'></script>
|
||||
<script src='js/id/actions/unjoin_node.js'></script>
|
||||
<script src='js/id/actions/reverse.js'></script>
|
||||
<script src='js/id/actions/split.js'></script>
|
||||
|
||||
<script src='js/id/behavior.js'></script>
|
||||
<script src='js/id/behavior/add_way.js'></script>
|
||||
<script src='js/id/behavior/drag.js'></script>
|
||||
<script src='js/id/behavior/drag_midpoint.js'></script>
|
||||
<script src='js/id/behavior/drag_node.js'></script>
|
||||
<script src='js/id/behavior/draw.js'></script>
|
||||
<script src='js/id/behavior/lasso.js'></script>
|
||||
<script src='js/id/behavior/draw_way.js'></script>
|
||||
<script src='js/id/behavior/hash.js'></script>
|
||||
<script src='js/id/behavior/hover.js'></script>
|
||||
@@ -114,41 +134,59 @@
|
||||
|
||||
<script src='js/id/operations.js'></script>
|
||||
<script src='js/id/operations/circularize.js'></script>
|
||||
<script src='js/id/operations/orthogonalize.js'></script>
|
||||
<script src='js/id/operations/delete.js'></script>
|
||||
<script src='js/id/operations/disconnect.js'></script>
|
||||
<script src='js/id/operations/merge.js'></script>
|
||||
<script src='js/id/operations/move.js'></script>
|
||||
<script src='js/id/operations/reverse.js'></script>
|
||||
<script src='js/id/operations/split.js'></script>
|
||||
<script src='js/id/operations/unjoin.js'></script>
|
||||
|
||||
<script src='js/id/graph/entity.js'></script>
|
||||
<script src='js/id/graph/graph.js'></script>
|
||||
<script src='js/id/graph/history.js'></script>
|
||||
<script src='js/id/graph/node.js'></script>
|
||||
<script src='js/id/graph/relation.js'></script>
|
||||
<script src='js/id/graph/way.js'></script>
|
||||
<script src='js/id/core/difference.js'></script>
|
||||
<script src='js/id/core/entity.js'></script>
|
||||
<script src='js/id/core/graph.js'></script>
|
||||
<script src='js/id/core/history.js'></script>
|
||||
<script src='js/id/core/node.js'></script>
|
||||
<script src='js/id/core/relation.js'></script>
|
||||
<script src='js/id/core/way.js'></script>
|
||||
|
||||
<script src='js/id/controller.js'></script>
|
||||
<script src='js/id/validate.js'></script>
|
||||
<script src='js/id/connection.js'></script>
|
||||
<script src='js/id/validate.js'></script>
|
||||
|
||||
<script src='locale/locale.js'></script>
|
||||
<script src='js/lib/locale.js'></script>
|
||||
<script src='locale/da.js'></script>
|
||||
<script src='locale/de.js'></script>
|
||||
<script src='locale/en.js'></script>
|
||||
<script src='locale/es.js'></script>
|
||||
<script src='locale/fr.js'></script>
|
||||
<script src='locale/ja.js'></script>
|
||||
<script src='locale/lv.js'></script>
|
||||
<script src='locale/tr.js'></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="iD"></div><script>
|
||||
locale.current = 'en';
|
||||
<div id='iD'></div><script>
|
||||
locale
|
||||
.current('en')
|
||||
.current(iD.detect().locale);
|
||||
|
||||
var id = iD();
|
||||
|
||||
d3.json('keys.json', function(err, keys) {
|
||||
d3.json('presets/presets_josm.json', function(err, presets_data) {
|
||||
var id = iD();
|
||||
id.connection().keys(keys)
|
||||
.presetData(iD.presetData().data(presets_data))
|
||||
.url('http://api06.dev.openstreetmap.org');
|
||||
d3.select("#iD").call(id);
|
||||
|
||||
id.connection()
|
||||
.keys(keys)
|
||||
.presetData(iD.presetData().data(presets_data));
|
||||
|
||||
d3.select("#iD")
|
||||
.call(id.ui())
|
||||
});
|
||||
});
|
||||
</script></body>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
<!-- google analytics -->
|
||||
<script type='text/javascript'>
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-38039653-2']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
@@ -156,6 +194,26 @@ _gaq.push(['_trackPageview']);
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
|
||||
// javascript errors
|
||||
var lastev = '';
|
||||
window.onerror = function(message, file, lineNumber) {
|
||||
var ev = ['_trackEvent', 'error', file + ':' + lineNumber, message + ''];
|
||||
if (ev.join(',') !== lastev) {
|
||||
_gaq.push(ev);
|
||||
lastev = ev.join(',');
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
</script>
|
||||
|
||||
<!-- crazyegg -->
|
||||
<script type="text/javascript">
|
||||
setTimeout(function(){var a=document.createElement("script");
|
||||
var b=document.getElementsByTagName("script")[0];
|
||||
a.src=document.location.protocol+"//dnn506yrbagrg.cloudfront.net/pages/scripts/0013/6714.js?"+Math.floor(new Date().getTime()/3600000);
|
||||
a.async=true;a.type="text/javascript";b.parentNode.insertBefore(a,b)}, 1);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -16,8 +16,17 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="iD"></div><script>
|
||||
locale.current = 'en';
|
||||
|
||||
var id = iD();
|
||||
id.connection().url('http://api06.dev.openstreetmap.org');
|
||||
d3.select("#iD").call(id);
|
||||
|
||||
d3.json('keys.json', function(err, keys) {
|
||||
id.connection()
|
||||
.keys(keys)
|
||||
.url('http://api06.dev.openstreetmap.org');
|
||||
|
||||
d3.select("#iD")
|
||||
.call(id.ui())
|
||||
});
|
||||
</script></body>
|
||||
</html>
|
||||
|
||||
@@ -1,53 +1,59 @@
|
||||
iD.actions.Circularize = function(wayId, map) {
|
||||
iD.actions.Circularize = function(wayId, projection, count) {
|
||||
count = count || 12;
|
||||
|
||||
function closestIndex(nodes, loc) {
|
||||
var idx, min = Infinity, dist;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
dist = iD.geo.dist(nodes[i].loc, loc);
|
||||
if (dist < min) {
|
||||
min = dist;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
var way = graph.entity(wayId),
|
||||
nodes = graph.childNodes(way),
|
||||
tags = {}, key, role;
|
||||
|
||||
var points = nodes.map(function(n) {
|
||||
return map.projection(n.loc);
|
||||
}),
|
||||
nodes = _.uniq(graph.childNodes(way)),
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
centroid = d3.geom.polygon(points).centroid(),
|
||||
radius = d3.median(points, function(p) {
|
||||
return iD.geo.dist(centroid, p);
|
||||
}),
|
||||
circular_nodes = [];
|
||||
ids = [];
|
||||
|
||||
for (var i = 0; i < 12; i++) {
|
||||
circular_nodes.push(iD.Node({ loc: map.projection.invert([
|
||||
centroid[0] + Math.cos((i / 12) * Math.PI * 2) * radius,
|
||||
centroid[1] + Math.sin((i / 12) * Math.PI * 2) * radius])
|
||||
}));
|
||||
for (var i = 0; i < count; i++) {
|
||||
var node,
|
||||
loc = projection.invert([
|
||||
centroid[0] + Math.cos((i / 12) * Math.PI * 2) * radius,
|
||||
centroid[1] + Math.sin((i / 12) * Math.PI * 2) * radius]);
|
||||
|
||||
if (nodes.length) {
|
||||
var idx = closestIndex(nodes, loc);
|
||||
node = nodes[idx];
|
||||
nodes.splice(idx, 1);
|
||||
} else {
|
||||
node = iD.Node();
|
||||
}
|
||||
|
||||
ids.push(node.id);
|
||||
graph = graph.replace(node.move(loc));
|
||||
}
|
||||
|
||||
circular_nodes.push(circular_nodes[0]);
|
||||
ids.push(ids[0]);
|
||||
graph = graph.replace(way.update({nodes: ids}));
|
||||
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
if (graph.parentWays(nodes[i]).length > 1) {
|
||||
var closest, closest_dist = Infinity, dist;
|
||||
for (var j = 0; j < circular_nodes.length; j++) {
|
||||
dist = iD.geo.dist(circular_nodes[j].loc, nodes[i].loc);
|
||||
if (dist < closest_dist) {
|
||||
closest_dist = dist;
|
||||
closest = j;
|
||||
}
|
||||
}
|
||||
circular_nodes.splice(closest, 1, nodes[i]);
|
||||
if (closest === 0) circular_nodes.splice(circular_nodes.length - 1, 1, nodes[i]);
|
||||
else if (closest === circular_nodes.length - 1) circular_nodes.splice(0, 1, nodes[i]);
|
||||
} else {
|
||||
graph = graph.remove(nodes[i]);
|
||||
}
|
||||
graph.parentWays(nodes[i]).forEach(function(parent) {
|
||||
graph = graph.replace(parent.replaceNode(nodes[i].id,
|
||||
ids[closestIndex(graph.childNodes(way), nodes[i].loc)]));
|
||||
});
|
||||
|
||||
graph = iD.actions.DeleteNode(nodes[i].id)(graph);
|
||||
}
|
||||
|
||||
for (i = 0; i < circular_nodes.length; i++) {
|
||||
graph = graph.replace(circular_nodes[i]);
|
||||
}
|
||||
|
||||
return graph.replace(way.update({
|
||||
nodes: _.pluck(circular_nodes, 'id')
|
||||
}));
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Connect the ways at the given nodes.
|
||||
//
|
||||
// The last node will survive. All other nodes will be replaced with
|
||||
// the surviving node in parent ways, and then removed.
|
||||
//
|
||||
// Tags and relation memberships of of non-surviving nodes are merged
|
||||
// to the survivor.
|
||||
//
|
||||
// This is the inverse of `iD.actions.Disconnect`.
|
||||
//
|
||||
// Reference:
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
|
||||
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
|
||||
//
|
||||
iD.actions.Connect = function(nodeIds) {
|
||||
var action = function(graph) {
|
||||
var survivor = graph.entity(_.last(nodeIds));
|
||||
|
||||
for (var i = 0; i < nodeIds.length - 1; i++) {
|
||||
var node = graph.entity(nodeIds[i]), index;
|
||||
|
||||
graph.parentWays(node).forEach(function(parent) {
|
||||
graph = graph.replace(parent.replaceNode(node.id, survivor.id));
|
||||
});
|
||||
|
||||
graph.parentRelations(node).forEach(function(parent) {
|
||||
graph = graph.replace(parent.replaceMember(node, survivor));
|
||||
});
|
||||
|
||||
survivor = survivor.mergeTags(node.tags);
|
||||
graph = iD.actions.DeleteNode(node.id)(graph);
|
||||
}
|
||||
|
||||
graph = graph.replace(survivor);
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return nodeIds.length > 1;
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
iD.actions.DeleteMultiple = function(ids) {
|
||||
return function(graph) {
|
||||
var actions = {
|
||||
way: iD.actions.DeleteWay,
|
||||
node: iD.actions.DeleteNode,
|
||||
relation: iD.actions.DeleteRelation
|
||||
};
|
||||
|
||||
ids.forEach(function(id) {
|
||||
var entity = graph.entity(id);
|
||||
if (entity) { // It may have been deleted aready.
|
||||
graph = actions[entity.type](id)(graph);
|
||||
}
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
|
||||
iD.actions.DeleteRelation = function(relationId) {
|
||||
return function(graph) {
|
||||
var relation = graph.entity(relationId);
|
||||
|
||||
graph.parentRelations(relation)
|
||||
.forEach(function(parent) {
|
||||
graph = graph.replace(parent.removeMember(relationId));
|
||||
});
|
||||
|
||||
return graph.remove(relation);
|
||||
};
|
||||
};
|
||||
@@ -8,7 +8,7 @@ iD.actions.DeleteWay = function(wayId) {
|
||||
graph = graph.replace(parent.removeMember(wayId));
|
||||
});
|
||||
|
||||
way.nodes.forEach(function (nodeId) {
|
||||
way.nodes.forEach(function(nodeId) {
|
||||
var node = graph.entity(nodeId);
|
||||
|
||||
// Circular ways include nodes more than once, so they
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
iD.actions.DeprecateTags = function(entityId) {
|
||||
return function(graph) {
|
||||
var entity = graph.entity(entityId),
|
||||
newtags = _.clone(entity.tags),
|
||||
change = false,
|
||||
rule;
|
||||
|
||||
// This handles deprecated tags with a single condition
|
||||
for (var i = 0; i < iD.data.deprecated.length; i++) {
|
||||
|
||||
rule = iD.data.deprecated[i];
|
||||
var match = _.pairs(rule.old)[0],
|
||||
replacements = rule.replace ? _.pairs(rule.replace) : null;
|
||||
|
||||
if (entity.tags[match[0]] && match[1] === '*') {
|
||||
|
||||
var value = entity.tags[match[0]];
|
||||
if (replacements && !newtags[replacements[0][0]]) {
|
||||
newtags[replacements[0][0]] = value;
|
||||
}
|
||||
delete newtags[match[0]];
|
||||
change = true;
|
||||
|
||||
} else if (entity.tags[match[0]] === match[1]) {
|
||||
newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (change) {
|
||||
return graph.replace(entity.update({tags: newtags}));
|
||||
} else {
|
||||
return graph;
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,14 +1,16 @@
|
||||
// Unjoin the ways at the given node.
|
||||
// Disconect the ways at the given node.
|
||||
//
|
||||
// For testing convenience, accepts an ID to assign to the (first) new node.
|
||||
// Normally, this will be undefined and the way will automatically
|
||||
// be assigned a new ID.
|
||||
//
|
||||
// This is the inverse of `iD.actions.Connect`.
|
||||
//
|
||||
// Reference:
|
||||
// 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
|
||||
//
|
||||
iD.actions.UnjoinNode = function(nodeId, newNodeId) {
|
||||
iD.actions.Disconnect = function(nodeId, newNodeId) {
|
||||
var action = function(graph) {
|
||||
if (!action.enabled(graph))
|
||||
return graph;
|
||||
@@ -0,0 +1,76 @@
|
||||
// Join ways at the end node they share.
|
||||
//
|
||||
// This is the inverse of `iD.actions.Split`.
|
||||
//
|
||||
// Reference:
|
||||
// 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
|
||||
//
|
||||
iD.actions.Join = function(ids) {
|
||||
var idA = ids[0],
|
||||
idB = ids[1];
|
||||
|
||||
function groupEntitiesByGeometry(graph) {
|
||||
var entities = ids.map(function(id) { return graph.entity(id); });
|
||||
return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
var a = graph.entity(idA),
|
||||
b = graph.entity(idB),
|
||||
nodes;
|
||||
|
||||
if (a.first() === b.first()) {
|
||||
// a <-- b ==> c
|
||||
// Expected result:
|
||||
// a <-- b <-- c
|
||||
b = iD.actions.Reverse(idB)(graph).entity(idB);
|
||||
nodes = b.nodes.slice().concat(a.nodes.slice(1));
|
||||
|
||||
} else if (a.first() === b.last()) {
|
||||
// a <-- b <== c
|
||||
// Expected result:
|
||||
// a <-- b <-- c
|
||||
nodes = b.nodes.concat(a.nodes.slice(1));
|
||||
|
||||
} else if (a.last() === b.first()) {
|
||||
// a --> b ==> c
|
||||
// Expected result:
|
||||
// a --> b --> c
|
||||
nodes = a.nodes.concat(b.nodes.slice(1));
|
||||
|
||||
} else if (a.last() === b.last()) {
|
||||
// a --> b <== c
|
||||
// Expected result:
|
||||
// a --> b --> c
|
||||
b = iD.actions.Reverse(idB)(graph).entity(idB);
|
||||
nodes = a.nodes.concat(b.nodes.slice().slice(1));
|
||||
}
|
||||
|
||||
graph.parentRelations(b).forEach(function(parent) {
|
||||
graph = graph.replace(parent.replaceMember(b, a));
|
||||
});
|
||||
|
||||
graph = graph.replace(a.mergeTags(b.tags).update({ nodes: nodes }));
|
||||
graph = iD.actions.DeleteWay(idB)(graph);
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
var geometries = groupEntitiesByGeometry(graph);
|
||||
|
||||
if (ids.length !== 2 || ids.length !== geometries.line.length)
|
||||
return false;
|
||||
|
||||
var a = graph.entity(idA),
|
||||
b = graph.entity(idB);
|
||||
|
||||
return a.first() === b.first() ||
|
||||
a.first() === b.last() ||
|
||||
a.last() === b.first() ||
|
||||
a.last() === b.last();
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
iD.actions.Merge = function(ids) {
|
||||
function groupEntitiesByGeometry(graph) {
|
||||
var entities = ids.map(function(id) { return graph.entity(id); });
|
||||
return _.extend({point: [], area: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
var geometries = groupEntitiesByGeometry(graph),
|
||||
area = geometries.area[0],
|
||||
points = geometries.point;
|
||||
|
||||
points.forEach(function(point) {
|
||||
area = area.mergeTags(point.tags);
|
||||
|
||||
graph.parentRelations(point).forEach(function(parent) {
|
||||
graph = graph.replace(parent.replaceMember(point, area));
|
||||
});
|
||||
|
||||
graph = graph.remove(point);
|
||||
});
|
||||
|
||||
graph = graph.replace(area);
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
var geometries = groupEntitiesByGeometry(graph);
|
||||
return geometries.area.length === 1 &&
|
||||
geometries.point.length > 0 &&
|
||||
(geometries.area.length + geometries.point.length) === ids.length;
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
iD.actions.MoveWay = function(wayId, delta, projection) {
|
||||
return function(graph) {
|
||||
return graph.update(function (graph) {
|
||||
return graph.update(function(graph) {
|
||||
var way = graph.entity(wayId);
|
||||
|
||||
_.uniq(way.nodes).forEach(function (id) {
|
||||
_.uniq(way.nodes).forEach(function(id) {
|
||||
var node = graph.entity(id),
|
||||
start = projection(node.loc),
|
||||
end = projection.invert([start[0] + delta[0], start[1] + delta[1]]);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
|
||||
*/
|
||||
|
||||
iD.actions.Orthogonalize = function(wayId, projection) {
|
||||
var action = function(graph) {
|
||||
var way = graph.entity(wayId),
|
||||
nodes = graph.childNodes(way),
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
quad_nodes = [],
|
||||
best, i, j;
|
||||
|
||||
var score = squareness();
|
||||
for (i = 0; i < 1000; i++) {
|
||||
var motions = points.map(stepMap);
|
||||
for (j = 0; j < motions.length; j++) {
|
||||
points[j] = addPoints(points[j],motions[j]);
|
||||
}
|
||||
var newScore = squareness();
|
||||
if (newScore < score) {
|
||||
best = _.clone(points);
|
||||
score = newScore;
|
||||
}
|
||||
if (score < 1.0e-8) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
points = best;
|
||||
|
||||
for (i = 0; i < points.length - 1; i++) {
|
||||
graph = graph.replace(graph.entity(nodes[i].id).move(projection.invert(points[i])));
|
||||
}
|
||||
|
||||
return graph;
|
||||
|
||||
function stepMap(b, i, array) {
|
||||
var a = array[(i - 1 + array.length) % array.length],
|
||||
c = array[(i + 1) % array.length],
|
||||
p = subtractPoints(a, b),
|
||||
q = subtractPoints(c, b);
|
||||
|
||||
var scale = iD.geo.dist(p, [0, 0]) + iD.geo.dist(q, [0, 0]);
|
||||
p = normalizePoint(p, 1.0);
|
||||
q = normalizePoint(q, 1.0);
|
||||
|
||||
var dotp = p[0] *q[0] + p[1] *q[1];
|
||||
// nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
|
||||
if (dotp < -0.707106781186547) {
|
||||
dotp += 1.0;
|
||||
}
|
||||
|
||||
return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
|
||||
}
|
||||
|
||||
function squareness() {
|
||||
var g = 0.0;
|
||||
for (var i = 1; i < points.length - 1; i++) {
|
||||
var score = scoreOfPoints(points[i - 1], points[i], points[i + 1]);
|
||||
g += score;
|
||||
}
|
||||
var startScore = scoreOfPoints(points[points.length - 1], points[0], points[1]);
|
||||
var endScore = scoreOfPoints(points[points.length - 2], points[points.length - 1], points[0]);
|
||||
g += startScore;
|
||||
g += endScore;
|
||||
return g;
|
||||
}
|
||||
|
||||
function scoreOfPoints(a, b, c) {
|
||||
var p = subtractPoints(a, b),
|
||||
q = subtractPoints(c, b);
|
||||
|
||||
p = normalizePoint(p, 1.0);
|
||||
q = normalizePoint(q, 1.0);
|
||||
|
||||
var dotp = p[0] * q[0] + p[1] * q[1];
|
||||
// score is constructed so that +1, -1 and 0 are all scored 0, any other angle
|
||||
// is scored higher.
|
||||
return 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
|
||||
}
|
||||
|
||||
function subtractPoints(a, b) {
|
||||
return [a[0] - b[0], a[1] - b[1]];
|
||||
}
|
||||
|
||||
function addPoints(a, b) {
|
||||
return [a[0] + b[0], a[1] + b[1]];
|
||||
}
|
||||
|
||||
function normalizePoint(point, thickness) {
|
||||
var vector = [0, 0];
|
||||
var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
|
||||
if (length !== 0) {
|
||||
vector[0] = point[0] / length;
|
||||
vector[1] = point[1] / length;
|
||||
}
|
||||
|
||||
vector[0] *= thickness;
|
||||
vector[1] *= thickness;
|
||||
|
||||
return vector;
|
||||
}
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return graph.entity(wayId).isClosed();
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
@@ -27,7 +27,7 @@
|
||||
http://wiki.openstreetmap.org/wiki/Route#Members
|
||||
http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
|
||||
*/
|
||||
iD.actions.ReverseWay = function(wayId) {
|
||||
iD.actions.Reverse = function(wayId) {
|
||||
var replacements = [
|
||||
[/:right$/, ':left'], [/:left$/, ':right'],
|
||||
[/:forward$/, ':backward'], [/:backward$/, ':forward']
|
||||
@@ -62,8 +62,8 @@ iD.actions.ReverseWay = function(wayId) {
|
||||
tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
|
||||
}
|
||||
|
||||
graph.parentRelations(way).forEach(function (relation) {
|
||||
relation.members.forEach(function (member, index) {
|
||||
graph.parentRelations(way).forEach(function(relation) {
|
||||
relation.members.forEach(function(member, index) {
|
||||
if (member.id === way.id && (role = {forward: 'backward', backward: 'forward'}[member.role])) {
|
||||
relation = relation.updateMember({role: role}, index);
|
||||
graph = graph.replace(relation);
|
||||
@@ -0,0 +1,102 @@
|
||||
// Split a way at the given node.
|
||||
//
|
||||
// This is the inverse of `iD.actions.Join`.
|
||||
//
|
||||
// For testing convenience, accepts an ID to assign to the new way.
|
||||
// Normally, this will be undefined and the way will automatically
|
||||
// be assigned a new ID.
|
||||
//
|
||||
// Reference:
|
||||
// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
|
||||
//
|
||||
iD.actions.Split = function(nodeId, newWayId) {
|
||||
function candidateWays(graph) {
|
||||
var node = graph.entity(nodeId),
|
||||
parents = graph.parentWays(node);
|
||||
|
||||
return parents.filter(function(parent) {
|
||||
return parent.isClosed() ||
|
||||
(parent.first() !== nodeId &&
|
||||
parent.last() !== nodeId);
|
||||
});
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
var wayA = candidateWays(graph)[0],
|
||||
wayB = iD.Way({id: newWayId, tags: wayA.tags}),
|
||||
nodesA,
|
||||
nodesB,
|
||||
isArea = wayA.isArea();
|
||||
|
||||
if (wayA.isClosed()) {
|
||||
var nodes = wayA.nodes.slice(0, -1),
|
||||
idxA = _.indexOf(nodes, nodeId),
|
||||
idxB = idxA + Math.floor(nodes.length / 2);
|
||||
|
||||
if (idxB >= nodes.length) {
|
||||
idxB %= nodes.length;
|
||||
nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
|
||||
nodesB = nodes.slice(idxB, idxA + 1);
|
||||
} else {
|
||||
nodesA = nodes.slice(idxA, idxB + 1);
|
||||
nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
|
||||
}
|
||||
} else {
|
||||
var idx = _.indexOf(wayA.nodes, nodeId);
|
||||
nodesA = wayA.nodes.slice(0, idx + 1);
|
||||
nodesB = wayA.nodes.slice(idx);
|
||||
}
|
||||
|
||||
wayA = wayA.update({nodes: nodesA});
|
||||
wayB = wayB.update({nodes: nodesB});
|
||||
|
||||
graph = graph.replace(wayA);
|
||||
graph = graph.replace(wayB);
|
||||
|
||||
graph.parentRelations(wayA).forEach(function(relation) {
|
||||
if (relation.isRestriction()) {
|
||||
var via = relation.memberByRole('via');
|
||||
if (via && wayB.contains(via.id)) {
|
||||
relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
|
||||
graph = graph.replace(relation);
|
||||
}
|
||||
} else {
|
||||
var role = relation.memberById(wayA.id).role,
|
||||
last = wayB.last(),
|
||||
i = relation.memberById(wayA.id).index,
|
||||
j;
|
||||
|
||||
for (j = 0; j < relation.members.length; j++) {
|
||||
var entity = graph.entity(relation.members[j].id);
|
||||
if (entity && entity.type === 'way' && entity.contains(last)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
relation = relation.addMember({id: wayB.id, type: 'way', role: role}, i <= j ? i + 1 : i);
|
||||
graph = graph.replace(relation);
|
||||
}
|
||||
});
|
||||
|
||||
if (isArea) {
|
||||
var multipolygon = iD.Relation({
|
||||
tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
|
||||
members: [
|
||||
{id: wayA.id, role: 'outer', type: 'way'},
|
||||
{id: wayB.id, role: 'outer', type: 'way'}
|
||||
]});
|
||||
|
||||
graph = graph.replace(multipolygon);
|
||||
graph = graph.replace(wayA.update({tags: {}}));
|
||||
graph = graph.replace(wayB.update({tags: {}}));
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return candidateWays(graph).length === 1;
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
@@ -1,69 +0,0 @@
|
||||
// Split a way at the given node.
|
||||
//
|
||||
// For testing convenience, accepts an ID to assign to the new way.
|
||||
// Normally, this will be undefined and the way will automatically
|
||||
// be assigned a new ID.
|
||||
//
|
||||
// Reference:
|
||||
// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
|
||||
//
|
||||
iD.actions.SplitWay = function(nodeId, newWayId) {
|
||||
function candidateWays(graph) {
|
||||
var node = graph.entity(nodeId),
|
||||
parents = graph.parentWays(node);
|
||||
|
||||
return parents.filter(function (parent) {
|
||||
return parent.first() !== nodeId &&
|
||||
parent.last() !== nodeId;
|
||||
})
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
if (!action.enabled(graph))
|
||||
return graph;
|
||||
|
||||
var way = candidateWays(graph)[0],
|
||||
idx = _.indexOf(way.nodes, nodeId);
|
||||
|
||||
// Create a 'b' way that contains all of the tags in the second
|
||||
// half of this way
|
||||
var newWay = iD.Way({id: newWayId, tags: way.tags, nodes: way.nodes.slice(idx)});
|
||||
graph = graph.replace(newWay);
|
||||
|
||||
// Reduce the original way to only contain the first set of nodes
|
||||
graph = graph.replace(way.update({nodes: way.nodes.slice(0, idx + 1)}));
|
||||
|
||||
graph.parentRelations(way).forEach(function(relation) {
|
||||
if (relation.isRestriction()) {
|
||||
var via = relation.memberByRole('via');
|
||||
if (via && newWay.contains(via.id)) {
|
||||
relation = relation.updateMember({id: newWay.id}, relation.memberById(way.id).index);
|
||||
graph = graph.replace(relation);
|
||||
}
|
||||
} else {
|
||||
var role = relation.memberById(way.id).role,
|
||||
last = newWay.last(),
|
||||
i = relation.memberById(way.id).index,
|
||||
j;
|
||||
|
||||
for (j = 0; j < relation.members.length; j++) {
|
||||
var entity = graph.entity(relation.members[j].id);
|
||||
if (entity && entity.type === 'way' && entity.contains(last)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
relation = relation.addMember({id: newWay.id, type: 'way', role: role}, i <= j ? i + 1 : i);
|
||||
graph = graph.replace(relation);
|
||||
}
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return candidateWays(graph).length === 1;
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
@@ -1,18 +1,16 @@
|
||||
iD.behavior.AddWay = function(mode) {
|
||||
var map = mode.map,
|
||||
controller = mode.controller,
|
||||
event = d3.dispatch('start', 'startFromWay', 'startFromNode', 'startFromMidpoint'),
|
||||
draw = iD.behavior.Draw(map);
|
||||
iD.behavior.AddWay = function(context) {
|
||||
var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
|
||||
draw = iD.behavior.Draw(context);
|
||||
|
||||
var addWay = function(surface) {
|
||||
draw.on('click', event.start)
|
||||
.on('clickWay', event.startFromWay)
|
||||
.on('clickNode', event.startFromNode)
|
||||
.on('clickMidpoint', event.startFromMidpoint)
|
||||
.on('cancel', addWay.cancel)
|
||||
.on('finish', addWay.cancel);
|
||||
|
||||
map.fastEnable(false)
|
||||
context.map()
|
||||
.fastEnable(false)
|
||||
.minzoom(16)
|
||||
.dblclickEnable(false);
|
||||
|
||||
@@ -20,19 +18,20 @@ iD.behavior.AddWay = function(mode) {
|
||||
};
|
||||
|
||||
addWay.off = function(surface) {
|
||||
map.fastEnable(true)
|
||||
context.map()
|
||||
.fastEnable(true)
|
||||
.minzoom(0)
|
||||
.tail(false);
|
||||
|
||||
window.setTimeout(function() {
|
||||
map.dblclickEnable(true);
|
||||
context.map().dblclickEnable(true);
|
||||
}, 1000);
|
||||
|
||||
surface.call(draw.off);
|
||||
};
|
||||
|
||||
addWay.cancel = function() {
|
||||
controller.enter(iD.modes.Browse());
|
||||
context.enter(iD.modes.Browse(context));
|
||||
};
|
||||
|
||||
return d3.rebind(addWay, event, 'on');
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* Delegation is supported via the `delegate` function.
|
||||
|
||||
*/
|
||||
iD.behavior.drag = function () {
|
||||
iD.behavior.drag = function() {
|
||||
function d3_eventCancel() {
|
||||
d3.event.stopPropagation();
|
||||
d3.event.preventDefault();
|
||||
@@ -24,8 +24,7 @@ iD.behavior.drag = function () {
|
||||
origin = null,
|
||||
selector = '',
|
||||
filter = null,
|
||||
keybinding = d3.keybinding('drag'),
|
||||
event_, target;
|
||||
event_, target, surface;
|
||||
|
||||
event.of = function(thiz, argumentz) {
|
||||
return function(e1) {
|
||||
@@ -50,27 +49,26 @@ iD.behavior.drag = function () {
|
||||
moved = 0;
|
||||
|
||||
var w = d3.select(window)
|
||||
.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
|
||||
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
|
||||
.on(touchId !== null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
|
||||
.on(touchId !== null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
|
||||
|
||||
if (origin) {
|
||||
offset = origin.apply(target, arguments);
|
||||
offset = [ offset[0] - origin_[0], offset[1] - origin_[1] ];
|
||||
offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
|
||||
} else {
|
||||
offset = [ 0, 0 ];
|
||||
offset = [0, 0];
|
||||
}
|
||||
|
||||
if (touchId == null) d3_eventCancel();
|
||||
if (touchId === null) d3_eventCancel();
|
||||
|
||||
function point() {
|
||||
var p = target.parentNode;
|
||||
return touchId != null ? d3.touches(p).filter(function (p) {
|
||||
var p = target.parentNode || surface;
|
||||
return touchId !== null ? d3.touches(p).filter(function(p) {
|
||||
return p.identifier === touchId;
|
||||
})[0] : d3.mouse(p);
|
||||
}
|
||||
|
||||
function dragmove() {
|
||||
if (!target.parentNode) return dragend();
|
||||
|
||||
var p = point(),
|
||||
dx = p[0] - origin_[0],
|
||||
@@ -103,8 +101,8 @@ iD.behavior.drag = function () {
|
||||
if (d3.event.target === eventTarget) w.on("click.drag", click, true);
|
||||
}
|
||||
|
||||
w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
|
||||
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
|
||||
w.on(touchId !== null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
|
||||
.on(touchId !== null ? "touchend.drag-" + touchId : "mouseup.drag", null);
|
||||
}
|
||||
|
||||
function click() {
|
||||
@@ -137,9 +135,6 @@ iD.behavior.drag = function () {
|
||||
drag.off = function(selection) {
|
||||
selection.on("mousedown.drag" + selector, null)
|
||||
.on("touchstart.drag" + selector, null);
|
||||
keybinding
|
||||
.on('⌘+Z', null)
|
||||
.on('⌃+Z', null);
|
||||
};
|
||||
|
||||
drag.delegate = function(_) {
|
||||
@@ -174,11 +169,11 @@ iD.behavior.drag = function () {
|
||||
return drag;
|
||||
};
|
||||
|
||||
keybinding
|
||||
.on('⌘+Z', drag.cancel)
|
||||
.on('⌃+Z', drag.cancel);
|
||||
|
||||
d3.select(document).call(keybinding);
|
||||
drag.surface = function() {
|
||||
if (!arguments.length) return surface;
|
||||
surface = arguments[0];
|
||||
return drag;
|
||||
};
|
||||
|
||||
return d3.rebind(drag, event, "on");
|
||||
};
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
iD.behavior.DragMidpoint = function(mode) {
|
||||
var history = mode.history,
|
||||
projection = mode.map.projection;
|
||||
|
||||
var behavior = iD.behavior.drag()
|
||||
.delegate(".midpoint")
|
||||
.origin(function(d) {
|
||||
return projection(d.loc);
|
||||
})
|
||||
.on('start', function(d) {
|
||||
var node = iD.Node();
|
||||
|
||||
history.perform(iD.actions.AddMidpoint(d, node));
|
||||
|
||||
var vertex = d3.selectAll('.vertex')
|
||||
.filter(function(data) { return data.id === node.id; });
|
||||
|
||||
behavior.target(vertex.node(), vertex.datum());
|
||||
})
|
||||
.on('move', function(d) {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
history.replace(
|
||||
iD.actions.MoveNode(d.id, projection.invert(d3.event.point)));
|
||||
})
|
||||
.on('end', function() {
|
||||
history.replace(
|
||||
iD.actions.Noop(),
|
||||
t('operations.add.annotation.vertex'));
|
||||
});
|
||||
|
||||
return behavior;
|
||||
};
|
||||
@@ -1,10 +1,9 @@
|
||||
iD.behavior.DragNode = function(mode) {
|
||||
var history = mode.history,
|
||||
size = mode.map.size(),
|
||||
nudgeInterval,
|
||||
projection = mode.map.projection;
|
||||
iD.behavior.DragNode = function(context) {
|
||||
var nudgeInterval,
|
||||
wasMidpoint,
|
||||
cancelled;
|
||||
|
||||
function edge(point) {
|
||||
function edge(point, size) {
|
||||
var pad = [30, 100, 30, 100];
|
||||
if (point[0] > size[0] - pad[0]) return [-10, 0];
|
||||
else if (point[0] < pad[2]) return [10, 0];
|
||||
@@ -16,43 +15,150 @@ iD.behavior.DragNode = function(mode) {
|
||||
function startNudge(nudge) {
|
||||
if (nudgeInterval) window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = window.setInterval(function() {
|
||||
mode.map.pan(nudge).redraw();
|
||||
context.pan(nudge);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function stopNudge(nudge) {
|
||||
function stopNudge() {
|
||||
if (nudgeInterval) window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = null;
|
||||
}
|
||||
|
||||
function annotation(entity) {
|
||||
return t('operations.move.annotation.' + entity.geometry(mode.history.graph()));
|
||||
function moveAnnotation(entity) {
|
||||
return t('operations.move.annotation.' + entity.geometry(context.graph()));
|
||||
}
|
||||
|
||||
return iD.behavior.drag()
|
||||
.delegate(".node")
|
||||
.origin(function(entity) {
|
||||
return projection(entity.loc);
|
||||
})
|
||||
.on('start', function() {
|
||||
history.perform(
|
||||
function connectAnnotation(datum) {
|
||||
return t('operations.connect.annotation.' + datum.geometry(context.graph()));
|
||||
}
|
||||
|
||||
function origin(entity) {
|
||||
return context.projection(entity.loc);
|
||||
}
|
||||
|
||||
function start(entity) {
|
||||
cancelled = d3.event.sourceEvent.shiftKey;
|
||||
if (cancelled) return behavior.cancel();
|
||||
|
||||
context.history()
|
||||
.on('undone.drag-node', cancel);
|
||||
|
||||
wasMidpoint = entity.type === 'midpoint';
|
||||
if (wasMidpoint) {
|
||||
var midpoint = entity;
|
||||
entity = iD.Node();
|
||||
context.perform(iD.actions.AddMidpoint(midpoint, entity));
|
||||
|
||||
var vertex = context.surface()
|
||||
.selectAll('.vertex')
|
||||
.filter(function(d) { return d.id === entity.id; });
|
||||
behavior.target(vertex.node(), entity);
|
||||
|
||||
} else {
|
||||
context.perform(
|
||||
iD.actions.Noop());
|
||||
})
|
||||
.on('move', function(entity) {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
}
|
||||
|
||||
var nudge = edge(d3.event.point);
|
||||
if (nudge) startNudge(nudge);
|
||||
else stopNudge();
|
||||
var activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
|
||||
activeIDs.push(entity.id);
|
||||
|
||||
history.replace(
|
||||
iD.actions.MoveNode(entity.id, projection.invert(d3.event.point)),
|
||||
annotation(entity));
|
||||
})
|
||||
.on('end', function(entity) {
|
||||
stopNudge();
|
||||
history.replace(
|
||||
context.surface()
|
||||
.classed('behavior-drag-node', true)
|
||||
.selectAll('.node, .way')
|
||||
.filter(function(d) { return activeIDs.indexOf(d.id) >= 0; })
|
||||
.classed('active', true);
|
||||
}
|
||||
|
||||
function datum() {
|
||||
if (d3.event.sourceEvent.altKey) {
|
||||
return {};
|
||||
} else {
|
||||
return d3.event.sourceEvent.target.__data__ || {};
|
||||
}
|
||||
}
|
||||
|
||||
function move(entity) {
|
||||
if (cancelled) return;
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
|
||||
var nudge = edge(d3.event.point, context.map().size());
|
||||
if (nudge) startNudge(nudge);
|
||||
else stopNudge();
|
||||
|
||||
var loc = context.map().mouseCoordinates();
|
||||
|
||||
var d = datum();
|
||||
if (d.type === 'node' && d.id !== entity.id) {
|
||||
loc = d.loc;
|
||||
} else if (d.type === 'way') {
|
||||
var point = d3.mouse(context.surface().node()),
|
||||
index = iD.geo.chooseIndex(d, point, context);
|
||||
if (iD.geo.dist(point, context.projection(index.loc)) < 10) {
|
||||
loc = index.loc;
|
||||
}
|
||||
}
|
||||
|
||||
context.replace(iD.actions.MoveNode(entity.id, loc));
|
||||
}
|
||||
|
||||
function end(entity) {
|
||||
if (cancelled) return;
|
||||
off();
|
||||
|
||||
var d = datum();
|
||||
if (d.type === 'way') {
|
||||
var point = d3.mouse(context.surface().node()),
|
||||
choice = iD.geo.chooseIndex(d, point, context);
|
||||
if (iD.geo.dist(point, context.projection(choice.loc)) < 10) {
|
||||
context.replace(
|
||||
iD.actions.MoveNode(entity.id, choice.loc),
|
||||
iD.actions.AddVertex(d.id, entity.id, choice.index),
|
||||
connectAnnotation(d));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.type === 'node' && d.id !== entity.id) {
|
||||
context.replace(
|
||||
iD.actions.Connect([entity.id, d.id]),
|
||||
connectAnnotation(d));
|
||||
|
||||
} else if (wasMidpoint) {
|
||||
context.replace(
|
||||
iD.actions.Noop(),
|
||||
annotation(entity));
|
||||
});
|
||||
t('operations.add.annotation.vertex'));
|
||||
|
||||
} else {
|
||||
context.replace(
|
||||
iD.actions.Noop(),
|
||||
moveAnnotation(entity));
|
||||
}
|
||||
}
|
||||
|
||||
function off() {
|
||||
context.history()
|
||||
.on('undone.drag_node', null);
|
||||
|
||||
context.surface()
|
||||
.classed('behavior-drag-node', false)
|
||||
.selectAll('.active')
|
||||
.classed('active', false);
|
||||
|
||||
stopNudge();
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
off();
|
||||
behavior.cancel();
|
||||
}
|
||||
|
||||
var behavior = iD.behavior.drag()
|
||||
.delegate("g.node, g.point, g.midpoint")
|
||||
.surface(context.surface().node())
|
||||
.origin(origin)
|
||||
.on('start', start)
|
||||
.on('move', move)
|
||||
.on('end', end);
|
||||
|
||||
return behavior;
|
||||
};
|
||||
|
||||
@@ -1,56 +1,75 @@
|
||||
iD.behavior.Draw = function(map) {
|
||||
var event = d3.dispatch('move', 'click', 'clickWay', 'clickNode', 'clickMidpoint', 'undo', 'cancel', 'finish'),
|
||||
iD.behavior.Draw = function(context) {
|
||||
var event = d3.dispatch('move', 'click', 'clickWay',
|
||||
'clickNode', 'undo', 'cancel', 'finish'),
|
||||
keybinding = d3.keybinding('draw'),
|
||||
down, surface, hover;
|
||||
hover = iD.behavior.Hover(),
|
||||
closeTolerance = 4,
|
||||
tolerance = 12;
|
||||
|
||||
function datum() {
|
||||
if (d3.event.altKey) {
|
||||
return {};
|
||||
} else {
|
||||
return d3.event.target.__data__ || {};
|
||||
}
|
||||
if (d3.event.altKey) return {};
|
||||
else return d3.event.target.__data__ || {};
|
||||
}
|
||||
|
||||
function mousedown() {
|
||||
down = true;
|
||||
}
|
||||
|
||||
function mouseup() {
|
||||
down = false;
|
||||
function point() {
|
||||
var p = target.node().parentNode;
|
||||
return touchId !== null ? d3.touches(p).filter(function(p) {
|
||||
return p.identifier === touchId;
|
||||
})[0] : d3.mouse(p);
|
||||
}
|
||||
|
||||
var target = d3.select(this),
|
||||
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
|
||||
time = +new Date(),
|
||||
pos = point();
|
||||
|
||||
target.on('mousemove.draw', null);
|
||||
|
||||
d3.select(window).on('mouseup.draw', function() {
|
||||
target.on('mousemove.draw', mousemove);
|
||||
if (iD.geo.dist(pos, point()) < closeTolerance ||
|
||||
(iD.geo.dist(pos, point()) < tolerance &&
|
||||
(+new Date() - time) < 500)) {
|
||||
click();
|
||||
}
|
||||
if (target.node() === d3.event.target) {
|
||||
d3.select(window).on('click.draw', function() {
|
||||
d3.select(window).on('click.draw', null);
|
||||
d3.event.stopPropagation();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mousemove() {
|
||||
if (!down) {
|
||||
event.move(datum());
|
||||
}
|
||||
event.move(datum());
|
||||
}
|
||||
|
||||
function click() {
|
||||
var d = datum();
|
||||
if (d.type === 'way') {
|
||||
var choice = iD.geo.chooseIndex(d, d3.mouse(map.surface.node()), map);
|
||||
var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context);
|
||||
event.clickWay(d, choice.loc, choice.index);
|
||||
|
||||
} else if (d.type === 'node') {
|
||||
event.clickNode(d);
|
||||
|
||||
} else if (d.type === 'midpoint') {
|
||||
event.clickMidpoint(d);
|
||||
|
||||
} else {
|
||||
event.click(map.mouseCoordinates());
|
||||
event.click(context.map().mouseCoordinates());
|
||||
}
|
||||
}
|
||||
|
||||
function keydown() {
|
||||
if (d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
|
||||
surface.call(hover.off);
|
||||
context.uninstall(hover);
|
||||
}
|
||||
}
|
||||
|
||||
function keyup() {
|
||||
if (d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
|
||||
surface.call(hover);
|
||||
context.install(hover);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +89,7 @@ iD.behavior.Draw = function(map) {
|
||||
}
|
||||
|
||||
function draw(selection) {
|
||||
surface = selection;
|
||||
hover = iD.behavior.Hover();
|
||||
context.install(hover);
|
||||
|
||||
keybinding
|
||||
.on('⌫', backspace)
|
||||
@@ -81,10 +99,7 @@ iD.behavior.Draw = function(map) {
|
||||
|
||||
selection
|
||||
.on('mousedown.draw', mousedown)
|
||||
.on('mouseup.draw', mouseup)
|
||||
.on('mousemove.draw', mousemove)
|
||||
.on('click.draw', click)
|
||||
.call(hover);
|
||||
.on('mousemove.draw', mousemove);
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding)
|
||||
@@ -95,12 +110,13 @@ iD.behavior.Draw = function(map) {
|
||||
}
|
||||
|
||||
draw.off = function(selection) {
|
||||
context.uninstall(hover);
|
||||
|
||||
selection
|
||||
.on('mousedown.draw', null)
|
||||
.on('mouseup.draw', null)
|
||||
.on('mousemove.draw', null)
|
||||
.on('click.draw', null)
|
||||
.call(hover.off);
|
||||
.on('mousemove.draw', null);
|
||||
|
||||
d3.select(window).on('mouseup.draw', null);
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding.off)
|
||||
|
||||
@@ -1,35 +1,58 @@
|
||||
iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller,
|
||||
way = history.graph().entity(wayId),
|
||||
iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
|
||||
var way = context.entity(wayId),
|
||||
isArea = way.geometry() === 'area',
|
||||
finished = false,
|
||||
annotation = t((way.isDegenerate() ?
|
||||
'operations.start.annotation.' :
|
||||
'operations.continue.annotation.') + way.geometry(history.graph())),
|
||||
draw = iD.behavior.Draw(map);
|
||||
'operations.continue.annotation.') + context.geometry(wayId)),
|
||||
draw = iD.behavior.Draw(context);
|
||||
|
||||
var node = iD.Node({loc: map.mouseCoordinates()}),
|
||||
nodeId = node.id;
|
||||
var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
|
||||
start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
|
||||
end = iD.Node({loc: context.map().mouseCoordinates()}),
|
||||
segment = iD.Way({
|
||||
nodes: [start.id, end.id],
|
||||
tags: _.clone(way.tags)
|
||||
});
|
||||
|
||||
history[way.isDegenerate() ? 'replace' : 'perform'](
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddVertex(wayId, node.id, index));
|
||||
var f = context[way.isDegenerate() ? 'replace' : 'perform'];
|
||||
if (isArea) {
|
||||
f(iD.actions.AddEntity(end),
|
||||
iD.actions.AddVertex(wayId, end.id, index));
|
||||
} else {
|
||||
f(iD.actions.AddEntity(start),
|
||||
iD.actions.AddEntity(end),
|
||||
iD.actions.AddEntity(segment));
|
||||
}
|
||||
|
||||
function move(datum) {
|
||||
var loc = map.mouseCoordinates();
|
||||
var loc = context.map().mouseCoordinates();
|
||||
|
||||
if (datum.type === 'node' || datum.type === 'midpoint') {
|
||||
if (datum.id === end.id || datum.id === segment.id) {
|
||||
context.surface().selectAll('.way, .node')
|
||||
.filter(function(d) {
|
||||
return d.id === end.id || d.id === segment.id;
|
||||
})
|
||||
.classed('active', true);
|
||||
} else if (datum.type === 'node') {
|
||||
loc = datum.loc;
|
||||
} else if (datum.type === 'way') {
|
||||
loc = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map).loc;
|
||||
loc = iD.geo.chooseIndex(datum, d3.mouse(context.surface().node()), context).loc;
|
||||
}
|
||||
|
||||
history.replace(iD.actions.MoveNode(nodeId, loc));
|
||||
context.replace(iD.actions.MoveNode(end.id, loc));
|
||||
}
|
||||
|
||||
function undone() {
|
||||
controller.enter(iD.modes.Browse());
|
||||
context.enter(iD.modes.Browse(context));
|
||||
}
|
||||
|
||||
function lineActives(d) {
|
||||
return d.id === segment.id || d.id === start.id || d.id === end.id;
|
||||
}
|
||||
|
||||
function areaActives(d) {
|
||||
return d.id === wayId || d.id === end.id;
|
||||
}
|
||||
|
||||
var drawWay = function(surface) {
|
||||
@@ -37,123 +60,122 @@ iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) {
|
||||
.on('click', drawWay.add)
|
||||
.on('clickWay', drawWay.addWay)
|
||||
.on('clickNode', drawWay.addNode)
|
||||
.on('clickMidpoint', drawWay.addMidpoint)
|
||||
.on('undo', history.undo)
|
||||
.on('undo', context.undo)
|
||||
.on('cancel', drawWay.cancel)
|
||||
.on('finish', drawWay.finish);
|
||||
|
||||
map.fastEnable(false)
|
||||
context.map()
|
||||
.fastEnable(false)
|
||||
.minzoom(16)
|
||||
.dblclickEnable(false);
|
||||
|
||||
surface.call(draw)
|
||||
.selectAll('.way, .node')
|
||||
.filter(function (d) { return d.id === wayId || d.id === nodeId; })
|
||||
.filter(isArea ? areaActives : lineActives)
|
||||
.classed('active', true);
|
||||
|
||||
history.on('undone.draw', undone);
|
||||
context.history()
|
||||
.on('undone.draw', undone);
|
||||
};
|
||||
|
||||
drawWay.off = function(surface) {
|
||||
if (!finished)
|
||||
history.pop();
|
||||
context.pop();
|
||||
|
||||
map.fastEnable(true)
|
||||
context.map()
|
||||
.fastEnable(true)
|
||||
.minzoom(0)
|
||||
.tail(false);
|
||||
|
||||
window.setTimeout(function() {
|
||||
map.dblclickEnable(true);
|
||||
context.map().dblclickEnable(true);
|
||||
}, 1000);
|
||||
|
||||
surface.call(draw.off)
|
||||
.selectAll('.way, .node')
|
||||
.classed('active', false);
|
||||
|
||||
history.on('undone.draw', null);
|
||||
context.history()
|
||||
.on('undone.draw', null);
|
||||
};
|
||||
|
||||
function ReplaceTemporaryNode(newNode) {
|
||||
return function(graph) {
|
||||
return graph
|
||||
.replace(way.removeNode(nodeId).addNode(newNode.id, index))
|
||||
.remove(node);
|
||||
}
|
||||
if (isArea) {
|
||||
return graph
|
||||
.replace(way.removeNode(end.id).addNode(newNode.id, index))
|
||||
.remove(end);
|
||||
|
||||
} else {
|
||||
return graph
|
||||
.replace(graph.entity(wayId).addNode(newNode.id, index))
|
||||
.remove(end)
|
||||
.remove(segment)
|
||||
.remove(start);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Accept the current position of the temporary node and continue drawing.
|
||||
drawWay.add = function(loc) {
|
||||
var newNode = iD.Node({loc: loc});
|
||||
|
||||
history.replace(
|
||||
context.replace(
|
||||
iD.actions.AddEntity(newNode),
|
||||
ReplaceTemporaryNode(newNode),
|
||||
annotation);
|
||||
|
||||
finished = true;
|
||||
controller.enter(mode);
|
||||
context.enter(mode);
|
||||
};
|
||||
|
||||
// Connect the way to an existing way.
|
||||
drawWay.addWay = function(way, loc, wayIndex) {
|
||||
var newNode = iD.Node({loc: loc});
|
||||
|
||||
history.perform(
|
||||
context.perform(
|
||||
iD.actions.AddEntity(newNode),
|
||||
iD.actions.AddVertex(way.id, newNode.id, wayIndex),
|
||||
ReplaceTemporaryNode(newNode),
|
||||
annotation);
|
||||
|
||||
finished = true;
|
||||
controller.enter(mode);
|
||||
context.enter(mode);
|
||||
};
|
||||
|
||||
// Connect the way to an existing node and continue drawing.
|
||||
drawWay.addNode = function(node) {
|
||||
history.perform(
|
||||
context.perform(
|
||||
ReplaceTemporaryNode(node),
|
||||
annotation);
|
||||
|
||||
finished = true;
|
||||
controller.enter(mode);
|
||||
};
|
||||
|
||||
// Add a midpoint, connect the way to it, and continue drawing.
|
||||
drawWay.addMidpoint = function(midpoint) {
|
||||
var node = iD.Node();
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddMidpoint(midpoint, node),
|
||||
ReplaceTemporaryNode(node),
|
||||
annotation);
|
||||
|
||||
finished = true;
|
||||
controller.enter(mode);
|
||||
context.enter(mode);
|
||||
};
|
||||
|
||||
// Finish the draw operation, removing the temporary node. If the way has enough
|
||||
// nodes to be valid, it's selected. Otherwise, return to browse mode.
|
||||
drawWay.finish = function() {
|
||||
history.pop();
|
||||
context.pop();
|
||||
finished = true;
|
||||
|
||||
var way = history.graph().entity(wayId);
|
||||
var way = context.entity(wayId);
|
||||
if (way) {
|
||||
controller.enter(iD.modes.Select([way.id], true));
|
||||
context.enter(iD.modes.Select(context, [way.id], true));
|
||||
} else {
|
||||
controller.enter(iD.modes.Browse());
|
||||
context.enter(iD.modes.Browse(context));
|
||||
}
|
||||
};
|
||||
|
||||
// Cancel the draw operation and return to browse, deleting everything drawn.
|
||||
drawWay.cancel = function() {
|
||||
history.perform(
|
||||
context.perform(
|
||||
d3.functor(baseGraph),
|
||||
t('operations.cancel_draw.annotation'));
|
||||
|
||||
finished = true;
|
||||
controller.enter(iD.modes.Browse());
|
||||
context.enter(iD.modes.Browse(context));
|
||||
};
|
||||
|
||||
return d3.rebind(drawWay, event, 'on');
|
||||
return drawWay;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.behavior.Hash = function(controller, map) {
|
||||
iD.behavior.Hash = function(context) {
|
||||
var s0 = null, // cached location.hash
|
||||
lat = 90 - 1e-8; // allowable latitude range
|
||||
|
||||
@@ -27,13 +27,13 @@ iD.behavior.Hash = function(controller, map) {
|
||||
};
|
||||
|
||||
var move = _.throttle(function() {
|
||||
var s1 = formatter(map);
|
||||
var s1 = formatter(context.map());
|
||||
if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
|
||||
}, 100);
|
||||
}, 500);
|
||||
|
||||
function hashchange() {
|
||||
if (location.hash === s0) return; // ignore spurious hashchange events
|
||||
if (parser(map, (s0 = location.hash).substring(1))) {
|
||||
if (parser(context.map(), (s0 = location.hash).substring(1))) {
|
||||
move(); // replace bogus hash
|
||||
}
|
||||
}
|
||||
@@ -42,40 +42,39 @@ iD.behavior.Hash = function(controller, map) {
|
||||
// do so before any features are loaded. thus wait for the feature to
|
||||
// be loaded and then select
|
||||
function willselect(id) {
|
||||
map.on('drawn.hash', function() {
|
||||
var entity = map.history().graph().entity(id);
|
||||
if (entity === undefined) return;
|
||||
else selectoff();
|
||||
controller.enter(iD.modes.Select([entity.id]));
|
||||
map.on('drawn.hash', null);
|
||||
context.map().on('drawn.hash', function() {
|
||||
if (!context.entity(id)) return;
|
||||
selectoff();
|
||||
context.enter(iD.modes.Select(context, [id]));
|
||||
});
|
||||
controller.on('enter.hash', function() {
|
||||
if (controller.mode.id !== 'browse') selectoff();
|
||||
|
||||
context.on('enter.hash', function() {
|
||||
if (context.mode().id !== 'browse') selectoff();
|
||||
});
|
||||
}
|
||||
|
||||
function selectoff() {
|
||||
map.on('drawn.hash', null);
|
||||
context.map().on('drawn.hash', null);
|
||||
}
|
||||
|
||||
function hash() {
|
||||
map.on('move.hash', move);
|
||||
context.map()
|
||||
.on('move.hash', move);
|
||||
|
||||
d3.select(window)
|
||||
.on('hashchange.hash', hashchange);
|
||||
|
||||
if (location.hash) {
|
||||
var q = iD.util.stringQs(location.hash.substring(1));
|
||||
if (q.id) {
|
||||
willselect(q.id);
|
||||
}
|
||||
if (q.id) willselect(q.id);
|
||||
hashchange();
|
||||
hash.hadHash = true;
|
||||
if (q.map) hash.hadHash = true;
|
||||
}
|
||||
}
|
||||
|
||||
hash.off = function() {
|
||||
map.on('move.hash', null);
|
||||
context.map()
|
||||
.on('move.hash', null);
|
||||
|
||||
d3.select(window)
|
||||
.on('hashchange.hash', null);
|
||||
|
||||
@@ -7,20 +7,22 @@
|
||||
Only one of these elements can have the :hover pseudo-class, but all of them will
|
||||
have the .hover class.
|
||||
*/
|
||||
iD.behavior.Hover = function () {
|
||||
iD.behavior.Hover = function() {
|
||||
var hover = function(selection) {
|
||||
selection.classed('behavior-hover', true);
|
||||
|
||||
selection.on('mouseover.hover', function () {
|
||||
function mouseover() {
|
||||
var datum = d3.event.target.__data__;
|
||||
if (datum) {
|
||||
selection.selectAll('*')
|
||||
.filter(function (d) { return d === datum; })
|
||||
.filter(function(d) { return d === datum; })
|
||||
.classed('hover', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
selection.on('mouseout.hover', function () {
|
||||
selection.on('mouseover.hover', mouseover);
|
||||
|
||||
selection.on('mouseout.hover', function() {
|
||||
selection.selectAll('.hover')
|
||||
.classed('hover', false);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
iD.behavior.Lasso = function(context) {
|
||||
|
||||
var behavior = function(selection) {
|
||||
|
||||
var timeout = null,
|
||||
// the position of the first mousedown
|
||||
pos = null,
|
||||
lasso;
|
||||
|
||||
function mousedown() {
|
||||
if (d3.event.shiftKey === true) {
|
||||
|
||||
pos = [d3.event.clientX, d3.event.clientY];
|
||||
|
||||
lasso = iD.ui.Lasso().a(d3.mouse(context.surface().node()));
|
||||
|
||||
context.surface().call(lasso);
|
||||
|
||||
selection
|
||||
.on('mousemove.lasso', mousemove)
|
||||
.on('mouseup.lasso', mouseup);
|
||||
|
||||
d3.event.stopPropagation();
|
||||
d3.event.preventDefault();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function mousemove() {
|
||||
lasso.b(d3.mouse(context.surface().node()));
|
||||
}
|
||||
|
||||
function normalize(a, b) {
|
||||
return [
|
||||
[Math.min(a[0], b[0]), Math.min(a[1], b[1])],
|
||||
[Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
|
||||
}
|
||||
|
||||
function mouseup() {
|
||||
|
||||
var extent = iD.geo.Extent(
|
||||
normalize(context.projection.invert(lasso.a()),
|
||||
context.projection.invert(lasso.b())));
|
||||
|
||||
lasso.close();
|
||||
|
||||
selection
|
||||
.on('mousemove.lasso', null)
|
||||
.on('mouseup.lasso', null);
|
||||
|
||||
if (d3.event.clientX !== pos[0] || d3.event.clientY !== pos[1]) {
|
||||
var selected = context.graph().intersects(extent).filter(function (entity) {
|
||||
return entity.type === 'node';
|
||||
});
|
||||
|
||||
if (selected.length) {
|
||||
context.enter(iD.modes.Select(context, _.pluck(selected, 'id')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selection
|
||||
.on('mousedown.lasso', mousedown);
|
||||
};
|
||||
|
||||
behavior.off = function(selection) {
|
||||
selection.on('mousedown.lasso', null);
|
||||
};
|
||||
|
||||
return behavior;
|
||||
};
|
||||
@@ -1,16 +1,29 @@
|
||||
iD.behavior.Select = function(mode) {
|
||||
var controller = mode.controller;
|
||||
|
||||
function click() {
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum instanceof iD.Entity) {
|
||||
controller.enter(iD.modes.Select([datum.id]));
|
||||
} else {
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
}
|
||||
|
||||
iD.behavior.Select = function(context) {
|
||||
var behavior = function(selection) {
|
||||
function click() {
|
||||
var datum = d3.event.target.__data__;
|
||||
if (!(datum instanceof iD.Entity)) {
|
||||
if (!d3.event.shiftKey)
|
||||
context.enter(iD.modes.Browse(context));
|
||||
|
||||
} else if (!d3.event.shiftKey) {
|
||||
// Avoid re-entering Select mode with same entity.
|
||||
if (context.selection().length !== 1 || context.selection()[0] !== datum.id) {
|
||||
context.enter(iD.modes.Select(context, [datum.id]));
|
||||
} else {
|
||||
context.mode().reselect();
|
||||
}
|
||||
} else if (context.selection().indexOf(datum.id) >= 0) {
|
||||
var selection = _.without(context.selection(), datum.id);
|
||||
context.enter(selection.length ?
|
||||
iD.modes.Select(context, selection) :
|
||||
iD.modes.Browse(context));
|
||||
|
||||
} else {
|
||||
context.enter(iD.modes.Select(context, context.selection().concat([datum.id])));
|
||||
}
|
||||
}
|
||||
|
||||
selection.on('click.select', click);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.Connection = function() {
|
||||
iD.Connection = function(context) {
|
||||
|
||||
var event = d3.dispatch('auth', 'load'),
|
||||
url = 'http://www.openstreetmap.org',
|
||||
@@ -9,7 +9,13 @@ iD.Connection = function() {
|
||||
keys,
|
||||
inflight = {},
|
||||
loadedTiles = {},
|
||||
oauth = iD.OAuth().url(url);
|
||||
oauth = iD.OAuth(context).url(url),
|
||||
ndStr = 'nd',
|
||||
tagStr = 'tag',
|
||||
memberStr = 'member',
|
||||
nodeStr = 'node',
|
||||
wayStr = 'way',
|
||||
relationStr = 'relation';
|
||||
|
||||
function changesetUrl(changesetId) {
|
||||
return url + '/browse/changeset/' + changesetId;
|
||||
@@ -36,92 +42,99 @@ iD.Connection = function() {
|
||||
}
|
||||
|
||||
function getNodes(obj) {
|
||||
var nelems = obj.getElementsByTagName('nd'), nodes = new Array(nelems.length);
|
||||
for (var i = 0, l = nelems.length; i < l; i++) {
|
||||
nodes[i] = 'n' + nelems[i].attributes.ref.nodeValue;
|
||||
var elems = obj.getElementsByTagName(ndStr),
|
||||
nodes = new Array(elems.length);
|
||||
for (var i = 0, l = elems.length; i < l; i++) {
|
||||
nodes[i] = 'n' + elems[i].attributes.ref.nodeValue;
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function getTags(obj) {
|
||||
var tags = {}, tagelems = obj.getElementsByTagName('tag');
|
||||
for (var i = 0, l = tagelems.length; i < l; i++) {
|
||||
var item = tagelems[i];
|
||||
tags[item.attributes.k.nodeValue] = item.attributes.v.nodeValue;
|
||||
var elems = obj.getElementsByTagName(tagStr),
|
||||
tags = {};
|
||||
for (var i = 0, l = elems.length; i < l; i++) {
|
||||
var attrs = elems[i].attributes;
|
||||
tags[attrs.k.nodeValue] = attrs.v.nodeValue;
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
function getMembers(obj) {
|
||||
var elems = obj.getElementsByTagName('member'),
|
||||
var elems = obj.getElementsByTagName(memberStr),
|
||||
members = new Array(elems.length);
|
||||
|
||||
for (var i = 0, l = elems.length; i < l; i++) {
|
||||
var attrs = elems[i].attributes;
|
||||
members[i] = {
|
||||
id: elems[i].attributes.type.nodeValue[0] + elems[i].attributes.ref.nodeValue,
|
||||
type: elems[i].attributes.type.nodeValue,
|
||||
role: elems[i].attributes.role.nodeValue
|
||||
id: attrs.type.nodeValue[0] + attrs.ref.nodeValue,
|
||||
type: attrs.type.nodeValue,
|
||||
role: attrs.role.nodeValue
|
||||
};
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
function nodeData(obj) {
|
||||
var o = { type: 'node', tags: getTags(obj) };
|
||||
for (var i = 0, l = obj.attributes.length; i < l; i++) {
|
||||
o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
|
||||
}
|
||||
if (o.lon && o.lat) {
|
||||
o.loc = [parseFloat(o.lon), parseFloat(o.lat)];
|
||||
delete o.lon; delete o.lat;
|
||||
}
|
||||
o.id = iD.Entity.id.fromOSM('node', o.id);
|
||||
return new iD.Node(o);
|
||||
}
|
||||
var parsers = {
|
||||
node: function nodeData(obj) {
|
||||
var attrs = obj.attributes;
|
||||
return new iD.Node({
|
||||
id: iD.Entity.id.fromOSM(nodeStr, attrs.id.nodeValue),
|
||||
loc: [parseFloat(attrs.lon.nodeValue), parseFloat(attrs.lat.nodeValue)],
|
||||
version: attrs.version.nodeValue,
|
||||
changeset: attrs.changeset.nodeValue,
|
||||
user: attrs.user.nodeValue,
|
||||
uid: attrs.uid.nodeValue,
|
||||
visible: attrs.visible.nodeValue,
|
||||
timestamp: attrs.timestamp.nodeValue,
|
||||
tags: getTags(obj)
|
||||
});
|
||||
},
|
||||
|
||||
function wayData(obj) {
|
||||
var o = { type: 'way', nodes: getNodes(obj),
|
||||
tags: getTags(obj)
|
||||
};
|
||||
for (var i = 0, l = obj.attributes.length; i < l; i++) {
|
||||
o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
|
||||
}
|
||||
o.id = iD.Entity.id.fromOSM('way', o.id);
|
||||
return new iD.Way(o);
|
||||
}
|
||||
way: function wayData(obj) {
|
||||
var attrs = obj.attributes;
|
||||
return new iD.Way({
|
||||
id: iD.Entity.id.fromOSM(wayStr, attrs.id.nodeValue),
|
||||
version: attrs.version.nodeValue,
|
||||
changeset: attrs.changeset.nodeValue,
|
||||
user: attrs.user.nodeValue,
|
||||
uid: attrs.uid.nodeValue,
|
||||
visible: attrs.visible.nodeValue,
|
||||
timestamp: attrs.timestamp.nodeValue,
|
||||
tags: getTags(obj),
|
||||
nodes: getNodes(obj)
|
||||
});
|
||||
},
|
||||
|
||||
function relationData(obj) {
|
||||
var o = {
|
||||
type: 'relation', members: getMembers(obj),
|
||||
tags: getTags(obj)
|
||||
};
|
||||
for (var i = 0, l = obj.attributes.length; i < l; i++) {
|
||||
o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
|
||||
relation: function relationData(obj) {
|
||||
var attrs = obj.attributes;
|
||||
return new iD.Relation({
|
||||
id: iD.Entity.id.fromOSM(relationStr, attrs.id.nodeValue),
|
||||
version: attrs.version.nodeValue,
|
||||
changeset: attrs.changeset.nodeValue,
|
||||
user: attrs.user.nodeValue,
|
||||
uid: attrs.uid.nodeValue,
|
||||
visible: attrs.visible.nodeValue,
|
||||
timestamp: attrs.timestamp.nodeValue,
|
||||
tags: getTags(obj),
|
||||
members: getMembers(obj)
|
||||
});
|
||||
}
|
||||
o.id = iD.Entity.id.fromOSM('relation', o.id);
|
||||
return new iD.Relation(o);
|
||||
}
|
||||
};
|
||||
|
||||
function parse(dom) {
|
||||
if (!dom || !dom.childNodes) return new Error('Bad request');
|
||||
var root = dom.childNodes[0];
|
||||
var entities = {};
|
||||
|
||||
var root = dom.childNodes[0],
|
||||
children = root.childNodes,
|
||||
entities = {};
|
||||
|
||||
var i, o, l;
|
||||
for (i = 0, l = root.childNodes.length; i < l; i++) {
|
||||
switch(root.childNodes[i].nodeName) {
|
||||
case 'node':
|
||||
o = nodeData(root.childNodes[i]);
|
||||
entities[o.id] = o;
|
||||
break;
|
||||
case 'way':
|
||||
o = wayData(root.childNodes[i]);
|
||||
entities[o.id] = o;
|
||||
break;
|
||||
case 'relation':
|
||||
o = relationData(root.childNodes[i]);
|
||||
entities[o.id] = o;
|
||||
break;
|
||||
for (i = 0, l = children.length; i < l; i++) {
|
||||
var child = children[i],
|
||||
parser = parsers[child.nodeName];
|
||||
if (parser) {
|
||||
o = parser(child);
|
||||
entities[o.id] = o;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,21 +200,21 @@ iD.Connection = function() {
|
||||
content: JXON.stringify(connection.changesetJXON({
|
||||
imagery_used: imagery_used.join(';'),
|
||||
comment: comment,
|
||||
created_by: 'iD ' + (version || '')
|
||||
created_by: 'iD ' + iD.version
|
||||
}))
|
||||
}, function (err, changeset_id) {
|
||||
}, function(err, changeset_id) {
|
||||
if (err) return callback(err);
|
||||
oauth.xhr({
|
||||
method: 'POST',
|
||||
path: '/api/0.6/changeset/' + changeset_id + '/upload',
|
||||
options: { header: { 'Content-Type': 'text/xml' } },
|
||||
content: JXON.stringify(connection.osmChangeJXON(user.id, changeset_id, changes))
|
||||
}, function (err) {
|
||||
}, function(err) {
|
||||
if (err) return callback(err);
|
||||
oauth.xhr({
|
||||
method: 'PUT',
|
||||
path: '/api/0.6/changeset/' + changeset_id + '/close'
|
||||
}, function (err) {
|
||||
}, function(err) {
|
||||
callback(err, changeset_id);
|
||||
});
|
||||
});
|
||||
@@ -210,13 +223,14 @@ iD.Connection = function() {
|
||||
|
||||
function userDetails(callback) {
|
||||
function done(err, user_details) {
|
||||
if (err) return callback(err);
|
||||
var u = user_details.getElementsByTagName('user')[0],
|
||||
img = u.getElementsByTagName('img'),
|
||||
image_url = '';
|
||||
if (img && img[0].getAttribute('href')) {
|
||||
image_url = img[0].getAttribute('href');
|
||||
}
|
||||
callback(connection.user({
|
||||
callback(undefined, connection.user({
|
||||
display_name: u.attributes.display_name.nodeValue,
|
||||
image_url: image_url,
|
||||
id: u.attributes.id.nodeValue
|
||||
@@ -329,12 +343,6 @@ iD.Connection = function() {
|
||||
return oauth.authenticate(done);
|
||||
};
|
||||
|
||||
connection.version = function(_) {
|
||||
if (!arguments.length) return version;
|
||||
version = _;
|
||||
return connection;
|
||||
};
|
||||
|
||||
connection.bboxFromAPI = bboxFromAPI;
|
||||
connection.changesetUrl = changesetUrl;
|
||||
connection.loadFromURL = loadFromURL;
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// A controller holds a single action at a time and calls `.enter` and `.exit`
|
||||
// to bind and unbind actions.
|
||||
iD.Controller = function(map, history) {
|
||||
var event = d3.dispatch('enter', 'exit');
|
||||
var controller = { mode: null };
|
||||
|
||||
controller.enter = function(mode) {
|
||||
mode.controller = controller;
|
||||
mode.history = history;
|
||||
mode.map = map;
|
||||
|
||||
if (controller.mode) {
|
||||
controller.mode.exit();
|
||||
event.exit(controller.mode);
|
||||
}
|
||||
|
||||
mode.enter();
|
||||
controller.mode = mode;
|
||||
event.enter(mode);
|
||||
};
|
||||
|
||||
return d3.rebind(controller, event, 'on');
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
iD.Difference represents the difference between two graphs.
|
||||
It knows how to calculate the set of entities that were
|
||||
created, modified, or deleted, and also contains the logic
|
||||
for recursively extending a difference to the complete set
|
||||
of entities that will require a redraw, taking into account
|
||||
child and parent relationships.
|
||||
*/
|
||||
iD.Difference = function(base, head) {
|
||||
var changes = {}, length = 0;
|
||||
|
||||
_.each(head.entities, function(h, id) {
|
||||
var b = base.entities[id];
|
||||
if (h !== b) {
|
||||
changes[id] = {base: b, head: h};
|
||||
length++;
|
||||
}
|
||||
});
|
||||
|
||||
_.each(base.entities, function(b, id) {
|
||||
var h = head.entities[id];
|
||||
if (!changes[id] && h !== b) {
|
||||
changes[id] = {base: b, head: h};
|
||||
length++;
|
||||
}
|
||||
});
|
||||
|
||||
var difference = {};
|
||||
|
||||
difference.length = function() {
|
||||
return length;
|
||||
};
|
||||
|
||||
difference.changes = function() {
|
||||
return changes;
|
||||
};
|
||||
|
||||
difference.extantIDs = function() {
|
||||
var result = [];
|
||||
_.each(changes, function(change, id) {
|
||||
if (change.head) result.push(id);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
difference.modified = function() {
|
||||
var result = [];
|
||||
_.each(changes, function(change) {
|
||||
if (change.base && change.head) result.push(change.head);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
difference.created = function() {
|
||||
var result = [];
|
||||
_.each(changes, function(change) {
|
||||
if (!change.base && change.head) result.push(change.head);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
difference.deleted = function() {
|
||||
var result = [];
|
||||
_.each(changes, function(change) {
|
||||
if (change.base && !change.head) result.push(change.base);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
difference.complete = function(extent) {
|
||||
var result = {}, id, change;
|
||||
|
||||
function addParents(parents) {
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
var parent = parents[i];
|
||||
|
||||
if (parent.id in result)
|
||||
continue;
|
||||
|
||||
result[parent.id] = parent;
|
||||
addParents(head.parentRelations(parent));
|
||||
}
|
||||
}
|
||||
|
||||
for (id in changes) {
|
||||
change = changes[id];
|
||||
|
||||
var h = change.head,
|
||||
b = change.base,
|
||||
entity = h || b;
|
||||
|
||||
if (extent &&
|
||||
(!h || !h.intersects(extent, head)) &&
|
||||
(!b || !b.intersects(extent, base)))
|
||||
continue;
|
||||
|
||||
result[id] = h;
|
||||
|
||||
if (entity.type === 'way') {
|
||||
var nh = h ? h.nodes : [],
|
||||
nb = b ? b.nodes : [],
|
||||
diff;
|
||||
|
||||
diff = _.difference(nh, nb);
|
||||
for (var i = 0; i < diff.length; i++) {
|
||||
result[diff[i]] = head.entity(diff[i]);
|
||||
}
|
||||
|
||||
diff = _.difference(nb, nh);
|
||||
for (i = 0; i < diff.length; i++) {
|
||||
result[diff[i]] = head.entity(diff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
addParents(head.parentWays(entity));
|
||||
addParents(head.parentRelations(entity));
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return difference;
|
||||
};
|
||||
@@ -9,22 +9,22 @@ iD.Entity = function(attrs) {
|
||||
return (new iD.Entity()).initialize(arguments);
|
||||
};
|
||||
|
||||
iD.Entity.id = function (type) {
|
||||
iD.Entity.id = function(type) {
|
||||
return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
|
||||
};
|
||||
|
||||
iD.Entity.id.next = {node: -1, way: -1, relation: -1};
|
||||
|
||||
iD.Entity.id.fromOSM = function (type, id) {
|
||||
iD.Entity.id.fromOSM = function(type, id) {
|
||||
return type[0] + id;
|
||||
};
|
||||
|
||||
iD.Entity.id.toOSM = function (id) {
|
||||
iD.Entity.id.toOSM = function(id) {
|
||||
return id.slice(1);
|
||||
};
|
||||
|
||||
// A function suitable for use as the second argument to d3.selection#data().
|
||||
iD.Entity.key = function (entity) {
|
||||
iD.Entity.key = function(entity) {
|
||||
return entity.id;
|
||||
};
|
||||
|
||||
@@ -43,7 +43,6 @@ iD.Entity.prototype = {
|
||||
|
||||
if (!this.id && this.type) {
|
||||
this.id = iD.Entity.id(this.type);
|
||||
this._updated = true;
|
||||
}
|
||||
|
||||
if (iD.debug) {
|
||||
@@ -63,15 +62,21 @@ iD.Entity.prototype = {
|
||||
},
|
||||
|
||||
update: function(attrs) {
|
||||
return iD.Entity(this, attrs, {_updated: true});
|
||||
return iD.Entity(this, attrs);
|
||||
},
|
||||
|
||||
created: function() {
|
||||
return this._updated && this.osmId().charAt(0) === '-';
|
||||
},
|
||||
|
||||
modified: function() {
|
||||
return this._updated && this.osmId().charAt(0) !== '-';
|
||||
mergeTags: function(tags) {
|
||||
var merged = _.clone(this.tags);
|
||||
for (var k in tags) {
|
||||
var t1 = merged[k],
|
||||
t2 = tags[k];
|
||||
if (t1 && t1 !== t2) {
|
||||
merged[k] = t1 + "; " + t2;
|
||||
} else {
|
||||
merged[k] = t2;
|
||||
}
|
||||
}
|
||||
return this.update({tags: merged});
|
||||
},
|
||||
|
||||
intersects: function(extent, resolver) {
|
||||
@@ -79,7 +84,7 @@ iD.Entity.prototype = {
|
||||
},
|
||||
|
||||
hasInterestingTags: function() {
|
||||
return _.keys(this.tags).some(function (key) {
|
||||
return _.keys(this.tags).some(function(key) {
|
||||
return key != "attribution" &&
|
||||
key != "created_by" &&
|
||||
key != "source" &&
|
||||
@@ -88,6 +93,23 @@ iD.Entity.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
deprecatedTags: function() {
|
||||
var tags = _.pairs(this.tags);
|
||||
var deprecated = {};
|
||||
|
||||
iD.data.deprecated.forEach(function(d) {
|
||||
var match = _.pairs(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;
|
||||
},
|
||||
|
||||
friendlyName: function() {
|
||||
// Generate a string such as 'river' or 'Fred's House' for an entity.
|
||||
if (!this.tags || !Object.keys(this.tags).length) { return ''; }
|
||||
@@ -93,6 +93,7 @@ iD.Graph.prototype = {
|
||||
rebase: function(entities) {
|
||||
var base = this.base(),
|
||||
i, k, child, id, keys;
|
||||
|
||||
// Merging of data only needed if graph is the base graph
|
||||
if (!this.inherited) {
|
||||
for (i in entities) {
|
||||
@@ -110,7 +111,7 @@ iD.Graph.prototype = {
|
||||
if (base.parentWays[child]) {
|
||||
for (k = 0; k < base.parentWays[child].length; k++) {
|
||||
id = base.parentWays[child][k];
|
||||
if (this.entity(id) && !_.contains(this._parentWays[child], id)) {
|
||||
if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
|
||||
this._parentWays[child].push(id);
|
||||
}
|
||||
}
|
||||
@@ -123,7 +124,7 @@ iD.Graph.prototype = {
|
||||
if (base.parentRels[child]) {
|
||||
for (k = 0; k < base.parentRels[child].length; k++) {
|
||||
id = base.parentRels[child][k];
|
||||
if (this.entity(id) && !_.contains(this._parentRels[child], id)) {
|
||||
if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
|
||||
this._parentRels[child].push(id);
|
||||
}
|
||||
}
|
||||
@@ -189,14 +190,17 @@ iD.Graph.prototype = {
|
||||
},
|
||||
|
||||
replace: function(entity) {
|
||||
return this.update(function () {
|
||||
if (this.entities[entity.id] === entity)
|
||||
return this;
|
||||
|
||||
return this.update(function() {
|
||||
this._updateCalculated(this.entities[entity.id], entity);
|
||||
this.entities[entity.id] = entity;
|
||||
});
|
||||
},
|
||||
|
||||
remove: function(entity) {
|
||||
return this.update(function () {
|
||||
return this.update(function() {
|
||||
this._updateCalculated(entity, undefined);
|
||||
this.entities[entity.id] = undefined;
|
||||
});
|
||||
@@ -227,70 +231,55 @@ iD.Graph.prototype = {
|
||||
var items = [];
|
||||
for (var i in this.entities) {
|
||||
var entity = this.entities[i];
|
||||
if (entity && entity.intersects(extent, this)) {
|
||||
if (entity && this.hasAllChildren(entity) && entity.intersects(extent, this)) {
|
||||
items.push(entity);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
||||
difference: function (graph) {
|
||||
|
||||
function diff(a, b) {
|
||||
var result = [],
|
||||
keys = Object.keys(a.entities),
|
||||
entity, oldentity, id, i;
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
id = keys[i];
|
||||
entity = a.entities[id];
|
||||
oldentity = b.entities[id];
|
||||
if (entity !== oldentity) {
|
||||
|
||||
// maybe adding affected children better belongs in renderer/map.js?
|
||||
if (entity && entity.type === 'way' &&
|
||||
oldentity && oldentity.type === 'way') {
|
||||
result = result
|
||||
.concat(_.difference(entity.nodes, oldentity.nodes))
|
||||
.concat(_.difference(oldentity.nodes, entity.nodes));
|
||||
|
||||
} else if (entity && entity.type === 'way') {
|
||||
result = result.concat(entity.nodes);
|
||||
|
||||
} else if (oldentity && oldentity.type === 'way') {
|
||||
result = result.concat(oldentity.nodes);
|
||||
}
|
||||
|
||||
result.push(id);
|
||||
hasAllChildren: function(entity) {
|
||||
// we're only checking changed entities, since we assume fetched data
|
||||
// must have all children present
|
||||
if (this.entities.hasOwnProperty(entity.id)) {
|
||||
if (entity.type === 'way') {
|
||||
for (i = 0; i < entity.nodes.length; i++) {
|
||||
if (!this.entities[entity.nodes[i]]) return false;
|
||||
}
|
||||
} else if (entity.type === 'relation') {
|
||||
for (i = 0; i < entity.members.length; i++) {
|
||||
if (!this.entities[entity.members[i].id]) return false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return _.unique(diff(this, graph).concat(diff(graph, this)).sort());
|
||||
return true;
|
||||
},
|
||||
|
||||
modified: function() {
|
||||
var result = [], base = this.base().entities;
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (entity && base[id]) result.push(id);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
// Obliterates any existing entities
|
||||
load: function(entities) {
|
||||
|
||||
created: function() {
|
||||
var result = [], base = this.base().entities;
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (entity && !base[id]) result.push(id);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
var base = this.base(),
|
||||
i, entity, prefix;
|
||||
this.entities = Object.create(base.entities);
|
||||
|
||||
deleted: function() {
|
||||
var result = [], base = this.base().entities;
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (!entity && base[id]) result.push(id);
|
||||
});
|
||||
return result;
|
||||
for (i in entities) {
|
||||
entity = entities[i];
|
||||
prefix = i[0];
|
||||
|
||||
if (entity === 'undefined') {
|
||||
this.entities[i] = undefined;
|
||||
} else if (prefix == 'n') {
|
||||
this.entities[i] = new iD.Node(entity);
|
||||
|
||||
} else if (prefix == 'w') {
|
||||
this.entities[i] = new iD.Way(entity);
|
||||
|
||||
} else if (prefix == 'r') {
|
||||
this.entities[i] = new iD.Relation(entity);
|
||||
}
|
||||
this._updateCalculated(base.entities[i], this.entities[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,245 @@
|
||||
iD.History = function(context) {
|
||||
var stack, index,
|
||||
imagery_used = 'Bing',
|
||||
dispatch = d3.dispatch('change', 'undone', 'redone'),
|
||||
lock = false;
|
||||
|
||||
function perform(actions) {
|
||||
actions = Array.prototype.slice.call(actions);
|
||||
|
||||
var annotation;
|
||||
|
||||
if (!_.isFunction(_.last(actions))) {
|
||||
annotation = actions.pop();
|
||||
}
|
||||
|
||||
var graph = stack[index].graph;
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
graph = actions[i](graph);
|
||||
}
|
||||
|
||||
return {
|
||||
graph: graph,
|
||||
annotation: annotation,
|
||||
imagery_used: imagery_used
|
||||
};
|
||||
}
|
||||
|
||||
function change(previous) {
|
||||
var difference = iD.Difference(previous, history.graph());
|
||||
dispatch.change(difference);
|
||||
return difference;
|
||||
}
|
||||
|
||||
function getKey(n) {
|
||||
return 'iD_' + window.location.origin + '_' + n;
|
||||
}
|
||||
|
||||
var history = {
|
||||
graph: function() {
|
||||
return stack[index].graph;
|
||||
},
|
||||
|
||||
merge: function(entities) {
|
||||
for (var i = 0; i < stack.length; i++) {
|
||||
stack[i].graph.rebase(entities);
|
||||
}
|
||||
|
||||
dispatch.change();
|
||||
},
|
||||
|
||||
perform: function() {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
stack = stack.slice(0, index + 1);
|
||||
stack.push(perform(arguments));
|
||||
index++;
|
||||
|
||||
return change(previous);
|
||||
},
|
||||
|
||||
replace: function() {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
// assert(index == stack.length - 1)
|
||||
stack[index] = perform(arguments);
|
||||
|
||||
return change(previous);
|
||||
},
|
||||
|
||||
pop: function() {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
if (index > 0) {
|
||||
index--;
|
||||
stack.pop();
|
||||
return change(previous);
|
||||
}
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
// Pop to the first annotated state.
|
||||
while (index > 0) {
|
||||
if (stack[index].annotation) break;
|
||||
index--;
|
||||
}
|
||||
|
||||
// Pop to the next annotated state.
|
||||
while (index > 0) {
|
||||
index--;
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
|
||||
dispatch.undone();
|
||||
return change(previous);
|
||||
},
|
||||
|
||||
redo: function() {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
while (index < stack.length - 1) {
|
||||
index++;
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
|
||||
dispatch.redone();
|
||||
return change(previous);
|
||||
},
|
||||
|
||||
undoAnnotation: function() {
|
||||
var i = index;
|
||||
while (i >= 0) {
|
||||
if (stack[i].annotation) return stack[i].annotation;
|
||||
i--;
|
||||
}
|
||||
},
|
||||
|
||||
redoAnnotation: function() {
|
||||
var i = index + 1;
|
||||
while (i <= stack.length - 1) {
|
||||
if (stack[i].annotation) return stack[i].annotation;
|
||||
i++;
|
||||
}
|
||||
},
|
||||
|
||||
difference: function() {
|
||||
var base = stack[0].graph,
|
||||
head = stack[index].graph;
|
||||
return iD.Difference(base, head);
|
||||
},
|
||||
|
||||
changes: function() {
|
||||
var difference = history.difference();
|
||||
|
||||
function discardTags(entity) {
|
||||
if (_.isEmpty(entity.tags)) {
|
||||
return entity;
|
||||
} else {
|
||||
return entity.update({
|
||||
tags: _.omit(entity.tags, iD.data.discarded)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
modified: difference.modified().map(discardTags),
|
||||
created: difference.created().map(discardTags),
|
||||
deleted: difference.deleted()
|
||||
};
|
||||
},
|
||||
|
||||
hasChanges: function() {
|
||||
return this.difference().length() > 0;
|
||||
},
|
||||
|
||||
numChanges: function() {
|
||||
return this.difference().length();
|
||||
},
|
||||
|
||||
imagery_used: function(source) {
|
||||
if (source) imagery_used = source;
|
||||
else return _.without(
|
||||
_.unique(_.pluck(stack.slice(1, index + 1), 'imagery_used')),
|
||||
undefined, 'Custom');
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
stack = [{graph: iD.Graph()}];
|
||||
index = 0;
|
||||
dispatch.change();
|
||||
},
|
||||
|
||||
save: function() {
|
||||
if (!lock) return;
|
||||
context.storage(getKey('lock'), null);
|
||||
|
||||
if (stack.length <= 1) return;
|
||||
|
||||
var json = JSON.stringify(stack.map(function(i) {
|
||||
return {
|
||||
annotation: i.annotation,
|
||||
imagery_used: i.imagery_used,
|
||||
entities: i.graph.entities
|
||||
};
|
||||
}), function includeUndefined(key, value) {
|
||||
if (typeof value === 'undefined') return 'undefined';
|
||||
return value;
|
||||
});
|
||||
|
||||
context.storage(getKey('history'), json);
|
||||
context.storage(getKey('nextIDs'), JSON.stringify(iD.Entity.id.next));
|
||||
context.storage(getKey('index'), index);
|
||||
},
|
||||
|
||||
clearSaved: function() {
|
||||
if (!lock) return;
|
||||
context.storage(getKey('history'), null);
|
||||
context.storage(getKey('nextIDs'), null);
|
||||
context.storage(getKey('index'), null);
|
||||
},
|
||||
|
||||
lock: function() {
|
||||
if (context.storage(getKey('lock'))) return false;
|
||||
context.storage(getKey('lock'), true);
|
||||
lock = true;
|
||||
return lock;
|
||||
},
|
||||
|
||||
restorableChanges: function() {
|
||||
return lock && !!context.storage(getKey('history'));
|
||||
},
|
||||
|
||||
load: function() {
|
||||
if (!lock) return;
|
||||
|
||||
var json = context.storage(getKey('history')),
|
||||
nextIDs = context.storage(getKey('nextIDs')),
|
||||
index_ = context.storage(getKey('index'));
|
||||
|
||||
if (!json) return;
|
||||
if (nextIDs) iD.Entity.id.next = JSON.parse(nextIDs);
|
||||
if (index_ !== null) index = parseInt(index_, 10);
|
||||
|
||||
context.storage(getKey('history', null));
|
||||
context.storage(getKey('nextIDs', null));
|
||||
context.storage(getKey('index', null));
|
||||
|
||||
stack = JSON.parse(json).map(function(d, i) {
|
||||
d.graph = iD.Graph(stack[0].graph).load(d.entities);
|
||||
return d;
|
||||
});
|
||||
stack[0].graph.inherited = false;
|
||||
dispatch.change();
|
||||
|
||||
},
|
||||
|
||||
_getKey: getKey
|
||||
|
||||
};
|
||||
|
||||
history.reset();
|
||||
|
||||
return d3.rebind(history, dispatch, 'on');
|
||||
};
|
||||
@@ -12,7 +12,7 @@ _.extend(iD.Node.prototype, {
|
||||
type: "node",
|
||||
|
||||
extent: function() {
|
||||
return iD.geo.Extent(this.loc);
|
||||
return new iD.geo.Extent(this.loc);
|
||||
},
|
||||
|
||||
geometry: function(graph) {
|
||||
@@ -47,6 +47,6 @@ _.extend(iD.Node.prototype, {
|
||||
type: 'Point',
|
||||
coordinates: this.loc
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -14,7 +14,7 @@ _.extend(iD.Relation.prototype, {
|
||||
|
||||
extent: function(resolver) {
|
||||
return resolver.transient(this, 'extent', function() {
|
||||
return this.members.reduce(function (extent, member) {
|
||||
return this.members.reduce(function(extent, member) {
|
||||
member = resolver.entity(member.id);
|
||||
if (member) {
|
||||
return extent.extend(member.extent(resolver));
|
||||
@@ -26,7 +26,7 @@ _.extend(iD.Relation.prototype, {
|
||||
},
|
||||
|
||||
geometry: function() {
|
||||
return 'relation';
|
||||
return this.isMultipolygon() ? 'area' : 'relation';
|
||||
},
|
||||
|
||||
// Return the first member with the given role. A copy of the member object
|
||||
@@ -49,6 +49,16 @@ _.extend(iD.Relation.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
// 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);
|
||||
@@ -66,6 +76,28 @@ _.extend(iD.Relation.prototype, {
|
||||
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: {
|
||||
@@ -83,6 +115,31 @@ _.extend(iD.Relation.prototype, {
|
||||
return r;
|
||||
},
|
||||
|
||||
asGeoJSON: function(resolver) {
|
||||
if (this.isMultipolygon()) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
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));
|
||||
})
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
isMultipolygon: function() {
|
||||
return this.tags.type === 'multipolygon';
|
||||
},
|
||||
|
||||
isRestriction: function() {
|
||||
return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
|
||||
},
|
||||
@@ -99,8 +156,8 @@ _.extend(iD.Relation.prototype, {
|
||||
//
|
||||
multipolygon: function(resolver) {
|
||||
var members = this.members
|
||||
.filter(function (m) { return m.type === 'way' && resolver.entity(m.id); })
|
||||
.map(function (m) { return { role: m.role || 'outer', id: m.id, nodes: resolver.childNodes(resolver.entity(m.id)) }; });
|
||||
.filter(function(m) { return m.type === 'way' && resolver.entity(m.id); })
|
||||
.map(function(m) { return { role: m.role || 'outer', id: m.id, nodes: resolver.childNodes(resolver.entity(m.id)) }; });
|
||||
|
||||
function join(ways) {
|
||||
var joined = [], current, first, last, i, how, what;
|
||||
@@ -145,30 +202,28 @@ _.extend(iD.Relation.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
return joined;
|
||||
return joined.map(function(nodes) { return _.pluck(nodes, 'loc'); });
|
||||
}
|
||||
|
||||
function findOuter(inner) {
|
||||
var o, outer;
|
||||
|
||||
inner = _.pluck(inner, 'loc');
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
outer = outers[o];
|
||||
if (iD.geo.polygonContainsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
outer = outers[o];
|
||||
if (iD.geo.polygonIntersectsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
var outers = join(members.filter(function (m) { return m.role === 'outer'; })),
|
||||
inners = join(members.filter(function (m) { return m.role === 'inner'; })),
|
||||
result = outers.map(function (o) { return [o]; });
|
||||
var outers = join(members.filter(function(m) { return m.role === 'outer'; })),
|
||||
inners = join(members.filter(function(m) { return m.role === 'inner'; })),
|
||||
result = outers.map(function(o) { return [o]; });
|
||||
|
||||
for (var i = 0; i < inners.length; i++) {
|
||||
var o = findOuter(inners[i]);
|
||||
@@ -14,7 +14,7 @@ _.extend(iD.Way.prototype, {
|
||||
|
||||
extent: function(resolver) {
|
||||
return resolver.transient(this, 'extent', function() {
|
||||
return this.nodes.reduce(function (extent, id) {
|
||||
return this.nodes.reduce(function(extent, id) {
|
||||
return extent.extend(resolver.entity(id).extent(resolver));
|
||||
}, iD.geo.Extent());
|
||||
});
|
||||
@@ -47,11 +47,14 @@ _.extend(iD.Way.prototype, {
|
||||
// - doesn't have area=no
|
||||
// - doesn't have highway tag
|
||||
isArea: function() {
|
||||
return this.tags.area === 'yes' ||
|
||||
(this.isClosed() &&
|
||||
this.tags.area !== 'no' &&
|
||||
!this.tags.highway &&
|
||||
!this.tags.barrier);
|
||||
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.Way.areaKeys)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
isDegenerate: function() {
|
||||
@@ -74,6 +77,19 @@ _.extend(iD.Way.prototype, {
|
||||
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 = _.without(this.nodes, id);
|
||||
|
||||
@@ -103,13 +119,28 @@ _.extend(iD.Way.prototype, {
|
||||
},
|
||||
|
||||
asGeoJSON: function(resolver) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: _.pluck(resolver.childNodes(this), 'loc')
|
||||
}
|
||||
};
|
||||
if (this.isArea()) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [_.pluck(resolver.childNodes(this), 'loc')]
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: _.pluck(resolver.childNodes(this), 'loc')
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
iD.Way.areaKeys = iD.util.trueObj(['area', 'building', 'leisure', 'tourism', 'ruins',
|
||||
'historic', 'landuse', 'military', 'natural', 'amenity', 'shop', 'man_made',
|
||||
'public_transport']);
|
||||
@@ -9,16 +9,17 @@ iD.geo.interp = function(p1, p2, t) {
|
||||
p1[1] + (p2[1] - p1[1]) * t];
|
||||
};
|
||||
|
||||
// http://jsperf.com/id-dist-optimization
|
||||
iD.geo.dist = function(a, b) {
|
||||
return Math.sqrt(Math.pow(a[0] - b[0], 2) +
|
||||
Math.pow(a[1] - b[1], 2));
|
||||
var x = a[0] - b[0], y = a[1] - b[1];
|
||||
return Math.sqrt((x * x) + (y * y));
|
||||
};
|
||||
|
||||
iD.geo.chooseIndex = function(way, point, map) {
|
||||
iD.geo.chooseIndex = function(way, point, context) {
|
||||
var dist = iD.geo.dist,
|
||||
graph = map.history().graph(),
|
||||
graph = context.graph(),
|
||||
nodes = graph.childNodes(way),
|
||||
projNodes = nodes.map(function(n) { return map.projection(n.loc); });
|
||||
projNodes = nodes.map(function(n) { return context.projection(n.loc); });
|
||||
|
||||
for (var i = 0, changes = []; i < projNodes.length - 1; i++) {
|
||||
changes[i] =
|
||||
@@ -63,13 +64,13 @@ iD.geo.pointInPolygon = function(point, polygon) {
|
||||
};
|
||||
|
||||
iD.geo.polygonContainsPolygon = function(outer, inner) {
|
||||
return _.every(inner, function (point) {
|
||||
return _.every(inner, function(point) {
|
||||
return iD.geo.pointInPolygon(point, outer);
|
||||
});
|
||||
};
|
||||
|
||||
iD.geo.polygonIntersectsPolygon = function(outer, inner) {
|
||||
return _.some(inner, function (point) {
|
||||
return _.some(inner, function(point) {
|
||||
return iD.geo.pointInPolygon(point, outer);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.geo.Extent = function (min, max) {
|
||||
iD.geo.Extent = function geoExtent(min, max) {
|
||||
if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
|
||||
if (min instanceof iD.geo.Extent) {
|
||||
return min;
|
||||
@@ -14,21 +14,21 @@ iD.geo.Extent = function (min, max) {
|
||||
iD.geo.Extent.prototype = [[], []];
|
||||
|
||||
_.extend(iD.geo.Extent.prototype, {
|
||||
extend: function (obj) {
|
||||
obj = iD.geo.Extent(obj);
|
||||
extend: function(obj) {
|
||||
if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
|
||||
return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
|
||||
Math.min(obj[0][1], this[0][1])],
|
||||
[Math.max(obj[1][0], this[1][0]),
|
||||
Math.max(obj[1][1], this[1][1])]);
|
||||
},
|
||||
|
||||
center: function () {
|
||||
center: function() {
|
||||
return [(this[0][0] + this[1][0]) / 2,
|
||||
(this[0][1] + this[1][1]) / 2];
|
||||
},
|
||||
|
||||
intersects: function (obj) {
|
||||
obj = iD.geo.Extent(obj);
|
||||
intersects: function(obj) {
|
||||
if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
|
||||
return obj[0][0] <= this[1][0] &&
|
||||
obj[0][1] <= this[1][1] &&
|
||||
obj[1][0] >= this[0][0] &&
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
iD.History = function() {
|
||||
var stack, index,
|
||||
imagery_used = 'Bing',
|
||||
dispatch = d3.dispatch('change', 'undone', 'redone');
|
||||
|
||||
function perform(actions) {
|
||||
actions = Array.prototype.slice.call(actions);
|
||||
|
||||
var annotation;
|
||||
|
||||
if (!_.isFunction(_.last(actions))) {
|
||||
annotation = actions.pop();
|
||||
}
|
||||
|
||||
var graph = stack[index].graph;
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
graph = actions[i](graph);
|
||||
}
|
||||
|
||||
return {graph: graph, annotation: annotation, imagery_used: imagery_used};
|
||||
}
|
||||
|
||||
function change(previous) {
|
||||
dispatch.change(history.graph().difference(previous));
|
||||
}
|
||||
|
||||
var history = {
|
||||
graph: function () {
|
||||
return stack[index].graph;
|
||||
},
|
||||
|
||||
merge: function (entities) {
|
||||
for (var i = 0; i < stack.length; i++) {
|
||||
stack[i].graph.rebase(entities);
|
||||
}
|
||||
},
|
||||
|
||||
perform: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
stack = stack.slice(0, index + 1);
|
||||
stack.push(perform(arguments));
|
||||
index++;
|
||||
|
||||
change(previous);
|
||||
},
|
||||
|
||||
replace: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
// assert(index == stack.length - 1)
|
||||
stack[index] = perform(arguments);
|
||||
|
||||
change(previous);
|
||||
},
|
||||
|
||||
pop: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
if (index > 0) {
|
||||
index--;
|
||||
stack.pop();
|
||||
change(previous);
|
||||
}
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
// Pop to the first annotated state.
|
||||
while (index > 0) {
|
||||
if (stack[index].annotation) break;
|
||||
index--;
|
||||
}
|
||||
|
||||
// Pop to the next annotated state.
|
||||
while (index > 0) {
|
||||
index--;
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
|
||||
dispatch.undone();
|
||||
change(previous);
|
||||
},
|
||||
|
||||
redo: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
while (index < stack.length - 1) {
|
||||
index++;
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
|
||||
dispatch.redone();
|
||||
change(previous);
|
||||
},
|
||||
|
||||
undoAnnotation: function () {
|
||||
var i = index;
|
||||
while (i >= 0) {
|
||||
if (stack[i].annotation) return stack[i].annotation;
|
||||
i--;
|
||||
}
|
||||
},
|
||||
|
||||
redoAnnotation: function () {
|
||||
var i = index + 1;
|
||||
while (i <= stack.length - 1) {
|
||||
if (stack[i].annotation) return stack[i].annotation;
|
||||
i++;
|
||||
}
|
||||
},
|
||||
|
||||
changes: function () {
|
||||
var initial = stack[0].graph,
|
||||
current = stack[index].graph;
|
||||
|
||||
return {
|
||||
modified: current.modified().map(function (id) {
|
||||
return current.entity(id);
|
||||
}),
|
||||
created: current.created().map(function (id) {
|
||||
return current.entity(id);
|
||||
}),
|
||||
deleted: current.deleted().map(function (id) {
|
||||
return initial.entity(id);
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
hasChanges: function() {
|
||||
return !!this.numChanges();
|
||||
},
|
||||
|
||||
numChanges: function() {
|
||||
return d3.sum(d3.values(this.changes()).map(function(c) {
|
||||
return c.length;
|
||||
}));
|
||||
},
|
||||
|
||||
imagery_used: function(source) {
|
||||
if (source) imagery_used = source;
|
||||
else return _.without(
|
||||
_.unique(_.pluck(stack.slice(1, index + 1), 'imagery_used')),
|
||||
undefined, 'Custom');
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
stack = [{graph: iD.Graph()}];
|
||||
index = 0;
|
||||
dispatch.change();
|
||||
}
|
||||
};
|
||||
|
||||
history.reset();
|
||||
|
||||
return d3.rebind(history, dispatch, 'on');
|
||||
};
|
||||
@@ -1,290 +1,149 @@
|
||||
window.iD = function(container) {
|
||||
// the reported, displayed version of iD.
|
||||
var version = '0.0.0-alpha1';
|
||||
window.iD = function () {
|
||||
var context = {},
|
||||
storage = localStorage || {};
|
||||
|
||||
var connection = iD.Connection()
|
||||
.version(version),
|
||||
history = iD.History(),
|
||||
map = iD.Map()
|
||||
.connection(connection)
|
||||
.history(history),
|
||||
controller = iD.Controller(map, history);
|
||||
context.storage = function(k, v) {
|
||||
if (arguments.length === 1) return storage[k];
|
||||
else if (v === null) delete storage[k];
|
||||
else storage[k] = v;
|
||||
};
|
||||
|
||||
map.background.source(iD.BackgroundSource.Bing);
|
||||
var history = iD.History(context),
|
||||
dispatch = d3.dispatch('enter', 'exit'),
|
||||
mode,
|
||||
container,
|
||||
ui = iD.ui(context),
|
||||
map = iD.Map(context);
|
||||
|
||||
function editor(container) {
|
||||
if (!iD.supported()) {
|
||||
container.html('This editor is supported in Firefox, Chrome, Safari, Opera, ' +
|
||||
'and Internet Explorer 9 and above. Please upgrade your browser ' +
|
||||
'or use Potlatch 2 to edit the map.')
|
||||
.style('text-align:center;font-style:italic;');
|
||||
return;
|
||||
// the connection requires .storage() to be available on calling.
|
||||
var connection = iD.Connection(context);
|
||||
|
||||
connection.on('load.context', function loadContext(err, result) {
|
||||
history.merge(result);
|
||||
});
|
||||
|
||||
/* Straight accessors. Avoid using these if you can. */
|
||||
context.ui = function() { return ui; };
|
||||
context.connection = function() { return connection; };
|
||||
context.history = function() { return history; };
|
||||
context.map = function() { return map; };
|
||||
|
||||
/* History */
|
||||
context.graph = history.graph;
|
||||
context.perform = history.perform;
|
||||
context.replace = history.replace;
|
||||
context.pop = history.pop;
|
||||
context.undo = history.undo;
|
||||
context.redo = history.redo;
|
||||
context.changes = history.changes;
|
||||
|
||||
/* Graph */
|
||||
context.entity = function(id) {
|
||||
return history.graph().entity(id);
|
||||
};
|
||||
|
||||
context.geometry = function(id) {
|
||||
return context.entity(id).geometry(history.graph());
|
||||
};
|
||||
|
||||
/* Modes */
|
||||
context.enter = function(newMode) {
|
||||
if (mode) {
|
||||
mode.exit();
|
||||
dispatch.exit(mode);
|
||||
}
|
||||
|
||||
function hintprefix(x, y) {
|
||||
return '<span>' + y + '</span>' + '<div class="keyhint-wrap"><span class="keyhint"> ' + x + '</span></div>';
|
||||
mode = newMode;
|
||||
mode.enter();
|
||||
dispatch.enter(mode);
|
||||
};
|
||||
|
||||
context.mode = function() {
|
||||
return mode;
|
||||
};
|
||||
|
||||
context.selection = function() {
|
||||
if (mode.id === 'select') {
|
||||
return mode.selection();
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
var m = container.append('div')
|
||||
.attr('id', 'map')
|
||||
.call(map);
|
||||
/* Behaviors */
|
||||
context.install = function(behavior) {
|
||||
context.surface().call(behavior);
|
||||
};
|
||||
|
||||
var bar = container.append('div')
|
||||
.attr('id', 'bar')
|
||||
.attr('class','pad1 fillD');
|
||||
context.uninstall = function(behavior) {
|
||||
context.surface().call(behavior.off);
|
||||
};
|
||||
|
||||
var limiter = bar.append('div')
|
||||
.attr('class', 'limiter');
|
||||
/* Map */
|
||||
context.background = function() { return map.background; };
|
||||
context.surface = function() { return map.surface; };
|
||||
context.projection = map.projection;
|
||||
context.tail = map.tail;
|
||||
context.redraw = map.redraw;
|
||||
context.pan = map.pan;
|
||||
context.zoomIn = map.zoomIn;
|
||||
context.zoomOut = map.zoomOut;
|
||||
|
||||
var buttons_joined = limiter.append('div')
|
||||
.attr('class', 'button-wrap joined col4');
|
||||
context.container = function(_) {
|
||||
if (!arguments.length) return container;
|
||||
container = _;
|
||||
return context;
|
||||
};
|
||||
|
||||
var buttons = buttons_joined.selectAll('button.add-button')
|
||||
.data([iD.modes.Browse(), iD.modes.AddPoint(), iD.modes.AddLine(), iD.modes.AddArea()])
|
||||
.enter().append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('class', function (mode) { return mode.title + ' add-button col3'; })
|
||||
.call(bootstrap.tooltip().placement('bottom').html(true))
|
||||
.attr('data-original-title', function (mode) {
|
||||
return hintprefix(mode.key, mode.description);
|
||||
})
|
||||
.on('click.editor', function (mode) { controller.enter(mode); });
|
||||
|
||||
function disableTooHigh() {
|
||||
if (map.editable()) {
|
||||
notice.message(false);
|
||||
buttons.attr('disabled', null);
|
||||
} else {
|
||||
buttons.attr('disabled', 'disabled');
|
||||
notice.message(true);
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
}
|
||||
|
||||
var notice = iD.ui.notice(limiter)
|
||||
.message(false)
|
||||
.on('zoom', function() { map.zoom(16); });
|
||||
|
||||
map.on('move.editor', _.debounce(function() {
|
||||
disableTooHigh();
|
||||
contributors.call(iD.ui.contributors(map));
|
||||
}, 500));
|
||||
|
||||
buttons.append('span')
|
||||
.attr('class', function(d) {
|
||||
return d.id + ' icon icon-pre-text';
|
||||
});
|
||||
|
||||
buttons.append('span').attr('class', 'label').text(function (mode) { return mode.title; });
|
||||
|
||||
controller.on('enter.editor', function (entered) {
|
||||
buttons.classed('active', function (mode) { return entered.button === mode.button; });
|
||||
container.classed("mode-" + entered.id, true);
|
||||
});
|
||||
|
||||
controller.on('exit.editor', function (exited) {
|
||||
container.classed("mode-" + exited.id, false);
|
||||
});
|
||||
|
||||
var undo_buttons = limiter.append('div')
|
||||
.attr('class', 'button-wrap joined col1'),
|
||||
undo_tooltip = bootstrap.tooltip().placement('bottom').html(true);
|
||||
|
||||
undo_buttons.append('button')
|
||||
.attr({ id: 'undo', 'class': 'col6' })
|
||||
.property('disabled', true)
|
||||
.html("<span class='undo icon'></span><small></small>")
|
||||
.on('click.editor', history.undo)
|
||||
.call(undo_tooltip);
|
||||
|
||||
undo_buttons.append('button')
|
||||
.attr({ id: 'redo', 'class': 'col6' })
|
||||
.property('disabled', true)
|
||||
.html("<span class='redo icon'><small></small>")
|
||||
.on('click.editor', history.redo)
|
||||
.call(undo_tooltip);
|
||||
|
||||
var save_button = limiter.append('div').attr('class','button-wrap col1').append('button')
|
||||
.attr('class', 'save col12')
|
||||
.call(iD.ui.save().map(map).controller(controller));
|
||||
|
||||
var zoom = container.append('div')
|
||||
.attr('class', 'zoombuttons map-control')
|
||||
.selectAll('button')
|
||||
.data([['zoom-in', '+', map.zoomIn, 'Zoom In'], ['zoom-out', '-', map.zoomOut, 'Zoom Out']])
|
||||
.enter()
|
||||
.append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('class', function(d) { return d[0]; })
|
||||
.attr('title', function(d) { return d[3]; })
|
||||
.on('click.editor', function(d) { return d[2](); })
|
||||
.append('span')
|
||||
.attr('class', function(d) {
|
||||
return d[0] + ' icon';
|
||||
});
|
||||
|
||||
if (navigator.geolocation) {
|
||||
container.append('div')
|
||||
.call(iD.ui.geolocate(map));
|
||||
}
|
||||
|
||||
var gc = container.append('div').attr('class', 'geocode-control map-control')
|
||||
.call(iD.ui.geocoder().map(map));
|
||||
|
||||
container.append('div').attr('class', 'map-control layerswitcher-control')
|
||||
.call(iD.ui.layerswitcher(map));
|
||||
|
||||
container.append('div')
|
||||
.style('display', 'none')
|
||||
.attr('class', 'inspector-wrap fr col5');
|
||||
|
||||
var about = container.append('div')
|
||||
.attr('class','col12 about-block fillD pad1');
|
||||
|
||||
about.append('div')
|
||||
.attr('class', 'user-container')
|
||||
.append('div')
|
||||
.attr('class', 'hello');
|
||||
|
||||
var aboutList = about.append('ul')
|
||||
.attr('id','about')
|
||||
.attr('class','link-list');
|
||||
|
||||
var linkList = aboutList.append('ul')
|
||||
.attr('id','about')
|
||||
.attr('class','pad1 fillD about-block link-list');
|
||||
linkList.append('li').append('a').attr('target', '_blank')
|
||||
.attr('href', 'http://github.com/systemed/iD').text(version);
|
||||
linkList.append('li').append('a').attr('target', '_blank')
|
||||
.attr('href', 'http://github.com/systemed/iD/issues').text('report a bug');
|
||||
|
||||
var imagery = linkList.append('li').attr('id', 'attribution');
|
||||
imagery.append('span').text('imagery');
|
||||
imagery.append('a').attr('target', '_blank')
|
||||
.attr('href', 'http://opengeodata.org/microsoft-imagery-details').text(' provided by bing');
|
||||
|
||||
linkList.append('li').attr('class', 'source-switch').append('a').attr('href', '#')
|
||||
.text('dev')
|
||||
.on('click.editor', function() {
|
||||
d3.event.preventDefault();
|
||||
if (d3.select(this).classed('live')) {
|
||||
map.flush().connection()
|
||||
.url('http://api06.dev.openstreetmap.org');
|
||||
d3.select(this).text('dev').classed('live', false);
|
||||
} else {
|
||||
map.flush().connection()
|
||||
.url('http://www.openstreetmap.org');
|
||||
d3.select(this).text('live').classed('live', true);
|
||||
}
|
||||
});
|
||||
|
||||
var contributors = linkList.append('li')
|
||||
.attr('id', 'user-list');
|
||||
contributors.append('span')
|
||||
.attr('class', 'icon nearby icon-pre-text');
|
||||
contributors.append('span')
|
||||
.text('Viewing contributions by ');
|
||||
contributors.append('span')
|
||||
.attr('class', 'contributor-list');
|
||||
contributors.append('span')
|
||||
.attr('class', 'contributor-count');
|
||||
|
||||
history.on('change.editor', function() {
|
||||
window.onbeforeunload = history.hasChanges() ? function() {
|
||||
return 'You have unsaved changes.';
|
||||
} : null;
|
||||
|
||||
var undo = history.undoAnnotation(),
|
||||
redo = history.redoAnnotation();
|
||||
|
||||
function refreshTooltip(selection) {
|
||||
if (selection.property('disabled')) {
|
||||
selection.call(undo_tooltip.hide);
|
||||
} else if (selection.property('tooltipVisible')) {
|
||||
selection.call(undo_tooltip.show);
|
||||
}
|
||||
}
|
||||
|
||||
limiter.select('#undo')
|
||||
.property('disabled', !undo)
|
||||
.attr('data-original-title', hintprefix('⌘ + Z', undo))
|
||||
.call(refreshTooltip);
|
||||
|
||||
limiter.select('#redo')
|
||||
.property('disabled', !redo)
|
||||
.attr('data-original-title', hintprefix('⌘ + ⇧ + Z', redo))
|
||||
.call(refreshTooltip);
|
||||
});
|
||||
|
||||
d3.select(window).on('resize.editor', function() {
|
||||
map.size(m.size());
|
||||
});
|
||||
|
||||
var keybinding = d3.keybinding('main')
|
||||
.on('⌘+Z', function() { history.undo(); })
|
||||
.on('⌃+Z', function() { history.undo(); })
|
||||
.on('⌘+⇧+Z', function() { history.redo(); })
|
||||
.on('⌃+⇧+Z', function() { history.redo(); })
|
||||
.on('⌫', function() { d3.event.preventDefault(); });
|
||||
|
||||
[iD.modes.Browse(), iD.modes.AddPoint(), iD.modes.AddLine(), iD.modes.AddArea()].forEach(function(m) {
|
||||
keybinding.on(m.key, function() { if (map.editable()) controller.enter(m); });
|
||||
});
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
var hash = iD.behavior.Hash(controller, map);
|
||||
|
||||
hash();
|
||||
|
||||
if (!hash.hadHash) {
|
||||
map.centerZoom([-77.02271, 38.90085], 20);
|
||||
}
|
||||
|
||||
d3.select('.user-container').call(iD.ui.userpanel(connection)
|
||||
.on('logout.editor', connection.logout)
|
||||
.on('login.editor', connection.authenticate));
|
||||
|
||||
controller.enter(iD.modes.Browse());
|
||||
|
||||
if (!localStorage.sawSplash) {
|
||||
iD.ui.splash();
|
||||
localStorage.sawSplash = true;
|
||||
}
|
||||
var q = iD.util.stringQs(location.hash.substring(1)), detected = false;
|
||||
if (q.layer) {
|
||||
context.background()
|
||||
.source(_.find(iD.layers, function(l) {
|
||||
if (l.data.sourcetag === q.layer) {
|
||||
return (detected = true);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
editor.connection = function(_) {
|
||||
if (!arguments.length) return connection;
|
||||
connection = _;
|
||||
return editor;
|
||||
};
|
||||
|
||||
editor.map = function() {
|
||||
return map;
|
||||
};
|
||||
|
||||
editor.controller = function() {
|
||||
return controller;
|
||||
};
|
||||
|
||||
if (arguments.length) {
|
||||
d3.select(container).call(editor);
|
||||
if (!detected) {
|
||||
context.background()
|
||||
.source(_.find(iD.layers, function(l) {
|
||||
return l.data.name === 'Bing aerial imagery';
|
||||
}));
|
||||
}
|
||||
|
||||
return editor;
|
||||
return d3.rebind(context, dispatch, 'on');
|
||||
};
|
||||
|
||||
iD.supported = function() {
|
||||
if (navigator.appName !== 'Microsoft Internet Explorer') {
|
||||
return true;
|
||||
iD.version = '0.0.0-alpha1';
|
||||
|
||||
iD.detect = function() {
|
||||
var browser = {};
|
||||
|
||||
var ua = navigator.userAgent,
|
||||
msie = new RegExp("MSIE ([0-9]{1,}[\\.0-9]{0,})");
|
||||
|
||||
if (msie.exec(ua) !== null) {
|
||||
var rv = parseFloat(RegExp.$1);
|
||||
browser.support = !(rv && rv < 9);
|
||||
} else {
|
||||
var ua = navigator.userAgent;
|
||||
var re = new RegExp("MSIE ([0-9]{1,}[\\.0-9]{0,})");
|
||||
if (re.exec(ua) !== null) {
|
||||
rv = parseFloat( RegExp.$1 );
|
||||
}
|
||||
if (rv && rv < 9) return false;
|
||||
else return true;
|
||||
browser.support = true;
|
||||
}
|
||||
|
||||
// Added due to incomplete svg style support. See #715
|
||||
browser.opera = ua.indexOf('Opera') >= 0;
|
||||
|
||||
browser.locale = navigator.language;
|
||||
|
||||
function nav(x) {
|
||||
return navigator.userAgent.indexOf(x) !== -1;
|
||||
}
|
||||
|
||||
if (nav('Win')) browser.os = 'win';
|
||||
else if (nav('Mac')) browser.os = 'mac';
|
||||
else if (nav('X11')) browser.os = 'linux';
|
||||
else if (nav('Linux')) browser.os = 'linux';
|
||||
else browser.os = 'win';
|
||||
|
||||
return browser;
|
||||
};
|
||||
|
||||
@@ -1,87 +1,66 @@
|
||||
iD.modes.AddArea = function() {
|
||||
iD.modes.AddArea = function(context) {
|
||||
var mode = {
|
||||
id: 'add-area',
|
||||
button: 'area',
|
||||
title: t('modes.add_area.title'),
|
||||
description: t('modes.add_area.description'),
|
||||
key: t('modes.add_area.key')
|
||||
key: '4'
|
||||
};
|
||||
|
||||
var behavior,
|
||||
defaultTags = {area: 'yes'};
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
function start(loc) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawArea(way.id, graph));
|
||||
}
|
||||
|
||||
function startFromWay(other, loc, index) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(other.id, node.id, index));
|
||||
|
||||
controller.enter(iD.modes.DrawArea(way.id, graph));
|
||||
}
|
||||
|
||||
function startFromNode(node) {
|
||||
var graph = history.graph(),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawArea(way.id, graph));
|
||||
}
|
||||
|
||||
function startFromMidpoint(midpoint) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node(),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddMidpoint(midpoint, node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawArea(way.id, graph));
|
||||
}
|
||||
|
||||
behavior = iD.behavior.AddWay(mode)
|
||||
var behavior = iD.behavior.AddWay(context)
|
||||
.on('start', start)
|
||||
.on('startFromWay', startFromWay)
|
||||
.on('startFromNode', startFromNode)
|
||||
.on('startFromMidpoint', startFromMidpoint);
|
||||
.on('startFromNode', startFromNode),
|
||||
defaultTags = {area: 'yes'};
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail(t('modes.add_area.tail'));
|
||||
function start(loc) {
|
||||
var graph = context.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
context.enter(iD.modes.DrawArea(context, way.id, graph));
|
||||
}
|
||||
|
||||
function startFromWay(other, loc, index) {
|
||||
var graph = context.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(other.id, node.id, index));
|
||||
|
||||
context.enter(iD.modes.DrawArea(context, way.id, graph));
|
||||
}
|
||||
|
||||
function startFromNode(node) {
|
||||
var graph = context.graph(),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
context.enter(iD.modes.DrawArea(context, way.id, graph));
|
||||
}
|
||||
|
||||
mode.enter = function() {
|
||||
context.install(behavior);
|
||||
context.tail(t('modes.add_area.tail'));
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.surface.call(behavior.off);
|
||||
context.uninstall(behavior);
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -1,94 +1,74 @@
|
||||
iD.modes.AddLine = function() {
|
||||
iD.modes.AddLine = function(context) {
|
||||
var mode = {
|
||||
id: 'add-line',
|
||||
button: 'line',
|
||||
title: t('modes.add_line.title'),
|
||||
description: t('modes.add_line.description'),
|
||||
key: t('modes.add_line.key')
|
||||
key: '3'
|
||||
};
|
||||
|
||||
var behavior,
|
||||
defaultTags = {highway: 'residential'};
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
function start(loc) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
function startFromWay(other, loc, index) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(other.id, node.id, index));
|
||||
|
||||
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
function startFromNode(node) {
|
||||
var graph = history.graph(),
|
||||
parent = graph.parentWays(node)[0],
|
||||
isLine = parent && parent.geometry(graph) === 'line';
|
||||
|
||||
if (isLine && parent.first() === node.id) {
|
||||
controller.enter(iD.modes.DrawLine(parent.id, 'backward', graph));
|
||||
|
||||
} else if (isLine && parent.last() === node.id) {
|
||||
controller.enter(iD.modes.DrawLine(parent.id, 'forward', graph));
|
||||
|
||||
} else {
|
||||
var way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
|
||||
}
|
||||
}
|
||||
|
||||
function startFromMidpoint(midpoint) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node(),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddMidpoint(midpoint, node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
behavior = iD.behavior.AddWay(mode)
|
||||
var behavior = iD.behavior.AddWay(context)
|
||||
.on('start', start)
|
||||
.on('startFromWay', startFromWay)
|
||||
.on('startFromNode', startFromNode)
|
||||
.on('startFromMidpoint', startFromMidpoint);
|
||||
.on('startFromNode', startFromNode),
|
||||
defaultTags = {highway: 'residential'};
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail(t('modes.add_line.tail'));
|
||||
function start(loc) {
|
||||
var graph = context.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
context.enter(iD.modes.DrawLine(context, way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
function startFromWay(other, loc, index) {
|
||||
var graph = context.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id),
|
||||
iD.actions.AddVertex(other.id, node.id, index));
|
||||
|
||||
context.enter(iD.modes.DrawLine(context, way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
function startFromNode(node) {
|
||||
var graph = context.graph(),
|
||||
parent = graph.parentWays(node)[0],
|
||||
isLine = parent && parent.geometry(graph) === 'line';
|
||||
|
||||
if (isLine && parent.first() === node.id) {
|
||||
context.enter(iD.modes.DrawLine(context, parent.id, 'backward', graph));
|
||||
|
||||
} else if (isLine && parent.last() === node.id) {
|
||||
context.enter(iD.modes.DrawLine(context, parent.id, 'forward', graph));
|
||||
|
||||
} else {
|
||||
var way = iD.Way({tags: defaultTags});
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(way),
|
||||
iD.actions.AddVertex(way.id, node.id));
|
||||
|
||||
context.enter(iD.modes.DrawLine(context, way.id, 'forward', graph));
|
||||
}
|
||||
}
|
||||
|
||||
mode.enter = function() {
|
||||
context.install(behavior);
|
||||
context.tail(t('modes.add_line.tail'));
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.surface.call(behavior.off);
|
||||
context.uninstall(behavior);
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -1,58 +1,48 @@
|
||||
iD.modes.AddPoint = function() {
|
||||
iD.modes.AddPoint = function(context) {
|
||||
var mode = {
|
||||
id: 'add-point',
|
||||
title: t('modes.add_point.title'),
|
||||
description: t('modes.add_point.description'),
|
||||
key: t('modes.add_point.key')
|
||||
key: '2'
|
||||
};
|
||||
|
||||
var behavior;
|
||||
var behavior = iD.behavior.Draw(context)
|
||||
.on('click', add)
|
||||
.on('clickWay', addWay)
|
||||
.on('clickNode', addNode)
|
||||
.on('cancel', cancel)
|
||||
.on('finish', cancel);
|
||||
|
||||
function add(loc) {
|
||||
var node = iD.Node({loc: loc});
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
t('operations.add.annotation.point'));
|
||||
|
||||
context.enter(iD.modes.Select(context, [node.id], true));
|
||||
}
|
||||
|
||||
function addWay(way, loc, index) {
|
||||
add(loc);
|
||||
}
|
||||
|
||||
function addNode(node) {
|
||||
add(node.loc);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
context.enter(iD.modes.Browse(context));
|
||||
}
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
function add(loc) {
|
||||
var node = iD.Node({loc: loc});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
t('operations.add.annotation.point'));
|
||||
|
||||
controller.enter(iD.modes.Select([node.id], true));
|
||||
}
|
||||
|
||||
function addWay(way, loc, index) {
|
||||
add(loc);
|
||||
}
|
||||
|
||||
function addNode(node) {
|
||||
add(node.loc);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
|
||||
behavior = iD.behavior.Draw(map)
|
||||
.on('click', add)
|
||||
.on('clickWay', addWay)
|
||||
.on('clickNode', addNode)
|
||||
.on('clickMidpoint', addNode)
|
||||
.on('cancel', cancel)
|
||||
.on('finish', cancel);
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail(t('modes.add_point.tail'));
|
||||
context.install(behavior);
|
||||
context.tail(t('modes.add_point.tail'));
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
var map = mode.map,
|
||||
surface = map.surface;
|
||||
|
||||
map.tail(false);
|
||||
behavior.off(surface);
|
||||
context.uninstall(behavior);
|
||||
context.tail(false);
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
iD.modes.Browse = function() {
|
||||
iD.modes.Browse = function(context) {
|
||||
var mode = {
|
||||
button: 'browse',
|
||||
id: 'browse',
|
||||
title: t('modes.browse.title'),
|
||||
description: t('modes.browse.description'),
|
||||
key: t('modes.browse.key')
|
||||
key: '1'
|
||||
};
|
||||
|
||||
var behaviors;
|
||||
var behaviors = [
|
||||
iD.behavior.Hover(),
|
||||
iD.behavior.Select(context),
|
||||
iD.behavior.Lasso(context),
|
||||
iD.behavior.DragNode(context)];
|
||||
|
||||
mode.enter = function() {
|
||||
var surface = mode.map.surface;
|
||||
|
||||
behaviors = [
|
||||
iD.behavior.Hover(),
|
||||
iD.behavior.Select(mode),
|
||||
iD.behavior.DragNode(mode),
|
||||
iD.behavior.DragMidpoint(mode)];
|
||||
|
||||
behaviors.forEach(function(behavior) {
|
||||
behavior(surface);
|
||||
context.install(behavior);
|
||||
});
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
var surface = mode.map.surface;
|
||||
|
||||
behaviors.forEach(function(behavior) {
|
||||
behavior.off(surface);
|
||||
context.uninstall(behavior);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.modes.DrawArea = function(wayId, baseGraph) {
|
||||
iD.modes.DrawArea = function(context, wayId, baseGraph) {
|
||||
var mode = {
|
||||
button: 'area',
|
||||
id: 'draw-area'
|
||||
@@ -7,11 +7,11 @@ iD.modes.DrawArea = function(wayId, baseGraph) {
|
||||
var behavior;
|
||||
|
||||
mode.enter = function() {
|
||||
var way = mode.history.graph().entity(wayId),
|
||||
var way = context.entity(wayId),
|
||||
headId = way.nodes[way.nodes.length - 2],
|
||||
tailId = way.first();
|
||||
|
||||
behavior = iD.behavior.DrawWay(wayId, -1, mode, baseGraph);
|
||||
behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph);
|
||||
|
||||
var addNode = behavior.addNode;
|
||||
|
||||
@@ -23,12 +23,12 @@ iD.modes.DrawArea = function(wayId, baseGraph) {
|
||||
}
|
||||
};
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail(t('modes.draw_area.tail'));
|
||||
context.install(behavior);
|
||||
context.tail(t('modes.draw_area.tail'));
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.surface.call(behavior.off);
|
||||
context.uninstall(behavior);
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.modes.DrawLine = function(wayId, direction, baseGraph) {
|
||||
iD.modes.DrawLine = function(context, wayId, direction, baseGraph) {
|
||||
var mode = {
|
||||
button: 'line',
|
||||
id: 'draw-line'
|
||||
@@ -7,11 +7,11 @@ iD.modes.DrawLine = function(wayId, direction, baseGraph) {
|
||||
var behavior;
|
||||
|
||||
mode.enter = function() {
|
||||
var way = mode.history.graph().entity(wayId),
|
||||
var way = context.entity(wayId),
|
||||
index = (direction === 'forward') ? undefined : 0,
|
||||
headId = (direction === 'forward') ? way.last() : way.first();
|
||||
|
||||
behavior = iD.behavior.DrawWay(wayId, index, mode, baseGraph);
|
||||
behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph);
|
||||
|
||||
var addNode = behavior.addNode;
|
||||
|
||||
@@ -23,12 +23,12 @@ iD.modes.DrawLine = function(wayId, direction, baseGraph) {
|
||||
}
|
||||
};
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail(t('modes.draw_line.tail'));
|
||||
context.install(behavior);
|
||||
context.tail(t('modes.draw_line.tail'));
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.surface.call(behavior.off);
|
||||
context.uninstall(behavior);
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -1,60 +1,87 @@
|
||||
iD.modes.MoveWay = function(wayId) {
|
||||
iD.modes.MoveWay = function(context, wayId) {
|
||||
var mode = {
|
||||
id: 'move-way'
|
||||
id: 'move-way',
|
||||
button: 'browse'
|
||||
};
|
||||
|
||||
var keybinding = d3.keybinding('move-way');
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
graph = history.graph(),
|
||||
selection = map.surface,
|
||||
controller = mode.controller,
|
||||
projection = map.projection,
|
||||
way = graph.entity(wayId),
|
||||
origin = d3.mouse(selection.node()),
|
||||
annotation = t('operations.move.annotation.' + way.geometry(graph));
|
||||
var origin = context.map().mouseCoordinates(),
|
||||
nudgeInterval,
|
||||
annotation = t('operations.move.annotation.' + context.geometry(wayId));
|
||||
|
||||
// If intiated via keyboard
|
||||
if (!origin[0] && !origin[1]) origin = null;
|
||||
|
||||
history.perform(
|
||||
context.perform(
|
||||
iD.actions.Noop(),
|
||||
annotation);
|
||||
|
||||
function edge(point, size) {
|
||||
var pad = [30, 100, 30, 100];
|
||||
if (point[0] > size[0] - pad[0]) return [-10, 0];
|
||||
else if (point[0] < pad[2]) return [10, 0];
|
||||
else if (point[1] > size[1] - pad[1]) return [0, -10];
|
||||
else if (point[1] < pad[3]) return [0, 10];
|
||||
return null;
|
||||
}
|
||||
|
||||
function startNudge(nudge) {
|
||||
if (nudgeInterval) window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = window.setInterval(function() {
|
||||
context.pan(nudge);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function stopNudge() {
|
||||
if (nudgeInterval) window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = null;
|
||||
}
|
||||
|
||||
function point() {
|
||||
return d3.mouse(context.map().surface.node());
|
||||
}
|
||||
|
||||
function move() {
|
||||
var p = d3.mouse(selection.node()),
|
||||
delta = origin ?
|
||||
[p[0] - origin[0], p[1] - origin[1]] :
|
||||
[0, 0];
|
||||
var p = point();
|
||||
|
||||
origin = p;
|
||||
var delta = origin ?
|
||||
[p[0] - context.projection(origin)[0],
|
||||
p[1] - context.projection(origin)[1]] :
|
||||
[0, 0];
|
||||
|
||||
history.replace(
|
||||
iD.actions.MoveWay(wayId, delta, projection),
|
||||
var nudge = edge(p, context.map().size());
|
||||
if (nudge) startNudge(nudge);
|
||||
else stopNudge();
|
||||
|
||||
origin = context.map().mouseCoordinates();
|
||||
|
||||
context.replace(
|
||||
iD.actions.MoveWay(wayId, delta, context.projection),
|
||||
annotation);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
d3.event.stopPropagation();
|
||||
controller.enter(iD.modes.Select([way.id], true));
|
||||
context.enter(iD.modes.Select(context, [wayId], true));
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
history.pop();
|
||||
controller.enter(iD.modes.Select([way.id], true));
|
||||
context.pop();
|
||||
context.enter(iD.modes.Select(context, [wayId], true));
|
||||
}
|
||||
|
||||
function undone() {
|
||||
controller.enter(iD.modes.Browse());
|
||||
context.enter(iD.modes.Browse(context));
|
||||
}
|
||||
|
||||
selection
|
||||
context.surface()
|
||||
.on('mousemove.move-way', move)
|
||||
.on('click.move-way', finish);
|
||||
|
||||
history.on('undone.move-way', undone);
|
||||
context.history()
|
||||
.on('undone.move-way', undone);
|
||||
|
||||
keybinding
|
||||
.on('⎋', cancel)
|
||||
@@ -65,15 +92,12 @@ iD.modes.MoveWay = function(wayId) {
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
selection = map.surface;
|
||||
|
||||
selection
|
||||
context.surface()
|
||||
.on('mousemove.move-way', null)
|
||||
.on('click.move-way', null);
|
||||
|
||||
history.on('undone.move-way', null);
|
||||
context.history()
|
||||
.on('undone.move-way', null);
|
||||
|
||||
keybinding.off();
|
||||
};
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
iD.modes.Select = function(selection, initial) {
|
||||
iD.modes.Select = function(context, selection, initial) {
|
||||
var mode = {
|
||||
id: 'select',
|
||||
button: 'browse'
|
||||
};
|
||||
|
||||
var inspector = iD.ui.inspector().initial(!!initial),
|
||||
var inspector = iD.ui.Inspector().initial(!!initial),
|
||||
keybinding = d3.keybinding('select'),
|
||||
behaviors,
|
||||
timeout = null,
|
||||
behaviors = [
|
||||
iD.behavior.Hover(),
|
||||
iD.behavior.Select(context),
|
||||
iD.behavior.Lasso(context),
|
||||
iD.behavior.DragNode(context)],
|
||||
radialMenu;
|
||||
|
||||
function changeTags(d, tags) {
|
||||
if (!_.isEqual(singular().tags, tags)) {
|
||||
mode.history.perform(
|
||||
context.perform(
|
||||
iD.actions.ChangeTags(d.id, tags),
|
||||
t('operations.change_tags.annotation'));
|
||||
}
|
||||
@@ -19,41 +24,53 @@ iD.modes.Select = function(selection, initial) {
|
||||
|
||||
function singular() {
|
||||
if (selection.length === 1) {
|
||||
return mode.map.history().graph().entity(selection[0]);
|
||||
return context.entity(selection[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function positionMenu() {
|
||||
var entity = singular();
|
||||
|
||||
if (entity && entity.type === 'node') {
|
||||
radialMenu.center(context.projection(entity.loc));
|
||||
} else {
|
||||
radialMenu.center(d3.mouse(context.surface().node()));
|
||||
}
|
||||
}
|
||||
|
||||
function showMenu() {
|
||||
context.surface()
|
||||
.call(radialMenu.close)
|
||||
.call(radialMenu);
|
||||
}
|
||||
|
||||
mode.selection = function() {
|
||||
return selection;
|
||||
};
|
||||
|
||||
mode.reselect = function() {
|
||||
positionMenu();
|
||||
showMenu();
|
||||
};
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = map.history(),
|
||||
graph = history.graph(),
|
||||
surface = map.surface,
|
||||
entity = singular();
|
||||
|
||||
inspector
|
||||
.graph(graph)
|
||||
.presetData(map.connection().presetData());
|
||||
|
||||
behaviors = [
|
||||
iD.behavior.Hover(),
|
||||
iD.behavior.Select(mode),
|
||||
iD.behavior.DragNode(mode),
|
||||
iD.behavior.DragMidpoint(mode)];
|
||||
var entity = singular();
|
||||
|
||||
behaviors.forEach(function(behavior) {
|
||||
behavior(surface);
|
||||
context.install(behavior);
|
||||
});
|
||||
|
||||
var operations = d3.values(iD.operations)
|
||||
.map(function (o) { return o(selection, mode); })
|
||||
.filter(function (o) { return o.available(); });
|
||||
var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
|
||||
.map(function(o) { return o(selection, context); })
|
||||
.filter(function(o) { return o.available(); });
|
||||
operations.unshift(iD.operations.Delete(selection, context));
|
||||
|
||||
keybinding.on('⎋', function() {
|
||||
context.enter(iD.modes.Browse(context));
|
||||
});
|
||||
|
||||
operations.forEach(function(operation) {
|
||||
keybinding.on(operation.key, function () {
|
||||
keybinding.on(operation.key, function() {
|
||||
if (operation.enabled()) {
|
||||
operation();
|
||||
}
|
||||
@@ -66,9 +83,12 @@ iD.modes.Select = function(selection, initial) {
|
||||
}), true));
|
||||
|
||||
if (entity) {
|
||||
inspector.graph(graph);
|
||||
inspector
|
||||
.context(context)
|
||||
.presetData(context.connection().presetData());
|
||||
|
||||
d3.select('.inspector-wrap')
|
||||
context.container()
|
||||
.select('.inspector-wrap')
|
||||
.style('display', 'block')
|
||||
.style('opacity', 1)
|
||||
.datum(entity)
|
||||
@@ -77,38 +97,39 @@ iD.modes.Select = function(selection, initial) {
|
||||
if (d3.event) {
|
||||
// Pan the map if the clicked feature intersects with the position
|
||||
// of the inspector
|
||||
var inspector_size = d3.select('.inspector-wrap').size(),
|
||||
map_size = mode.map.size(),
|
||||
var inspector_size = context.container().select('.inspector-wrap').size(),
|
||||
map_size = context.map().size(),
|
||||
offset = 50,
|
||||
shift_left = d3.event.x - map_size[0] + inspector_size[0] + offset,
|
||||
shift_left = d3.event.clientX - map_size[0] + inspector_size[0] + offset,
|
||||
center = (map_size[0] / 2) + shift_left + offset;
|
||||
|
||||
if (shift_left > 0 && inspector_size[1] > d3.event.y) {
|
||||
mode.map.centerEase(mode.map.projection.invert([center, map_size[1]/2]));
|
||||
if (shift_left > 0 && inspector_size[1] > d3.event.clientY) {
|
||||
context.map().centerEase(context.projection.invert([center, map_size[1]/2]));
|
||||
}
|
||||
}
|
||||
|
||||
inspector
|
||||
.on('changeTags', changeTags)
|
||||
.on('close', function() { mode.controller.enter(iD.modes.Browse()); });
|
||||
|
||||
history.on('change.select', function() {
|
||||
// Exit mode if selected entity gets undone
|
||||
var oldEntity = entity,
|
||||
newEntity = history.graph().entity(selection[0]);
|
||||
|
||||
if (!newEntity) {
|
||||
mode.controller.enter(iD.modes.Browse());
|
||||
} else if (!_.isEqual(oldEntity.tags, newEntity.tags)) {
|
||||
inspector.tags(newEntity.tags);
|
||||
}
|
||||
|
||||
surface.call(radialMenu.close);
|
||||
});
|
||||
.on('close', function() { context.enter(iD.modes.Browse(context)); });
|
||||
}
|
||||
|
||||
map.on('move.select', function() {
|
||||
surface.call(radialMenu.close);
|
||||
context.history().on('change.select', function() {
|
||||
context.surface().call(radialMenu.close);
|
||||
|
||||
if (_.any(selection, function(id) { return !context.entity(id); })) {
|
||||
// Exit mode if selected entity gets undone
|
||||
context.enter(iD.modes.Browse(context));
|
||||
|
||||
} else if (entity) {
|
||||
var newEntity = context.entity(selection[0]);
|
||||
if (!_.isEqual(entity.tags, newEntity.tags)) {
|
||||
inspector.tags(newEntity.tags);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
context.map().on('move.select', function() {
|
||||
context.surface().call(radialMenu.close);
|
||||
});
|
||||
|
||||
function dblclick() {
|
||||
@@ -117,12 +138,32 @@ iD.modes.Select = function(selection, initial) {
|
||||
|
||||
if (datum instanceof iD.Way && !target.classed('fill')) {
|
||||
var choice = iD.geo.chooseIndex(datum,
|
||||
d3.mouse(mode.map.surface.node()), mode.map),
|
||||
d3.mouse(context.surface().node()), context),
|
||||
node = iD.Node({ loc: choice.loc });
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddVertex(datum.id, node.id, choice.index),
|
||||
var prev = datum.nodes[choice.index - 1],
|
||||
next = datum.nodes[choice.index],
|
||||
prevParents = context.graph().parentWays({ id: prev }),
|
||||
ways = [];
|
||||
|
||||
|
||||
for (var i = 0; i < prevParents.length; i++) {
|
||||
var p = prevParents[i];
|
||||
for (var k = 0; k < p.nodes.length; k++) {
|
||||
if (p.nodes[k] === prev) {
|
||||
if (p.nodes[k-1] === next) {
|
||||
ways.push({ id: p.id, index: k});
|
||||
break;
|
||||
} else if (p.nodes[k+1] === next) {
|
||||
ways.push({ id: p.id, index: k+1});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.perform(iD.actions.AddEntity(node),
|
||||
iD.actions.AddMidpoint({ ways: ways, loc: node.loc }, node),
|
||||
t('operations.add.annotation.vertex'));
|
||||
|
||||
d3.event.preventDefault();
|
||||
@@ -130,36 +171,49 @@ iD.modes.Select = function(selection, initial) {
|
||||
}
|
||||
}
|
||||
|
||||
function selected(entity) {
|
||||
if (!entity) return false;
|
||||
if (selection.indexOf(entity.id) >= 0) return true;
|
||||
return d3.select(this).classed('stroke') &&
|
||||
_.any(context.graph().parentRelations(entity), function(parent) {
|
||||
return selection.indexOf(parent.id) >= 0;
|
||||
});
|
||||
}
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
surface.on('dblclick.select', dblclick)
|
||||
context.surface()
|
||||
.selectAll("*")
|
||||
.filter(function (d) { return d && selection.indexOf(d.id) >= 0; })
|
||||
.filter(selected)
|
||||
.classed('selected', true);
|
||||
|
||||
radialMenu = iD.ui.RadialMenu(operations);
|
||||
var show = d3.event && !initial;
|
||||
|
||||
if (d3.event && !initial) {
|
||||
var loc = map.mouseCoordinates();
|
||||
if (show) {
|
||||
positionMenu();
|
||||
}
|
||||
|
||||
if (entity && entity.type === 'node') {
|
||||
loc = entity.loc;
|
||||
timeout = window.setTimeout(function() {
|
||||
if (show) {
|
||||
showMenu();
|
||||
}
|
||||
|
||||
surface.call(radialMenu, map.projection(loc));
|
||||
}
|
||||
context.surface()
|
||||
.on('dblclick.select', dblclick);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
mode.exit = function () {
|
||||
var surface = mode.map.surface,
|
||||
history = mode.history;
|
||||
|
||||
mode.exit = function() {
|
||||
if (singular()) {
|
||||
changeTags(singular(), inspector.tags());
|
||||
}
|
||||
|
||||
d3.select('.inspector-wrap')
|
||||
if (timeout) window.clearTimeout(timeout);
|
||||
|
||||
context.container()
|
||||
.select('.inspector-wrap')
|
||||
.style('display', 'none')
|
||||
.html('');
|
||||
|
||||
@@ -168,7 +222,7 @@ iD.modes.Select = function(selection, initial) {
|
||||
d3.selectAll('div.typeahead').remove();
|
||||
|
||||
behaviors.forEach(function(behavior) {
|
||||
behavior.off(surface);
|
||||
context.uninstall(behavior);
|
||||
});
|
||||
|
||||
var q = iD.util.stringQs(location.hash.substring(1));
|
||||
@@ -176,13 +230,14 @@ iD.modes.Select = function(selection, initial) {
|
||||
|
||||
keybinding.off();
|
||||
|
||||
history.on('change.select', null);
|
||||
context.history()
|
||||
.on('change.select', null);
|
||||
|
||||
surface.on('dblclick.select', null)
|
||||
context.surface()
|
||||
.call(radialMenu.close)
|
||||
.on('dblclick.select', null)
|
||||
.selectAll(".selected")
|
||||
.classed('selected', false);
|
||||
|
||||
surface.call(radialMenu.close);
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.OAuth = function() {
|
||||
iD.OAuth = function(context) {
|
||||
var baseurl = 'http://www.openstreetmap.org',
|
||||
o = {},
|
||||
keys,
|
||||
@@ -6,10 +6,6 @@ iD.OAuth = function() {
|
||||
|
||||
function keyclean(x) { return x.replace(/\W/g, ''); }
|
||||
|
||||
if (token('oauth_token')) {
|
||||
o.oauth_token = token('oauth_token');
|
||||
}
|
||||
|
||||
function timenonce(o) {
|
||||
o.oauth_timestamp = ohauth.timestamp();
|
||||
o.oauth_nonce = ohauth.nonce();
|
||||
@@ -17,11 +13,12 @@ iD.OAuth = function() {
|
||||
}
|
||||
|
||||
// token getter/setter, namespaced to the current `apibase` value.
|
||||
function token(k, x) {
|
||||
if (arguments.length == 2) {
|
||||
localStorage[keyclean(baseurl) + k] = x;
|
||||
}
|
||||
return localStorage[keyclean(baseurl) + k];
|
||||
function token() {
|
||||
return context.storage.apply(context, arguments);
|
||||
}
|
||||
|
||||
if (token('oauth_token')) {
|
||||
o.oauth_token = token('oauth_token');
|
||||
}
|
||||
|
||||
oauth.authenticated = function() {
|
||||
@@ -63,7 +60,7 @@ iD.OAuth = function() {
|
||||
o.oauth_signature = ohauth.signature(oauth_secret, '',
|
||||
ohauth.baseString('POST', url, o));
|
||||
|
||||
var l = iD.ui.loading('contacting openstreetmap...');
|
||||
var l = iD.ui.loading(context.container(), 'contacting openstreetmap...');
|
||||
|
||||
// it would make more sense to have this code within the callback
|
||||
// to oauth.xhr below. however, it needs to be directly within a
|
||||
@@ -112,7 +109,7 @@ iD.OAuth = function() {
|
||||
var request_token_secret = token('oauth_request_token_secret');
|
||||
o.oauth_signature = ohauth.signature(oauth_secret, request_token_secret,
|
||||
ohauth.baseString('POST', url, o));
|
||||
var l = iD.ui.loading('contacting openstreetmap...');
|
||||
var l = iD.ui.loading(context.container(), 'contacting openstreetmap...');
|
||||
|
||||
function accessTokenDone(err, xhr) {
|
||||
if (err) callback(err);
|
||||
|
||||
@@ -1 +1 @@
|
||||
iD.operations = {}
|
||||
iD.operations = {};
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
iD.operations.Circularize = function(selection, mode) {
|
||||
iD.operations.Circularize = function(selection, context) {
|
||||
var entityId = selection[0],
|
||||
history = mode.map.history(),
|
||||
action = iD.actions.Circularize(entityId, mode.map);
|
||||
action = iD.actions.Circularize(entityId, context.projection);
|
||||
|
||||
var operation = function() {
|
||||
var graph = history.graph(),
|
||||
entity = graph.entity(entityId),
|
||||
annotation = t('operations.circularize.annotation.' + entity.geometry(graph));
|
||||
|
||||
history.perform(action, annotation);
|
||||
var annotation = t('operations.circularize.annotation.' + context.geometry(entityId));
|
||||
context.perform(action, annotation);
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var graph = history.graph(),
|
||||
entity = graph.entity(entityId);
|
||||
return selection.length === 1 && entity.type === 'way';
|
||||
return selection.length === 1 &&
|
||||
context.entity(entityId).type === 'way';
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
var graph = history.graph();
|
||||
return action.enabled(graph);
|
||||
return action.enabled(context.graph());
|
||||
};
|
||||
|
||||
operation.id = "circularize";
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
iD.operations.Delete = function(selection, mode) {
|
||||
var entityId = selection[0],
|
||||
history = mode.map.history();
|
||||
|
||||
iD.operations.Delete = function(selection, context) {
|
||||
var operation = function() {
|
||||
var graph = history.graph(),
|
||||
entity = graph.entity(entityId),
|
||||
action = {way: iD.actions.DeleteWay, node: iD.actions.DeleteNode}[entity.type],
|
||||
annotation = t('operations.delete.annotation.' + entity.geometry(graph));
|
||||
var annotation;
|
||||
|
||||
history.perform(
|
||||
action(entityId),
|
||||
if (selection.length === 1) {
|
||||
annotation = t('operations.delete.annotation.' + context.geometry(selection[0]));
|
||||
} else {
|
||||
annotation = t('operations.delete.annotation.multiple', {n: selection.length});
|
||||
}
|
||||
|
||||
context.perform(
|
||||
iD.actions.DeleteMultiple(selection),
|
||||
annotation);
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var graph = history.graph(),
|
||||
entity = graph.entity(entityId);
|
||||
return selection.length === 1 &&
|
||||
(entity.type === 'way' || entity.type === 'node');
|
||||
return true;
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
iD.operations.Disconnect = function(selection, context) {
|
||||
var entityId = selection[0],
|
||||
action = iD.actions.Disconnect(entityId);
|
||||
|
||||
var operation = function() {
|
||||
context.perform(action, t('operations.disconnect.annotation'));
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
return selection.length === 1 &&
|
||||
context.geometry(entityId) === 'vertex';
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
return action.enabled(context.graph());
|
||||
};
|
||||
|
||||
operation.id = "disconnect";
|
||||
operation.key = t('operations.disconnect.key');
|
||||
operation.title = t('operations.disconnect.title');
|
||||
operation.description = t('operations.disconnect.description');
|
||||
|
||||
return operation;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
iD.operations.Merge = function(selection, context) {
|
||||
var join = iD.actions.Join(selection),
|
||||
merge = iD.actions.Merge(selection);
|
||||
|
||||
var operation = function() {
|
||||
var annotation = t('operations.merge.annotation', {n: selection.length}),
|
||||
action;
|
||||
|
||||
if (join.enabled(context.graph())) {
|
||||
action = join;
|
||||
} else {
|
||||
action = merge;
|
||||
}
|
||||
|
||||
var difference = context.perform(action, annotation);
|
||||
context.enter(iD.modes.Select(context, difference.extantIDs()));
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
return selection.length >= 2;
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
return join.enabled(context.graph()) ||
|
||||
merge.enabled(context.graph());
|
||||
};
|
||||
|
||||
operation.id = "merge";
|
||||
operation.key = t('operations.merge.key');
|
||||
operation.title = t('operations.merge.title');
|
||||
operation.description = t('operations.merge.description');
|
||||
|
||||
return operation;
|
||||
};
|
||||
@@ -1,15 +1,13 @@
|
||||
iD.operations.Move = function(selection, mode) {
|
||||
var entityId = selection[0],
|
||||
history = mode.map.history();
|
||||
iD.operations.Move = function(selection, context) {
|
||||
var entityId = selection[0];
|
||||
|
||||
var operation = function() {
|
||||
mode.controller.enter(iD.modes.MoveWay(entityId));
|
||||
context.enter(iD.modes.MoveWay(context, entityId));
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var graph = history.graph();
|
||||
return selection.length === 1 &&
|
||||
graph.entity(entityId).type === 'way';
|
||||
context.entity(entityId).type === 'way';
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
iD.operations.Orthogonalize = function(selection, context) {
|
||||
var entityId = selection[0],
|
||||
action = iD.actions.Orthogonalize(entityId, context.projection);
|
||||
|
||||
var operation = function() {
|
||||
var annotation = t('operations.orthogonalize.annotation.' + context.geometry(entityId));
|
||||
context.perform(action, annotation);
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
return selection.length === 1 &&
|
||||
context.entity(entityId).type === 'way';
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
return action.enabled(context.graph());
|
||||
};
|
||||
|
||||
operation.id = "orthogonalize";
|
||||
operation.key = t('operations.orthogonalize.key');
|
||||
operation.title = t('operations.orthogonalize.title');
|
||||
operation.description = t('operations.orthogonalize.description');
|
||||
|
||||
return operation;
|
||||
};
|
||||
@@ -1,18 +1,15 @@
|
||||
iD.operations.Reverse = function(selection, mode) {
|
||||
var entityId = selection[0],
|
||||
history = mode.map.history();
|
||||
iD.operations.Reverse = function(selection, context) {
|
||||
var entityId = selection[0];
|
||||
|
||||
var operation = function() {
|
||||
history.perform(
|
||||
iD.actions.ReverseWay(entityId),
|
||||
context.perform(
|
||||
iD.actions.Reverse(entityId),
|
||||
t('operations.reverse.annotation'));
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var graph = history.graph(),
|
||||
entity = graph.entity(entityId);
|
||||
return selection.length === 1 &&
|
||||
entity.geometry(graph) === 'line';
|
||||
context.geometry(entityId) === 'line';
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
iD.operations.Split = function(selection, mode) {
|
||||
iD.operations.Split = function(selection, context) {
|
||||
var entityId = selection[0],
|
||||
history = mode.map.history(),
|
||||
action = iD.actions.SplitWay(entityId);
|
||||
action = iD.actions.Split(entityId);
|
||||
|
||||
var operation = function() {
|
||||
history.perform(action, t('operations.split.annotation'));
|
||||
var annotation = t('operations.split.annotation'),
|
||||
difference = context.perform(action, annotation);
|
||||
context.enter(iD.modes.Select(context, difference.extantIDs()));
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var graph = history.graph(),
|
||||
entity = graph.entity(entityId);
|
||||
return selection.length === 1 &&
|
||||
entity.geometry(graph) === 'vertex';
|
||||
context.geometry(entityId) === 'vertex';
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
var graph = history.graph();
|
||||
return action.enabled(graph);
|
||||
return action.enabled(context.graph());
|
||||
};
|
||||
|
||||
operation.id = "split";
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
iD.operations.Unjoin = function(selection, mode) {
|
||||
var entityId = selection[0],
|
||||
history = mode.map.history(),
|
||||
action = iD.actions.UnjoinNode(entityId);
|
||||
|
||||
var operation = function() {
|
||||
history.perform(action, 'Unjoined lines.');
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var graph = history.graph(),
|
||||
entity = graph.entity(entityId);
|
||||
return selection.length === 1 &&
|
||||
entity.geometry(graph) === 'vertex';
|
||||
};
|
||||
|
||||
operation.enabled = function() {
|
||||
var graph = history.graph();
|
||||
return action.enabled(graph);
|
||||
};
|
||||
|
||||
operation.id = "unjoin";
|
||||
operation.key = t('operations.unjoin.key');
|
||||
operation.title = t('operations.unjoin.title');
|
||||
operation.description = t('operations.unjoin.description');
|
||||
|
||||
return operation;
|
||||
};
|
||||
@@ -12,16 +12,6 @@ iD.Background = function() {
|
||||
transformProp = iD.util.prefixCSSProperty('Transform'),
|
||||
source = d3.functor('');
|
||||
|
||||
var imgstyle = 'position:absolute;transform-origin:0 0;' +
|
||||
'-ms-transform-origin:0 0;' +
|
||||
'-webkit-transform-origin:0 0;' +
|
||||
'-moz-transform-origin:0 0;' +
|
||||
'-o-transform-origin:0 0;' +
|
||||
'-webkit-user-select: none;' +
|
||||
'-webkit-user-drag: none;' +
|
||||
'-moz-user-drag: none;' +
|
||||
'opacity:0;';
|
||||
|
||||
function tileSizeAtZoom(d, z) {
|
||||
return Math.ceil(tileSize[0] * Math.pow(2, z - d[2])) / tileSize[0];
|
||||
}
|
||||
@@ -63,7 +53,7 @@ iD.Background = function() {
|
||||
var sel = this,
|
||||
tiles = tile
|
||||
.scale(projection.scale())
|
||||
.scaleExtent(source.scaleExtent || [1, 17])
|
||||
.scaleExtent((source.data && source.data.scaleExtent) || [1, 17])
|
||||
.translate(projection.translate())(),
|
||||
requests = [],
|
||||
scaleExtent = tile.scaleExtent(),
|
||||
@@ -92,8 +82,7 @@ iD.Background = function() {
|
||||
cache[d[3]] = true;
|
||||
d3.select(this)
|
||||
.on('load', null)
|
||||
.transition()
|
||||
.style('opacity', 1);
|
||||
.classed('tile-loaded', true);
|
||||
background.apply(sel);
|
||||
}
|
||||
|
||||
@@ -119,19 +108,24 @@ iD.Background = function() {
|
||||
|
||||
image.exit()
|
||||
.style(transformProp, imageTransform)
|
||||
.transition()
|
||||
.style('opacity', 0)
|
||||
.remove();
|
||||
.classed('tile-loaded', false)
|
||||
.each(function() {
|
||||
var tile = this;
|
||||
window.setTimeout(function() {
|
||||
// this tile may already be removed
|
||||
if (tile.parentNode) {
|
||||
tile.parentNode.removeChild(tile);
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
image.enter().append('img')
|
||||
.attr('style', imgstyle)
|
||||
.attr('class', 'tile')
|
||||
.attr('src', function(d) { return d[3]; })
|
||||
.on('error', error)
|
||||
.on('load', load);
|
||||
|
||||
image.style(transformProp, imageTransform);
|
||||
|
||||
if (Object.keys(cache).length > 100) cache = {};
|
||||
}
|
||||
|
||||
background.offset = function(_) {
|
||||
@@ -158,9 +152,22 @@ iD.Background = function() {
|
||||
return background;
|
||||
};
|
||||
|
||||
function setPermalink(source) {
|
||||
var tag = source.data.sourcetag;
|
||||
var q = iD.util.stringQs(location.hash.substring(1));
|
||||
if (tag) {
|
||||
location.replace('#' + iD.util.qsString(_.assign(q, {
|
||||
layer: tag
|
||||
}), true));
|
||||
} else {
|
||||
location.replace('#' + iD.util.qsString(_.omit(q, 'layer'), true));
|
||||
}
|
||||
}
|
||||
|
||||
background.source = function(_) {
|
||||
if (!arguments.length) return source;
|
||||
source = _;
|
||||
setPermalink(source);
|
||||
return background;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
iD.BackgroundSource = {};
|
||||
|
||||
// derive the url of a 'quadkey' style tile from a coordinate object
|
||||
iD.BackgroundSource.template = function(template, subdomains, scaleExtent) {
|
||||
scaleExtent = scaleExtent || [0, 18];
|
||||
iD.BackgroundSource.template = function(data) {
|
||||
var generator = function(coord) {
|
||||
var u = '';
|
||||
for (var zoom = coord[2]; zoom > 0; zoom--) {
|
||||
@@ -12,18 +11,17 @@ iD.BackgroundSource.template = function(template, subdomains, scaleExtent) {
|
||||
if ((coord[1] & mask) !== 0) byte += 2;
|
||||
u += byte.toString();
|
||||
}
|
||||
// distribute requests against multiple domains
|
||||
var t = subdomains ?
|
||||
subdomains[coord[2] % subdomains.length] : '';
|
||||
return template
|
||||
.replace('{t}', t)
|
||||
|
||||
return data.template
|
||||
.replace('{t}', data.subdomains ?
|
||||
data.subdomains[coord[2] % data.subdomains.length] : '')
|
||||
.replace('{u}', u)
|
||||
.replace('{x}', coord[0])
|
||||
.replace('{y}', coord[1])
|
||||
.replace('{z}', coord[2]);
|
||||
};
|
||||
|
||||
generator.scaleExtent = scaleExtent;
|
||||
generator.data = data;
|
||||
|
||||
return generator;
|
||||
};
|
||||
@@ -31,21 +29,9 @@ iD.BackgroundSource.template = function(template, subdomains, scaleExtent) {
|
||||
iD.BackgroundSource.Custom = function() {
|
||||
var template = window.prompt('Enter a tile template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.');
|
||||
if (!template) return null;
|
||||
return iD.BackgroundSource.template(template, null, [0, 20]);
|
||||
return iD.BackgroundSource.template({
|
||||
template: template,
|
||||
name: 'Custom (customized)'
|
||||
});
|
||||
};
|
||||
|
||||
iD.BackgroundSource.Bing = iD.BackgroundSource.template(
|
||||
'http://ecn.t{t}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z',
|
||||
[0, 1, 2, 3], [0, 20]);
|
||||
|
||||
iD.BackgroundSource.Tiger2012 = iD.BackgroundSource.template(
|
||||
'http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png',
|
||||
['a', 'b', 'c'], [0, 17]);
|
||||
|
||||
iD.BackgroundSource.OSM = iD.BackgroundSource.template(
|
||||
'http://{t}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
['a', 'b', 'c'], [0, 18]);
|
||||
|
||||
iD.BackgroundSource.MapBox = iD.BackgroundSource.template(
|
||||
'http://{t}.tiles.mapbox.com/v3/openstreetmap.map-4wvf9l0l/{z}/{x}/{y}.jpg70',
|
||||
['a', 'b', 'c'], [0, 16]);
|
||||
iD.BackgroundSource.Custom.data = { 'name': 'Custom' };
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
iD.layers = iD.data.imagery.map(iD.BackgroundSource.template);
|
||||
|
||||
iD.layers.push((function() {
|
||||
function custom() {
|
||||
var template = window.prompt('Enter a tile template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.');
|
||||
if (!template) return null;
|
||||
if (template.match(/google/g)) return null;
|
||||
return iD.BackgroundSource.template({
|
||||
template: template,
|
||||
name: 'Custom (customized)'
|
||||
});
|
||||
}
|
||||
custom.data = { name: 'Custom' };
|
||||
return custom;
|
||||
})());
|
||||
@@ -1,6 +1,5 @@
|
||||
iD.Map = function() {
|
||||
var connection, history,
|
||||
dimensions = [],
|
||||
iD.Map = function(context) {
|
||||
var dimensions = [],
|
||||
dispatch = d3.dispatch('move', 'drawn'),
|
||||
projection = d3.geo.mercator().scale(1024),
|
||||
roundedProjection = iD.svg.RoundProjection(projection),
|
||||
@@ -20,13 +19,15 @@ iD.Map = function() {
|
||||
vertices = iD.svg.Vertices(roundedProjection),
|
||||
lines = iD.svg.Lines(roundedProjection),
|
||||
areas = iD.svg.Areas(roundedProjection),
|
||||
multipolygons = iD.svg.Multipolygons(roundedProjection),
|
||||
midpoints = iD.svg.Midpoints(roundedProjection),
|
||||
labels = iD.svg.Labels(roundedProjection),
|
||||
tail = d3.tail(),
|
||||
surface, tilegroup;
|
||||
|
||||
function map(selection) {
|
||||
context.history()
|
||||
.on('change.map', redraw);
|
||||
|
||||
selection.call(zoom);
|
||||
|
||||
tilegroup = selection.append('div')
|
||||
@@ -41,12 +42,16 @@ iD.Map = function() {
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
}, true)
|
||||
.on('mouseup.zoom', function() {
|
||||
if (resetTransform()) redraw();
|
||||
})
|
||||
.attr('id', 'surface')
|
||||
.call(iD.svg.Surface());
|
||||
|
||||
|
||||
map.size(selection.size());
|
||||
map.surface = surface;
|
||||
map.tilesurface = tilegroup;
|
||||
|
||||
supersurface
|
||||
.call(tail);
|
||||
@@ -57,46 +62,21 @@ iD.Map = function() {
|
||||
function drawVector(difference) {
|
||||
var filter, all,
|
||||
extent = map.extent(),
|
||||
graph = history.graph();
|
||||
|
||||
function addParents(parents) {
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
var parent = parents[i];
|
||||
if (only[parent.id] === undefined) {
|
||||
only[parent.id] = parent;
|
||||
addParents(graph.parentRelations(parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
graph = context.graph();
|
||||
|
||||
if (!difference) {
|
||||
all = graph.intersects(extent);
|
||||
filter = d3.functor(true);
|
||||
} else {
|
||||
var only = {};
|
||||
|
||||
for (var j = 0; j < difference.length; j++) {
|
||||
var id = difference[j],
|
||||
entity = graph.entity(id);
|
||||
|
||||
// Even if the entity is false (deleted), it needs to be
|
||||
// removed from the surface
|
||||
only[id] = entity;
|
||||
|
||||
if (entity && entity.intersects(extent, graph)) {
|
||||
addParents(graph.parentWays(only[id]));
|
||||
addParents(graph.parentRelations(only[id]));
|
||||
}
|
||||
}
|
||||
|
||||
all = _.compact(_.values(only));
|
||||
var complete = difference.complete(extent);
|
||||
all = _.compact(_.values(complete));
|
||||
filter = function(d) {
|
||||
if (d.type === 'midpoint') {
|
||||
for (var i = 0; i < d.ways.length; i++) {
|
||||
if (d.ways[i].id in only) return true;
|
||||
if (d.ways[i].id in complete) return true;
|
||||
}
|
||||
} else {
|
||||
return d.id in only;
|
||||
return d.id in complete;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -109,7 +89,6 @@ iD.Map = function() {
|
||||
.call(vertices, graph, all, filter)
|
||||
.call(lines, graph, all, filter)
|
||||
.call(areas, graph, all, filter)
|
||||
.call(multipolygons, graph, all, filter)
|
||||
.call(midpoints, graph, all, filter)
|
||||
.call(labels, graph, all, filter, dimensions, !difference);
|
||||
}
|
||||
@@ -120,11 +99,6 @@ iD.Map = function() {
|
||||
surface.selectAll('.layer *').remove();
|
||||
}
|
||||
|
||||
function connectionLoad(err, result) {
|
||||
history.merge(result);
|
||||
redraw(Object.keys(result));
|
||||
}
|
||||
|
||||
function zoomPan() {
|
||||
if (d3.event && d3.event.sourceEvent.type === 'dblclick') {
|
||||
if (!dblclickEnabled) {
|
||||
@@ -135,10 +109,10 @@ iD.Map = function() {
|
||||
}
|
||||
|
||||
if (Math.log(d3.event.scale / Math.LN2 - 8) < minzoom + 1) {
|
||||
iD.ui.flash()
|
||||
iD.ui.flash(context.container())
|
||||
.select('.content')
|
||||
.text('Cannot zoom out further in current mode.');
|
||||
return map.zoom(16);
|
||||
return setZoom(16, true);
|
||||
}
|
||||
|
||||
projection
|
||||
@@ -172,17 +146,26 @@ iD.Map = function() {
|
||||
}
|
||||
|
||||
function redraw(difference) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// If we are in the middle of a zoom/pan, we can't do differenced redraws.
|
||||
// It would result in artifacts where differenced entities are redrawn with
|
||||
// one transform and unchanged entities with another.
|
||||
if (resetTransform())
|
||||
if (resetTransform()) {
|
||||
difference = undefined;
|
||||
}
|
||||
|
||||
surface.attr('data-zoom', ~~map.zoom());
|
||||
tilegroup.call(background);
|
||||
var zoom = String(~~map.zoom());
|
||||
if (surface.attr('data-zoom') !== zoom) {
|
||||
surface.attr('data-zoom', zoom);
|
||||
}
|
||||
|
||||
if (!difference) {
|
||||
tilegroup.call(background);
|
||||
}
|
||||
|
||||
if (map.editable()) {
|
||||
connection.loadTiles(projection, dimensions);
|
||||
context.connection().loadTiles(projection, dimensions);
|
||||
drawVector(difference);
|
||||
} else {
|
||||
editOff();
|
||||
@@ -195,7 +178,11 @@ iD.Map = function() {
|
||||
return map;
|
||||
}
|
||||
|
||||
var queueRedraw = _.debounce(redraw, 200);
|
||||
var timeoutId;
|
||||
function queueRedraw() {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function() { redraw(); }, 300);
|
||||
}
|
||||
|
||||
function pointLocation(p) {
|
||||
var translate = projection.translate(),
|
||||
@@ -230,8 +217,8 @@ iD.Map = function() {
|
||||
return map;
|
||||
};
|
||||
|
||||
function setZoom(z) {
|
||||
if (z === map.zoom())
|
||||
function setZoom(z, force) {
|
||||
if (z === map.zoom() && !force)
|
||||
return false;
|
||||
var scale = 256 * Math.pow(2, z),
|
||||
center = pxCenter(),
|
||||
@@ -258,7 +245,6 @@ iD.Map = function() {
|
||||
t[0] - ll[0] + c[0],
|
||||
t[1] - ll[1] + c[1]]);
|
||||
zoom.translate(projection.translate());
|
||||
dispatch.move(map);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -268,7 +254,7 @@ iD.Map = function() {
|
||||
t[1] += d[1];
|
||||
projection.translate(t);
|
||||
zoom.translate(projection.translate());
|
||||
return map;
|
||||
return redraw();
|
||||
};
|
||||
|
||||
map.size = function(_) {
|
||||
@@ -328,39 +314,37 @@ iD.Map = function() {
|
||||
|
||||
map.extent = function(_) {
|
||||
if (!arguments.length) {
|
||||
return iD.geo.Extent(projection.invert([0, dimensions[1]]),
|
||||
return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
|
||||
projection.invert([dimensions[0], 0]));
|
||||
} else {
|
||||
var extent = iD.geo.Extent(_),
|
||||
tl = projection([extent[0][0], extent[1][1]]),
|
||||
br = projection([extent[1][0], extent[0][1]]);
|
||||
|
||||
// Calculate maximum zoom that fits extent
|
||||
var hFactor = (br[0] - tl[0]) / dimensions[0],
|
||||
vFactor = (br[1] - tl[1]) / dimensions[1],
|
||||
hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
|
||||
vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
|
||||
newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
|
||||
|
||||
map.centerZoom(extent.center(), newZoom);
|
||||
var extent = iD.geo.Extent(_);
|
||||
map.centerZoom(extent.center(), map.extentZoom(extent));
|
||||
}
|
||||
};
|
||||
|
||||
map.flush = function () {
|
||||
connection.flush();
|
||||
history.reset();
|
||||
return map;
|
||||
map.extentZoom = function(_) {
|
||||
var extent = iD.geo.Extent(_),
|
||||
tl = projection([extent[0][0], extent[1][1]]),
|
||||
br = projection([extent[1][0], extent[0][1]]);
|
||||
|
||||
// Calculate maximum zoom that fits extent
|
||||
var hFactor = (br[0] - tl[0]) / dimensions[0],
|
||||
vFactor = (br[1] - tl[1]) / dimensions[1],
|
||||
hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
|
||||
vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
|
||||
newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
|
||||
|
||||
return newZoom;
|
||||
};
|
||||
|
||||
map.connection = function(_) {
|
||||
if (!arguments.length) return connection;
|
||||
connection = _;
|
||||
connection.on('load.tile', connectionLoad);
|
||||
map.flush = function() {
|
||||
context.connection().flush();
|
||||
context.history().reset();
|
||||
return map;
|
||||
};
|
||||
|
||||
var usedTails = {};
|
||||
map.tail = function (_) {
|
||||
map.tail = function(_) {
|
||||
if (!_ || usedTails[_] === undefined) {
|
||||
tail.text(_);
|
||||
usedTails[_] = true;
|
||||
@@ -378,13 +362,6 @@ iD.Map = function() {
|
||||
return map;
|
||||
};
|
||||
|
||||
map.history = function (_) {
|
||||
if (!arguments.length) return history;
|
||||
history = _;
|
||||
history.on('change.map', redraw);
|
||||
return map;
|
||||
};
|
||||
|
||||
map.background = background;
|
||||
map.projection = projection;
|
||||
map.redraw = redraw;
|
||||
|
||||
@@ -40,7 +40,7 @@ iD.taginfo = function() {
|
||||
}
|
||||
|
||||
function popularValues(parameters) {
|
||||
return function(d) { return parseFloat(d['fraction']) > 0.01; };
|
||||
return function(d) { return parseFloat(d.fraction) > 0.01; };
|
||||
}
|
||||
|
||||
function valKey(d) { return { value: d.key }; }
|
||||
@@ -76,7 +76,7 @@ iD.taginfo = function() {
|
||||
page: 1
|
||||
}, parameters)), function(err, d) {
|
||||
if (err) return callback(err);
|
||||
callback(null, d.data.filter(popularValues()).map(valKeyDescription));
|
||||
callback(null, d.data.filter(popularValues()).map(valKeyDescription), parameters);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
iD.svg = {
|
||||
RoundProjection: function (projection) {
|
||||
return function (d) {
|
||||
RoundProjection: function(projection) {
|
||||
return function(d) {
|
||||
return iD.geo.roundCoords(projection(d));
|
||||
};
|
||||
},
|
||||
|
||||
PointTransform: function (projection) {
|
||||
return function (entity) {
|
||||
return 'translate(' + projection(entity.loc) + ')';
|
||||
PointTransform: function(projection) {
|
||||
return function(entity) {
|
||||
// http://jsperf.com/short-array-join
|
||||
var pt = projection(entity.loc);
|
||||
return 'translate(' + pt[0] + ',' + pt[1] + ')';
|
||||
};
|
||||
},
|
||||
|
||||
LineString: function (projection, graph) {
|
||||
LineString: function(projection, graph) {
|
||||
var cache = {};
|
||||
return function (entity) {
|
||||
return function(entity) {
|
||||
if (cache[entity.id] !== undefined) {
|
||||
return cache[entity.id];
|
||||
}
|
||||
@@ -23,7 +25,22 @@ iD.svg = {
|
||||
}
|
||||
|
||||
return (cache[entity.id] =
|
||||
'M' + graph.childNodes(entity).map(function (n) { return projection(n.loc); }).join('L'));
|
||||
}
|
||||
'M' + graph.childNodes(entity).map(function(n) {
|
||||
var pt = projection(n.loc);
|
||||
return pt[0] + ',' + pt[1];
|
||||
}).join('L'));
|
||||
};
|
||||
},
|
||||
|
||||
MultipolygonMemberTags: function(graph) {
|
||||
return function(entity) {
|
||||
var tags = entity.tags;
|
||||
graph.parentRelations(entity).forEach(function(relation) {
|
||||
if (relation.isMultipolygon()) {
|
||||
tags = _.extend({}, relation.tags, tags);
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,38 +1,39 @@
|
||||
iD.svg.Areas = function(projection) {
|
||||
return function drawAreas(surface, graph, entities, filter) {
|
||||
var areas = [];
|
||||
var path = d3.geo.path().projection(projection),
|
||||
areas = [];
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
if (entity.geometry(graph) === 'area') {
|
||||
var points = graph.childNodes(entity).map(function(n) {
|
||||
return projection(n.loc);
|
||||
});
|
||||
|
||||
areas.push({
|
||||
entity: entity,
|
||||
area: entity.isDegenerate() ? 0 : Math.abs(d3.geom.polygon(points).area())
|
||||
area: Math.abs(path.area(entity.asGeoJSON(graph)))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
areas.sort(function(a, b) { return b.area - a.area; });
|
||||
|
||||
var lineString = iD.svg.LineString(projection, graph);
|
||||
function drawPaths(group, areas, filter, klass) {
|
||||
var tagClasses = iD.svg.TagClasses();
|
||||
|
||||
if (klass === 'stroke') {
|
||||
tagClasses.tags(iD.svg.MultipolygonMemberTags(graph));
|
||||
}
|
||||
|
||||
function drawPaths(group, areas, filter, classes) {
|
||||
var paths = group.selectAll('path.area')
|
||||
.filter(filter)
|
||||
.data(areas, iD.Entity.key);
|
||||
|
||||
paths.enter()
|
||||
.append('path')
|
||||
.attr('class', classes);
|
||||
.attr('class', function(d) { return d.type + ' area ' + klass; });
|
||||
|
||||
paths
|
||||
.order()
|
||||
.attr('d', lineString)
|
||||
.call(iD.svg.TagClasses())
|
||||
.attr('d', function(entity) { return path(entity.asGeoJSON(graph)); })
|
||||
.call(tagClasses)
|
||||
.call(iD.svg.MemberClasses(graph));
|
||||
|
||||
paths.exit()
|
||||
@@ -43,9 +44,14 @@ iD.svg.Areas = function(projection) {
|
||||
|
||||
areas = _.pluck(areas, 'entity');
|
||||
|
||||
var strokes = areas.filter(function(area) {
|
||||
return area.type === 'way';
|
||||
});
|
||||
|
||||
var fill = surface.select('.layer-fill'),
|
||||
stroke = surface.select('.layer-stroke'),
|
||||
fills = drawPaths(fill, areas, filter, 'way area fill'),
|
||||
strokes = drawPaths(stroke, areas, filter, 'way area stroke');
|
||||
stroke = surface.select('.layer-stroke');
|
||||
|
||||
drawPaths(fill, areas, filter, 'fill');
|
||||
drawPaths(stroke, strokes, filter, 'stroke');
|
||||
};
|
||||
};
|
||||
|
||||
@@ -33,8 +33,11 @@ iD.svg.Labels = function(projection) {
|
||||
var font_sizes = label_stack.map(function(d) {
|
||||
var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]);
|
||||
var m = style && style.cssText.match("font-size: ([0-9]{1,2})px;");
|
||||
if (!m) return default_size;
|
||||
return parseInt(m[1], 10);
|
||||
if (m) return parseInt(m[1], 10);
|
||||
style = iD.util.getStyle('text.' + d[0]);
|
||||
m = style && style.cssText.match("font-size: ([0-9]{1,2})px;");
|
||||
if (m) return parseInt(m[1], 10);
|
||||
return default_size;
|
||||
});
|
||||
|
||||
var pointOffsets = [
|
||||
@@ -79,7 +82,7 @@ iD.svg.Labels = function(projection) {
|
||||
'startOffset': '50%',
|
||||
'xlink:href': function(d, i) { return '#halo-' + d.id; }
|
||||
})
|
||||
.text(function(d, i) { return d.tags.name; });
|
||||
.text(function(d, i) { return name(d); });
|
||||
|
||||
texts.exit().remove();
|
||||
|
||||
@@ -116,14 +119,14 @@ iD.svg.Labels = function(projection) {
|
||||
'x': function(d, i) {
|
||||
var x = labels[i].x - 2;
|
||||
if (labels[i].textAnchor === 'middle') {
|
||||
x -= textWidth(d.tags.name, labels[i].height) / 2;
|
||||
x -= textWidth(name(d), labels[i].height) / 2;
|
||||
}
|
||||
return x;
|
||||
},
|
||||
'y': function(d, i) { return labels[i].y - labels[i].height + 1 - 2; },
|
||||
'rx': 3,
|
||||
'ry': 3,
|
||||
'width': function(d, i) { return textWidth(d.tags.name, labels[i].height) + 4; },
|
||||
'width': function(d, i) { return textWidth(name(d), labels[i].height) + 4; },
|
||||
'height': function(d, i) { return labels[i].height + 4; },
|
||||
'fill': 'white'
|
||||
});
|
||||
@@ -146,8 +149,8 @@ iD.svg.Labels = function(projection) {
|
||||
.attr('y', get(labels, 'y'))
|
||||
.attr('transform', get(labels, 'transform'))
|
||||
.style('text-anchor', get(labels, 'textAnchor'))
|
||||
.text(function(d) { return d.tags.name; })
|
||||
.each(function(d, i) { textWidth(d.tags.name, labels[i].height, this); });
|
||||
.text(function(d) { return name(d); })
|
||||
.each(function(d, i) { textWidth(name(d), labels[i].height, this); });
|
||||
|
||||
texts.exit().remove();
|
||||
return texts;
|
||||
@@ -200,33 +203,56 @@ iD.svg.Labels = function(projection) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
function hideOnMouseover() {
|
||||
var mouse = d3.mouse(this),
|
||||
var mouse = mousePosition(d3.event),
|
||||
pad = 50,
|
||||
rect = new RTree.Rectangle(mouse[0] - pad, mouse[1] - pad, 2*pad, 2*pad),
|
||||
labels = _.pluck(rtree.search(rect, this), 'leaf'),
|
||||
containsLabel = iD.util.trueObj(labels),
|
||||
selection = d3.select(this);
|
||||
|
||||
// ensures that simply resetting opacity
|
||||
// does not force style recalculation
|
||||
function resetOpacity() {
|
||||
if (this._opacity !== '') {
|
||||
this.style.opacity = '';
|
||||
this._opacity = '';
|
||||
}
|
||||
}
|
||||
|
||||
selection.selectAll('.layer-label text, .layer-halo path, .layer-halo rect')
|
||||
.style('opacity', '');
|
||||
.each(resetOpacity);
|
||||
|
||||
if (!labels.length) return;
|
||||
selection.selectAll('.layer-label text, .layer-halo path, .layer-halo rect')
|
||||
.filter(function(d) {
|
||||
return _.contains(labels, d.id);
|
||||
return containsLabel[d.id];
|
||||
})
|
||||
.style('opacity', 0);
|
||||
.style('opacity', 0)
|
||||
.property('_opacity', 0);
|
||||
}
|
||||
|
||||
function name(d) {
|
||||
return d.tags[lang] || d.tags.name;
|
||||
}
|
||||
|
||||
var rtree = new RTree(),
|
||||
rectangles = {};
|
||||
rectangles = {},
|
||||
lang = 'name:' + iD.detect().locale.toLowerCase().split('-')[0],
|
||||
mousePosition, cacheDimensions;
|
||||
|
||||
return function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
|
||||
|
||||
if (!mousePosition || dimensions.join(',') !== cacheDimensions) {
|
||||
mousePosition = iD.util.fastMouse(surface.node().parentNode);
|
||||
cacheDimensions = dimensions.join(',');
|
||||
}
|
||||
|
||||
d3.select(surface.node().parentNode)
|
||||
.on('mousemove.hidelabels', hideOnMouseover);
|
||||
|
||||
var hidePoints = !d3.select('.node.point').node();
|
||||
var hidePoints = !surface.select('.node.point').node();
|
||||
|
||||
var labelable = [], i, k, entity;
|
||||
for (i = 0; i < label_stack.length; i++) labelable.push([]);
|
||||
@@ -243,7 +269,7 @@ iD.svg.Labels = function(projection) {
|
||||
// Split entities into groups specified by label_stack
|
||||
for (i = 0; i < entities.length; i++) {
|
||||
entity = entities[i];
|
||||
if (!entity.tags.name) continue;
|
||||
if (!name(entity)) continue;
|
||||
if (hidePoints && entity.geometry(graph) === 'point') continue;
|
||||
for (k = 0; k < label_stack.length; k ++) {
|
||||
if (entity.geometry(graph) === label_stack[k][0] &&
|
||||
@@ -254,7 +280,6 @@ iD.svg.Labels = function(projection) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var positions = {
|
||||
point: [],
|
||||
line: [],
|
||||
@@ -272,7 +297,7 @@ iD.svg.Labels = function(projection) {
|
||||
var font_size = font_sizes[k];
|
||||
for (i = 0; i < labelable[k].length; i ++) {
|
||||
entity = labelable[k][i];
|
||||
var width = textWidth(entity.tags.name, font_size),
|
||||
var width = textWidth(name(entity), font_size),
|
||||
p;
|
||||
if (entity.geometry(graph) === 'point') {
|
||||
p = getPointLabel(entity, width, font_size);
|
||||
@@ -282,7 +307,7 @@ iD.svg.Labels = function(projection) {
|
||||
p = getAreaLabel(entity, width, font_size);
|
||||
}
|
||||
if (p) {
|
||||
p.classes = entity.geometry(graph) + ' tag-' + label_stack[k].slice(1).join('-');
|
||||
p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
|
||||
positions[entity.geometry(graph)].push(p);
|
||||
labelled[entity.geometry(graph)].push(entity);
|
||||
}
|
||||
@@ -333,9 +358,8 @@ iD.svg.Labels = function(projection) {
|
||||
}
|
||||
|
||||
function getAreaLabel(entity, width, height) {
|
||||
var nodes = _.pluck(graph.childNodes(entity), 'loc')
|
||||
.map(iD.svg.RoundProjection(projection)),
|
||||
centroid = d3.geom.polygon(nodes).centroid(),
|
||||
var path = d3.geo.path().projection(projection),
|
||||
centroid = path.centroid(entity.asGeoJSON(graph)),
|
||||
extent = entity.extent(graph),
|
||||
entitywidth = projection(extent[1])[0] - projection(extent[0])[0];
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
iD.svg.Lines = function(projection) {
|
||||
|
||||
var arrowtext = '►\u3000\u3000',
|
||||
var arrowtext = '►\u3000\u3000\u3000',
|
||||
alength;
|
||||
|
||||
var highway_stack = {
|
||||
@@ -34,19 +34,25 @@ iD.svg.Lines = function(projection) {
|
||||
}
|
||||
|
||||
return function drawLines(surface, graph, entities, filter) {
|
||||
function drawPaths(group, lines, filter, classes, lineString) {
|
||||
function drawPaths(group, lines, filter, klass, lineString) {
|
||||
var tagClasses = iD.svg.TagClasses();
|
||||
|
||||
if (klass === 'stroke') {
|
||||
tagClasses.tags(iD.svg.MultipolygonMemberTags(graph));
|
||||
}
|
||||
|
||||
var paths = group.selectAll('path.line')
|
||||
.filter(filter)
|
||||
.data(lines, iD.Entity.key);
|
||||
|
||||
paths.enter()
|
||||
.append('path')
|
||||
.attr('class', classes);
|
||||
.attr('class', 'way line ' + klass);
|
||||
|
||||
paths
|
||||
.order()
|
||||
.attr('d', lineString)
|
||||
.call(iD.svg.TagClasses())
|
||||
.call(tagClasses)
|
||||
.call(iD.svg.MemberClasses(graph));
|
||||
|
||||
paths.exit()
|
||||
@@ -56,13 +62,16 @@ iD.svg.Lines = function(projection) {
|
||||
}
|
||||
|
||||
if (!alength) {
|
||||
var arrow = surface.append('text').text(arrowtext);
|
||||
var container = surface.append('g')
|
||||
.attr('class', 'oneway'),
|
||||
arrow = container.append('text')
|
||||
.attr('class', 'textpath')
|
||||
.text(arrowtext);
|
||||
alength = arrow.node().getComputedTextLength();
|
||||
arrow.remove();
|
||||
container.remove();
|
||||
}
|
||||
|
||||
var lines = [],
|
||||
lineStrings = {};
|
||||
var lines = [];
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
@@ -80,13 +89,13 @@ iD.svg.Lines = function(projection) {
|
||||
stroke = surface.select('.layer-stroke'),
|
||||
defs = surface.select('defs'),
|
||||
text = surface.select('.layer-text'),
|
||||
shadows = drawPaths(shadow, lines, filter, 'way line shadow', lineString),
|
||||
casings = drawPaths(casing, lines, filter, 'way line casing', lineString),
|
||||
strokes = drawPaths(stroke, lines, filter, 'way line stroke', lineString);
|
||||
shadows = drawPaths(shadow, lines, filter, 'shadow', lineString),
|
||||
casings = drawPaths(casing, lines, filter, 'casing', lineString),
|
||||
strokes = drawPaths(stroke, lines, filter, 'stroke', lineString);
|
||||
|
||||
// Determine the lengths of oneway paths
|
||||
var lengths = {},
|
||||
oneways = strokes.filter(function (d) { return d.isOneWay(); }).each(function(d) {
|
||||
oneways = strokes.filter(function(d) { return d.isOneWay(); }).each(function(d) {
|
||||
lengths[d.id] = Math.floor(this.getTotalLength() / alength);
|
||||
}).data();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ iD.svg.MemberClasses = function(graph) {
|
||||
classes += ' member';
|
||||
}
|
||||
|
||||
relations.forEach(function (relation) {
|
||||
relations.forEach(function(relation) {
|
||||
classes += ' member-type-' + relation.tags.type;
|
||||
classes += ' member-role-' + relation.memberById(d.id).role;
|
||||
});
|
||||
|
||||
@@ -2,6 +2,10 @@ iD.svg.Midpoints = function(projection) {
|
||||
return function drawMidpoints(surface, graph, entities, filter) {
|
||||
var midpoints = {};
|
||||
|
||||
if (!surface.select('.layer-hit g.vertex').node()) {
|
||||
return surface.selectAll('.layer-hit g.midpoint').remove();
|
||||
}
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
if (entities[i].type !== 'way') continue;
|
||||
|
||||
@@ -31,14 +35,14 @@ iD.svg.Midpoints = function(projection) {
|
||||
|
||||
var groups = surface.select('.layer-hit').selectAll('g.midpoint')
|
||||
.filter(filter)
|
||||
.data(_.values(midpoints), function (d) { return d.id; });
|
||||
.data(_.values(midpoints), function(d) { return d.id; });
|
||||
|
||||
var group = groups.enter()
|
||||
.insert('g', ':first-child')
|
||||
.attr('class', 'midpoint');
|
||||
|
||||
group.append('circle')
|
||||
.attr('r', 7)
|
||||
.attr('r', 8)
|
||||
.attr('class', 'shadow');
|
||||
|
||||
group.append('circle')
|
||||
@@ -47,7 +51,9 @@ iD.svg.Midpoints = function(projection) {
|
||||
|
||||
groups.attr('transform', iD.svg.PointTransform(projection));
|
||||
|
||||
groups.select('circle');
|
||||
// Propagate data bindings.
|
||||
groups.select('circle.shadow');
|
||||
groups.select('circle.fill');
|
||||
|
||||
groups.exit()
|
||||
.remove();
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
iD.svg.Multipolygons = function(projection) {
|
||||
return function(surface, graph, entities, filter) {
|
||||
var multipolygons = [];
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
if (entity.geometry(graph) === 'relation' && entity.tags.type === 'multipolygon') {
|
||||
multipolygons.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
var lineStrings = {};
|
||||
|
||||
function lineString(entity) {
|
||||
if (lineStrings[entity.id] !== undefined) {
|
||||
return lineStrings[entity.id];
|
||||
}
|
||||
|
||||
var multipolygon = entity.multipolygon(graph);
|
||||
if (entity.members.length === 0 || !multipolygon) {
|
||||
return (lineStrings[entity.id] = null);
|
||||
}
|
||||
|
||||
multipolygon = _.flatten(multipolygon, true);
|
||||
return (lineStrings[entity.id] =
|
||||
multipolygon.map(function (ring) {
|
||||
return 'M' + ring.map(function (node) { return projection(node.loc); }).join('L');
|
||||
}).join(""));
|
||||
}
|
||||
|
||||
function drawPaths(group, multipolygons, filter, classes) {
|
||||
var paths = group.selectAll('path.multipolygon')
|
||||
.filter(filter)
|
||||
.data(multipolygons, iD.Entity.key);
|
||||
|
||||
paths.enter()
|
||||
.append('path')
|
||||
.attr('class', classes);
|
||||
|
||||
paths
|
||||
.order()
|
||||
.attr('d', lineString)
|
||||
.call(iD.svg.TagClasses())
|
||||
.call(iD.svg.MemberClasses(graph));
|
||||
|
||||
paths.exit()
|
||||
.remove();
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
var fill = surface.select('.layer-fill'),
|
||||
paths = drawPaths(fill, multipolygons, filter, 'relation multipolygon');
|
||||
};
|
||||
};
|
||||
@@ -52,6 +52,7 @@ iD.svg.Points = function(projection) {
|
||||
// sets the data (point entity) on the element
|
||||
groups.select('image')
|
||||
.attr('xlink:href', imageHref);
|
||||
groups.select('.shadow, .stroke');
|
||||
|
||||
groups.exit()
|
||||
.remove();
|
||||
@@ -79,7 +80,7 @@ iD.svg.Points.imageIndex = [
|
||||
},
|
||||
{
|
||||
tags: { man_made: 'lighthouse' },
|
||||
icon: 'lighthouselevel_crossing'
|
||||
icon: 'lighthouse'
|
||||
},
|
||||
{
|
||||
tags: { natural: 'peak' },
|
||||
|
||||
@@ -3,7 +3,7 @@ iD.svg.Surface = function() {
|
||||
selection.append('defs');
|
||||
|
||||
var layers = selection.selectAll('.layer')
|
||||
.data(['shadow', 'fill', 'casing', 'stroke', 'text', 'hit', 'halo', 'label']);
|
||||
.data(['fill', 'shadow', 'casing', 'stroke', 'text', 'hit', 'halo', 'label']);
|
||||
|
||||
layers.enter().append('g')
|
||||
.attr('class', function(d) { return 'layer layer-' + d; });
|
||||
|
||||
@@ -3,10 +3,11 @@ iD.svg.TagClasses = function() {
|
||||
'highway', 'railway', 'waterway', 'power', 'motorway', 'amenity',
|
||||
'natural', 'landuse', 'building', 'oneway', 'bridge', 'boundary',
|
||||
'leisure', 'construction'
|
||||
]), tagClassRe = /^tag-/;
|
||||
]), tagClassRe = /^tag-/,
|
||||
tags = function(entity) { return entity.tags; };
|
||||
|
||||
return function tagClassesSelection(selection) {
|
||||
selection.each(function tagClassesEach(d, i) {
|
||||
var tagClasses = function(selection) {
|
||||
selection.each(function tagClassesEach(entity) {
|
||||
var classes, value = this.className;
|
||||
|
||||
if (value.baseVal !== undefined) value = value.baseVal;
|
||||
@@ -15,11 +16,10 @@ iD.svg.TagClasses = function() {
|
||||
return name.length && !tagClassRe.test(name);
|
||||
}).join(' ');
|
||||
|
||||
var tags = d.tags;
|
||||
for (var k in tags) {
|
||||
var t = tags(entity);
|
||||
for (var k in t) {
|
||||
if (!keys[k]) continue;
|
||||
classes += ' tag-' + k + ' ' +
|
||||
'tag-' + k + '-' + tags[k];
|
||||
classes += ' tag-' + k + ' ' + 'tag-' + k + '-' + t[k];
|
||||
}
|
||||
|
||||
classes = classes.trim();
|
||||
@@ -29,4 +29,12 @@ iD.svg.TagClasses = function() {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
tagClasses.tags = function(_) {
|
||||
if (!arguments.length) return tags;
|
||||
tags = _;
|
||||
return tagClasses;
|
||||
};
|
||||
|
||||
return tagClasses;
|
||||
};
|
||||
|
||||
@@ -23,15 +23,15 @@ iD.svg.Vertices = function(projection) {
|
||||
|
||||
group.append('circle')
|
||||
.attr('r', 10)
|
||||
.attr('class', 'shadow');
|
||||
.attr('class', 'node vertex shadow');
|
||||
|
||||
group.append('circle')
|
||||
.attr('r', 6)
|
||||
.attr('class', 'stroke');
|
||||
.attr('r', 4)
|
||||
.attr('class', 'node vertex stroke');
|
||||
|
||||
group.append('circle')
|
||||
.attr('r', 3)
|
||||
.attr('class', 'fill');
|
||||
.attr('class', 'node vertex fill');
|
||||
|
||||
groups.attr('transform', iD.svg.PointTransform(projection))
|
||||
.call(iD.svg.TagClasses())
|
||||
@@ -40,8 +40,7 @@ iD.svg.Vertices = function(projection) {
|
||||
|
||||
// Selecting the following implicitly
|
||||
// sets the data (vertix entity) on the elements
|
||||
groups.select('circle.fill');
|
||||
groups.select('circle.stroke');
|
||||
groups.select('circle.fill, circle.stroke, circle.shadow');
|
||||
|
||||
groups.exit()
|
||||
.remove();
|
||||
|
||||
@@ -1 +1,144 @@
|
||||
iD.ui = {};
|
||||
iD.ui = function(context) {
|
||||
return function(container) {
|
||||
context.container(container);
|
||||
|
||||
var history = context.history(),
|
||||
map = context.map();
|
||||
|
||||
if (!iD.detect().support) {
|
||||
container
|
||||
.text(t('browser_notice'))
|
||||
.style({
|
||||
'text-align': 'center',
|
||||
'font-style': 'italic'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (iD.detect().opera) container.classed('opera', true);
|
||||
|
||||
var m = container.append('div')
|
||||
.attr('id', 'map')
|
||||
.call(map);
|
||||
|
||||
var bar = container.append('div')
|
||||
.attr('id', 'bar')
|
||||
.attr('class','pad1 fillD');
|
||||
|
||||
var limiter = bar.append('div')
|
||||
.attr('class', 'limiter');
|
||||
|
||||
limiter.append('div')
|
||||
.attr('class', 'button-wrap joined col4')
|
||||
.call(iD.ui.Modes(context), limiter);
|
||||
|
||||
limiter.append('div')
|
||||
.attr('class', 'button-wrap joined col1')
|
||||
.call(iD.ui.UndoRedo(context));
|
||||
|
||||
limiter.append('div')
|
||||
.attr('class', 'button-wrap col1')
|
||||
.call(iD.ui.Save(context));
|
||||
|
||||
container.append('div')
|
||||
.attr('class', 'map-control zoombuttons')
|
||||
.call(iD.ui.Zoom(context));
|
||||
|
||||
container.append('div')
|
||||
.attr('class', 'map-control geocode-control')
|
||||
.call(iD.ui.Geocoder(context));
|
||||
|
||||
container.append('div')
|
||||
.attr('class', 'map-control layerswitcher-control')
|
||||
.call(iD.ui.LayerSwitcher(context));
|
||||
|
||||
container.append('div')
|
||||
.attr('class', 'map-control geolocate-control')
|
||||
.call(iD.ui.Geolocate(map));
|
||||
|
||||
container.append('div')
|
||||
.style('display', 'none')
|
||||
.attr('class', 'inspector-wrap fr col5');
|
||||
|
||||
var about = container.append('div')
|
||||
.attr('class','col12 about-block fillD pad1');
|
||||
|
||||
about.append('div')
|
||||
.attr('class', 'account')
|
||||
.call(iD.ui.Account(context));
|
||||
|
||||
var linkList = about.append('ul')
|
||||
.attr('id', 'about')
|
||||
.attr('class', 'pad1 fillD about-block link-list');
|
||||
|
||||
linkList.append('li')
|
||||
.append('a')
|
||||
.attr('target', '_blank')
|
||||
.attr('href', 'http://github.com/systemed/iD')
|
||||
.text(iD.version);
|
||||
|
||||
linkList.append('li')
|
||||
.append('a')
|
||||
.attr('target', '_blank')
|
||||
.attr('href', 'http://github.com/systemed/iD/issues')
|
||||
.text(t('report_a_bug'));
|
||||
|
||||
linkList.append('li')
|
||||
.attr('class', 'attribution')
|
||||
.call(iD.ui.Attribution(context));
|
||||
|
||||
linkList.append('li')
|
||||
.attr('class', 'source-switch')
|
||||
.call(iD.ui.SourceSwitch(context));
|
||||
|
||||
linkList.append('li')
|
||||
.attr('class', 'user-list')
|
||||
.call(iD.ui.Contributors(context));
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
history.save();
|
||||
if (history.hasChanges()) return t('save.unsaved_changes');
|
||||
};
|
||||
|
||||
d3.select(window).on('resize.editor', function() {
|
||||
map.size(m.size());
|
||||
});
|
||||
|
||||
function pan(d) {
|
||||
return function() {
|
||||
context.pan(d);
|
||||
};
|
||||
}
|
||||
|
||||
// pan amount
|
||||
var pa = 5;
|
||||
|
||||
var keybinding = d3.keybinding('main')
|
||||
.on('⌫', function() { d3.event.preventDefault(); })
|
||||
.on('←', pan([pa, 0]))
|
||||
.on('↑', pan([0, pa]))
|
||||
.on('→', pan([-pa, 0]))
|
||||
.on('↓', pan([0, -pa]));
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
var hash = iD.behavior.Hash(context);
|
||||
|
||||
hash();
|
||||
|
||||
if (!hash.hadHash) {
|
||||
map.centerZoom([-77.02271, 38.90085], 20);
|
||||
}
|
||||
|
||||
context.enter(iD.modes.Browse(context));
|
||||
|
||||
context.container()
|
||||
.call(iD.ui.Splash(context))
|
||||
.call(iD.ui.Restore(context));
|
||||
};
|
||||
};
|
||||
|
||||
iD.ui.tooltipHtml = function(text, key) {
|
||||
return '<span>' + text + '</span>' + '<div class="keyhint-wrap"><span class="keyhint"> ' + key + '</span></div>';
|
||||
};
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
iD.ui.Account = function(context) {
|
||||
var connection = context.connection();
|
||||
|
||||
function update(selection) {
|
||||
if (!connection.authenticated()) {
|
||||
selection.html('')
|
||||
.style('display', 'none');
|
||||
return;
|
||||
}
|
||||
|
||||
selection.style('display', 'block');
|
||||
|
||||
connection.userDetails(function(err, details) {
|
||||
selection.html('');
|
||||
|
||||
if (err) return;
|
||||
|
||||
// Link
|
||||
var userLink = selection.append('a')
|
||||
.attr('href', connection.url() + '/user/' + details.display_name)
|
||||
.attr('target', '_blank');
|
||||
|
||||
// Add thumbnail or dont
|
||||
if (details.image_url) {
|
||||
userLink.append('img')
|
||||
.attr('class', 'icon icon-pre-text user-icon')
|
||||
.attr('src', details.image_url);
|
||||
} else {
|
||||
userLink.append('span')
|
||||
.attr('class', 'icon avatar icon-pre-text');
|
||||
}
|
||||
|
||||
// Add user name
|
||||
userLink.append('span')
|
||||
.attr('class', 'label')
|
||||
.text(details.display_name);
|
||||
|
||||
selection.append('a')
|
||||
.attr('class', 'logout')
|
||||
.attr('href', '#')
|
||||
.text(t('logout'))
|
||||
.on('click.logout', function() {
|
||||
d3.event.preventDefault();
|
||||
connection.logout();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return function(selection) {
|
||||
connection.on('auth', function() { update(selection); });
|
||||
update(selection);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
iD.ui.Attribution = function(context) {
|
||||
return function(selection) {
|
||||
selection.append('span')
|
||||
.text('imagery');
|
||||
|
||||
selection
|
||||
.append('span')
|
||||
.attr('class', 'provided-by');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
// Translate a MacOS key command into the appropriate Windows/Linux equivalent.
|
||||
// For example, ⌘Z -> Ctrl+Z
|
||||
iD.ui.cmd = function(code) {
|
||||
if (iD.detect().os === 'mac')
|
||||
return code;
|
||||
|
||||
var modifiers = {
|
||||
'⌘': 'Ctrl',
|
||||
'⇧': 'Shift',
|
||||
'⌥': 'Alt'
|
||||
}, keys = [];
|
||||
|
||||
for (var i = 0; i < code.length; i++) {
|
||||
if (code[i] in modifiers) {
|
||||
keys.push(modifiers[code[i]]);
|
||||
} else {
|
||||
keys.push(code[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return keys.join('+');
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.ui.commit = function(map) {
|
||||
iD.ui.Commit = function(context) {
|
||||
var event = d3.dispatch('cancel', 'save', 'fix');
|
||||
|
||||
function zipSame(d) {
|
||||
@@ -31,110 +31,117 @@ iD.ui.commit = function(map) {
|
||||
header = selection.append('div').attr('class', 'header modal-section fillL'),
|
||||
body = selection.append('div').attr('class', 'body');
|
||||
|
||||
header.append('h2').text('Save Changes');
|
||||
header.append('h2')
|
||||
.text(t('commit.title'));
|
||||
|
||||
// Comment Box
|
||||
var comment_section = body.append('div').attr('class','modal-section fillD');
|
||||
comment_section.append('textarea')
|
||||
var commentSection = body.append('div')
|
||||
.attr('class', 'modal-section fillD');
|
||||
|
||||
var commentField = commentSection.append('textarea')
|
||||
.attr('class', 'changeset-comment')
|
||||
.attr('placeholder', 'Brief Description of your contributions')
|
||||
.property('value', localStorage.comment || '')
|
||||
.node().select();
|
||||
.attr('placeholder', t('commit.description_placeholder'))
|
||||
.property('value', context.storage('comment') || '');
|
||||
|
||||
var commit_info =
|
||||
comment_section
|
||||
.append('p')
|
||||
.attr('class','commit-info');
|
||||
commentField.node().select();
|
||||
|
||||
commit_info.append('span').text('The changes you upload as ');
|
||||
var userLink = d3.select(document.createElement('div'));
|
||||
|
||||
var user_link = commit_info.append('a')
|
||||
.attr('class','user-info')
|
||||
.text(user.display_name)
|
||||
.attr('href', connection.url() + '/user/' + user.display_name)
|
||||
.attr('target', '_blank');
|
||||
|
||||
commit_info.append('span').text(' will be visible on all maps that use OpenStreetMap data:');
|
||||
userLink.append('a')
|
||||
.attr('class','user-info')
|
||||
.text(user.display_name)
|
||||
.attr('href', connection.url() + '/user/' + user.display_name)
|
||||
.attr('target', '_blank');
|
||||
|
||||
if (user.image_url) {
|
||||
user_link
|
||||
.append('img')
|
||||
.attr('src', user.image_url)
|
||||
.attr('class', 'icon icon-pre-text user-icon');
|
||||
userLink.append('img')
|
||||
.attr('src', user.image_url)
|
||||
.attr('class', 'icon icon-pre-text user-icon');
|
||||
}
|
||||
|
||||
// Confirm / Cancel Buttons
|
||||
var buttonwrap = comment_section.append('div')
|
||||
.attr('class', 'buttons cf')
|
||||
.append('div')
|
||||
.attr('class', 'button-wrap joined col4');
|
||||
commentSection.append('p')
|
||||
.attr('class', 'commit-info')
|
||||
.html(t('commit.upload_explanation', {user: userLink.html()}));
|
||||
|
||||
var savebutton = buttonwrap
|
||||
.append('button')
|
||||
// Confirm / Cancel Buttons
|
||||
var buttonWrap = commentSection.append('div')
|
||||
.attr('class', 'buttons cf')
|
||||
.append('div')
|
||||
.attr('class', 'button-wrap joined col4');
|
||||
|
||||
var saveButton = buttonWrap.append('button')
|
||||
.attr('class', 'save action col6 button')
|
||||
.on('click.save', function() {
|
||||
var comment = d3.select('textarea.changeset-comment').node().value;
|
||||
var comment = commentField.node().value;
|
||||
localStorage.comment = comment;
|
||||
event.save({
|
||||
comment: comment
|
||||
});
|
||||
});
|
||||
savebutton.append('span').attr('class','label').text('Save');
|
||||
|
||||
var cancelbutton = buttonwrap.append('button')
|
||||
saveButton.append('span')
|
||||
.attr('class', 'label')
|
||||
.text(t('commit.save'));
|
||||
|
||||
var cancelButton = buttonWrap.append('button')
|
||||
.attr('class', 'cancel col6 button')
|
||||
.on('click.cancel', function() {
|
||||
event.cancel();
|
||||
});
|
||||
cancelbutton.append('span').attr('class','label').text('Cancel');
|
||||
|
||||
cancelButton.append('span')
|
||||
.attr('class', 'label')
|
||||
.text(t('commit.cancel'));
|
||||
|
||||
var warnings = body.selectAll('div.warning-section')
|
||||
.data(iD.validate(changes, map.history().graph()))
|
||||
.data(iD.validate(changes, context.graph()))
|
||||
.enter()
|
||||
.append('div').attr('class', 'modal-section warning-section fillL');
|
||||
.append('div')
|
||||
.attr('class', 'modal-section warning-section fillL');
|
||||
|
||||
warnings.append('h3')
|
||||
.text('Warnings');
|
||||
.text(t('commit.warnings'));
|
||||
|
||||
var warning_li = warnings.append('ul')
|
||||
var warningLi = warnings.append('ul')
|
||||
.attr('class', 'changeset-list')
|
||||
.selectAll('li')
|
||||
.data(function(d) { return d; })
|
||||
.enter()
|
||||
.append('li');
|
||||
|
||||
warning_li.append('button')
|
||||
warningLi.append('button')
|
||||
.attr('class', 'minor')
|
||||
.on('click', event.fix)
|
||||
.append('span')
|
||||
.attr('class', 'icon warning');
|
||||
|
||||
warning_li.append('strong').text(function(d) {
|
||||
warningLi.append('strong').text(function(d) {
|
||||
return d.message;
|
||||
});
|
||||
|
||||
var section = body.selectAll('div.commit-section')
|
||||
.data(['modified', 'deleted', 'created'].filter(changesLength))
|
||||
.enter()
|
||||
.append('div').attr('class', 'commit-section modal-section fillL2');
|
||||
.append('div')
|
||||
.attr('class', 'commit-section modal-section fillL2');
|
||||
|
||||
section.append('h3').text(function(d) {
|
||||
return d.charAt(0).toUpperCase() + d.slice(1);
|
||||
})
|
||||
section.append('h3')
|
||||
.text(function(d) { return t('commit.' + d); })
|
||||
.append('small')
|
||||
.attr('class', 'count')
|
||||
.text(changesLength);
|
||||
|
||||
var li = section.append('ul')
|
||||
.attr('class','changeset-list')
|
||||
.attr('class', 'changeset-list')
|
||||
.selectAll('li')
|
||||
.data(function(d) { return zipSame(changes[d]); })
|
||||
.enter()
|
||||
.append('li');
|
||||
|
||||
li.append('strong').text(function(d) {
|
||||
return (d.count > 1) ? d.type + 's ' : d.type + ' ';
|
||||
});
|
||||
li.append('strong')
|
||||
.text(function(d) {
|
||||
return (d.count > 1) ? d.type + 's ' : d.type + ' ';
|
||||
});
|
||||
|
||||
li.append('span')
|
||||
.text(function(d) { return d.name; })
|
||||
.attr('title', function(d) { return d.tagText; });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
iD.ui.confirm = function() {
|
||||
var modal = iD.ui.modal();
|
||||
iD.ui.confirm = function(selection) {
|
||||
var modal = iD.ui.modal(selection);
|
||||
modal.select('.modal').classed('modal-alert', true);
|
||||
modal.select('.content')
|
||||
.attr('class','modal-section fillD')
|
||||
|
||||