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

This commit is contained in:
Saman Bemel-Benrud
2013-02-04 13:15:50 -05:00
23 changed files with 321 additions and 39 deletions

101
CONTRIBUTING.md Normal file
View File

@@ -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.

View File

@@ -16,8 +16,7 @@
## 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.
* [Read up on Contributing and the code style of iD](CONTRIBUTING.md)
* See [open issues in the issue tracker if you're looking for something to do](https://github.com/systemed/iD/issues?state=open)
To run the code locally, just fork this project and run it from a local webserver.

View File

@@ -85,6 +85,7 @@
<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.js'></script>
<script src='js/id/actions/split.js'></script>
@@ -112,6 +113,7 @@
<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>

View File

@@ -0,0 +1,132 @@
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 = [], 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) {
return graph;
}
score = newScore;
if (score < 1.0e-8) {
break;
}
}
for (i = 0; i < points.length - 1; i++) {
quad_nodes.push(iD.Node({ loc: projection.invert(points[i]) }));
}
for (i = 0; i < nodes.length; i++) {
if (graph.parentWays(nodes[i]).length > 1) {
var closest, closest_dist = Infinity, dist;
for (j = 0; j < quad_nodes.length; j++) {
dist = iD.geo.dist(quad_nodes[j].loc, nodes[i].loc);
if (dist < closest_dist) {
closest_dist = dist;
closest = j;
}
}
quad_nodes.splice(closest, 1, nodes[i]);
}
}
for (i = 0; i < quad_nodes.length; i++) {
graph = graph.replace(quad_nodes[i]);
}
var ids = _.pluck(quad_nodes, 'id'),
difference = _.difference(_.uniq(way.nodes), ids);
ids.push(ids[0]);
graph = graph.replace(way.update({nodes: ids}));
for (i = 0; i < difference.length; i++) {
graph = iD.actions.DeleteNode(difference[i])(graph);
}
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 = p.length + q.length;
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;
};

View File

@@ -17,7 +17,7 @@ iD.actions.Split = function(nodeId, newWayId) {
return parents.filter(function (parent) {
return parent.first() !== nodeId &&
parent.last() !== nodeId;
})
});
}
var action = function(graph) {

View File

@@ -9,7 +9,7 @@ iD.behavior.DragMidpoint = function(context) {
context.perform(iD.actions.AddMidpoint(d, node));
var vertex = d3.selectAll('.vertex')
var vertex = context.surface().selectAll('.vertex')
.filter(function(data) { return data.id === node.id; });
behavior.target(vertex.node(), vertex.datum());

View File

@@ -82,7 +82,7 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
return graph
.replace(way.removeNode(nodeId).addNode(newNode.id, index))
.remove(node);
}
};
}
// Accept the current position of the temporary node and continue drawing.

View File

@@ -127,7 +127,7 @@ iD.History = function() {
modified: difference.modified(),
created: difference.created(),
deleted: difference.deleted()
}
};
},
hasChanges: function() {

View File

@@ -47,6 +47,6 @@ _.extend(iD.Node.prototype, {
type: 'Point',
coordinates: this.loc
}
}
};
}
});

View File

@@ -1 +1 @@
iD.operations = {}
iD.operations = {};

View File

@@ -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;
};

View File

@@ -49,6 +49,7 @@ iD.Map = function(context) {
map.size(selection.size());
map.surface = surface;
map.tilesurface = tilegroup;
supersurface
.call(tail);

View File

@@ -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 }; }

View File

@@ -1,19 +1,19 @@
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) {
PointTransform: function(projection) {
return function(entity) {
return 'translate(' + projection(entity.loc) + ')';
};
},
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 +23,9 @@ 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) {
return projection(n.loc);
}).join('L'));
};
}
};

View File

@@ -226,7 +226,7 @@ iD.svg.Labels = function(projection) {
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([]);
@@ -254,7 +254,6 @@ iD.svg.Labels = function(projection) {
}
}
var positions = {
point: [],
line: [],

View File

@@ -140,8 +140,10 @@ iD.ui = function (context) {
var about = container.append('div')
.attr('class','col12 about-block fillD pad1');
about.append('div')
.attr('class', 'user-container')
var userContainer = about.append('div')
.attr('class', 'user-container');
userContainer
.append('div')
.attr('class', 'hello');
@@ -243,7 +245,7 @@ iD.ui = function (context) {
map.centerZoom([-77.02271, 38.90085], 20);
}
d3.select('.user-container').call(iD.ui.userpanel(connection)
userContainer.call(iD.ui.userpanel(connection)
.on('logout.editor', connection.logout)
.on('login.editor', connection.authenticate));

View File

@@ -35,11 +35,12 @@ iD.ui.commit = function(context) {
// Comment Box
var comment_section = body.append('div').attr('class','modal-section fillD');
comment_section.append('textarea')
var commentField = comment_section.append('textarea')
.attr('class', 'changeset-comment')
.attr('placeholder', 'Brief Description of your contributions')
.property('value', context.storage('comment') || '')
.node().select();
.property('value', context.storage('comment') || '');
commentField.node().select();
var commit_info =
comment_section
@@ -73,7 +74,7 @@ iD.ui.commit = function(context) {
.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

View File

@@ -38,9 +38,8 @@ iD.ui.geocoder = function() {
function setVisible(show) {
button.classed('active', show);
gcForm.classed('hide', !show);
var input_node = d3.select('.map-overlay input').node();
if (show) input_node.focus();
else input_node.blur();
if (show) inputNode.node().focus();
else inputNode.node().blur();
}
var button = selection.append('button')
@@ -51,7 +50,7 @@ iD.ui.geocoder = function() {
var gcForm = selection.append('form');
gcForm.attr('class','content fillD map-overlay hide')
var inputNode = gcForm.attr('class','content fillD map-overlay hide')
.append('input')
.attr({ type: 'text', placeholder: t('geocoder.find_a_place') })
.on('keydown', keydown);

View File

@@ -61,9 +61,10 @@ iD.ui.layerswitcher = function(context) {
opa.append('h4').text(t('layerswitcher.layers'));
opa.append('ul')
.attr('class', 'opacity-options')
.selectAll('div.opacity')
var opacityList = opa.append('ul')
.attr('class', 'opacity-options');
opacityList.selectAll('div.opacity')
.data(opacities)
.enter()
.append('li')
@@ -71,11 +72,11 @@ iD.ui.layerswitcher = function(context) {
return t('layerswitcher.percent_brightness', { opacity: (d * 100) });
})
.on('click.set-opacity', function(d) {
d3.select('#tile-g')
context.map().tilesurface
.transition()
.style('opacity', d)
.attr('data-opacity', d);
d3.selectAll('.opacity-options li')
opacityList.selectAll('li')
.classed('selected', false);
d3.select(this)
.classed('selected', true);

View File

@@ -14,6 +14,7 @@ iD.ui.save = function(context) {
.on('click', function() {
function commit(e) {
d3.select('.shaded').remove();
var l = iD.ui.loading(t('uploading_changes'), true);
connection.putChangeset(history.changes(), e.comment, history.imagery_used(), function(err, changeset_id) {
@@ -40,6 +41,7 @@ iD.ui.save = function(context) {
}));
}
});
}
if (history.hasChanges()) {

View File

@@ -2,15 +2,20 @@ iD.ui.splash = function() {
var modal = iD.ui.modal();
modal.select('.modal')
.attr('class', 'modal-splash modal')
.attr('class', 'modal-splash modal');
var introModal = modal.select('.content')
.append('div')
.attr('class', 'modal-section fillL');
introModal.append('div').attr('class','logo');
introModal.append('div')
.attr('class','logo');
introModal.append('div').html("<h2 class>Welcome to the iD OpenStreetMap editor</h2><p>This is development version 0.0.0-alpha1. For more information see <a href='http://ideditor.com/'>ideditor.com</a> and report bugs at <a href='https://github.com'>github.com.systemed/iD</a>.</p>");
introModal.append('div')
.html("<h2 class>Welcome to the iD OpenStreetMap editor</h2><p>" +
"This is development version 0.0.0-alpha1. " +
"For more information see <a href='http://ideditor.com/'>ideditor.com</a>" +
" and report bugs at <a href='https://github.com'>github.com.systemed/iD</a>.</p>");
return modal;
};
};

View File

@@ -65,6 +65,15 @@ locale.en = {
area: "Made an area circular."
}
},
orthogonalize: {
title: "Orthogonalize",
description: "Square these corners.",
key: "Q",
annotation: {
line: "Squared the corners of a line.",
area: "Squared the corners of an area."
}
},
delete: {
title: "Delete",
description: "Remove this from the map.",

View File

@@ -73,6 +73,7 @@
<script src='../js/id/actions/add_vertex.js'></script>
<script src='../js/id/actions/change_tags.js'></script>
<script src='../js/id/actions/circularize.js'></script>
<script src='../js/id/actions/orthogonalize.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>
@@ -108,6 +109,7 @@
<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>