Teach realigning roads by moving nodes, adding midpoints

(closes #2381)
This commit is contained in:
Bryan Housel
2017-04-06 17:09:32 -04:00
parent ef1e7038eb
commit 8fe33d9430
4 changed files with 256 additions and 25 deletions
+7 -1
View File
@@ -945,7 +945,13 @@ en:
choose_preset_residential: "There are many different types of roads, but this one is a residential road. **Choose the {name} type**"
retry_preset_residential: "You didn't select the {name} type. **Click here to choose again**"
name_road: "**Give this road a name, then hit escape, return, or click the {button} button to close the feature editor.**"
play: "You added a new road! Try drawing a few more lines, and see what other kinds of lines you can add to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**"
update_line: "Sometimes you will need to change the shape of an existing line. Here is a road that doesn't look quite right."
add_node: "We can add some nodes to this line to improve its shape. One way to add a node is to double-click the line where you want to add a node. **Double-click on the line to create a new node.**"
start_drag_endpoint: "When a line is selected, you can drag any of its nodes by clicking and holding down the left mouse button while you drag. **Drag the endpoint to the place where these roads should intersect.**"
finish_drag_endpoint: "This spot looks good. **Finish dragging by releasing the left mouse button**"
start_drag_midpoint: "Small triangles are drawn at the midpoints between nodes. Another way to create a new node is to drag a midpoint to a new location. **Drag the midpoint triangle to create a new node along the curve of the road.**"
continue_drag_midpoint: "This line is looking much better! Continue to adjust this line by double-clicking or dragging midpoints until the curve matches the road shape. **When you're happy with how the line looks, click OK.**"
play: "Great! Use the skills that you've learned in this chapter to practice editing some more lines. **When you are ready to continue to the next chapter, click '{next}'.**"
buildings:
title: "Buildings"
add_building: "OpenStreetMap is the world's largest database of buildings. You can help improve this database by tracing buildings that aren't already mapped. **Click the {button} Area button to add a new area.**"
+7 -1
View File
@@ -808,7 +808,13 @@
"choose_preset_residential": "There are many different types of roads, but this one is a residential road. **Choose the {name} type**",
"retry_preset_residential": "You didn't select the {name} type. **Click here to choose again**",
"name_road": "**Give this road a name, then hit escape, return, or click the {button} button to close the feature editor.**",
"play": "You added a new road! Try drawing a few more lines, and see what other kinds of lines you can add to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**"
"update_line": "Sometimes you will need to change the shape of an existing line. Here is a road that doesn't look quite right.",
"add_node": "We can add some nodes to this line to improve its shape. One way to add a node is to double-click the line where you want to add a node. **Double-click on the line to create a new node.**",
"start_drag_endpoint": "When a line is selected, you can drag any of its nodes by clicking and holding down the left mouse button while you drag. **Drag the endpoint to the place where these roads should intersect.**",
"finish_drag_endpoint": "This spot looks good. **Finish dragging by releasing the left mouse button**",
"start_drag_midpoint": "Small triangles are drawn at the midpoints between nodes. Another way to create a new node is to drag a midpoint to a new location. **Drag the midpoint triangle to create a new node along the curve of the road.**",
"continue_drag_midpoint": "This line is looking much better! Continue to adjust this line by double-clicking or dragging midpoints until the curve matches the road shape. **When you're happy with how the line looks, click OK.**",
"play": "Great! Use the skills that you've learned in this chapter to practice editing some more lines. **When you are ready to continue to the next chapter, click '{next}'.**"
},
"buildings": {
"title": "Buildings",
+240 -21
View File
@@ -1,6 +1,8 @@
import * as d3 from 'd3';
import _ from 'lodash';
import { t } from '../../util/locale';
import { geoSphericalDistance } from '../../geo/index';
import { modeSelect } from '../../modes/select';
import { utilRebind } from '../../util/rebind';
import { icon, pad } from './helper';
@@ -8,13 +10,18 @@ import { icon, pad } from './helper';
export function uiIntroLine(context, reveal) {
var dispatch = d3.dispatch('done'),
timeouts = [],
midpoint = [-85.62975395449628, 41.95787501510204],
start = [-85.6297754121684, 41.95805253325314],
intersection = [-85.62974496187628, 41.95742515554585],
tulipRoadId = null,
flowerRoadId = 'w646',
tulipRoadStart = [-85.6297754121684, 41.95805253325314],
tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204],
tulipRoadIntersection = [-85.62974496187628, 41.95742515554585],
woodRoadId = 'w525',
woodRoadEndId = 'n2862',
woodRoadAddNode = [-85.62390110349587, 41.95397111462291],
woodRoadDragEndpoint = [-85.62383958913921, 41.9546607846611],
woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872],
roadCategory = context.presets().item('category-road'),
residentialPreset = context.presets().item('highway/residential'),
targetId = 'w646',
lineId = null;
residentialPreset = context.presets().item('highway/residential');
var chapter = {
@@ -60,16 +67,16 @@ export function uiIntroLine(context, reveal) {
return chapter.restart();
}
lineId = null;
tulipRoadId = null;
var padding = 70 * Math.pow(2, context.map().zoom() - 18);
var box = pad(start, padding, context);
var box = pad(tulipRoadStart, padding, context);
box.height = box.height + 100;
reveal(box, t('intro.lines.start_line'));
context.map().on('move.intro drawn.intro', function() {
padding = 70 * Math.pow(2, context.map().zoom() - 18);
box = pad(start, padding, context);
box = pad(tulipRoadStart, padding, context);
box.height = box.height + 100;
reveal(box, t('intro.lines.start_line'), { duration: 0 });
});
@@ -92,12 +99,12 @@ export function uiIntroLine(context, reveal) {
return chapter.restart();
}
lineId = context.mode().selectedIDs()[0];
context.map().centerEase(midpoint);
tulipRoadId = context.mode().selectedIDs()[0];
context.map().centerEase(tulipRoadMidpoint);
timeout(function() {
var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
var box = pad(midpoint, padding, context);
var box = pad(tulipRoadMidpoint, padding, context);
box.height = box.height * 2;
reveal(box,
t('intro.lines.intersect', { name: t('intro.graph.name.flower-street') })
@@ -105,7 +112,7 @@ export function uiIntroLine(context, reveal) {
context.map().on('move.intro drawn.intro', function() {
padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
box = pad(midpoint, padding, context);
box = pad(tulipRoadMidpoint, padding, context);
box.height = box.height * 2;
reveal(box,
t('intro.lines.intersect', { name: t('intro.graph.name.flower-street') }),
@@ -115,7 +122,7 @@ export function uiIntroLine(context, reveal) {
}, 260); // after easing..
context.history().on('change.intro', function() {
var entity = lineId && context.hasEntity(lineId);
var entity = tulipRoadId && context.hasEntity(tulipRoadId);
if (!entity) return chapter.restart();
if (isLineConnected()) {
@@ -144,13 +151,13 @@ export function uiIntroLine(context, reveal) {
function isLineConnected() {
var entity = lineId && context.hasEntity(lineId);
var entity = tulipRoadId && context.hasEntity(tulipRoadId);
if (!entity) return false;
var drawNodes = context.graph().childNodes(entity);
return _.some(drawNodes, function(node) {
return _.some(context.graph().parentWays(node), function(parent) {
return parent.id === targetId;
return parent.id === flowerRoadId;
});
});
}
@@ -159,7 +166,7 @@ export function uiIntroLine(context, reveal) {
function retryIntersect() {
d3.select(window).on('mousedown.intro', eventCancel, true);
var box = pad(intersection, 80, context);
var box = pad(tulipRoadIntersection, 80, context);
reveal(box,
t('intro.lines.retry_intersect', { name: t('intro.graph.name.flower-street') })
);
@@ -170,10 +177,10 @@ export function uiIntroLine(context, reveal) {
function continueLine() {
if (context.mode().id !== 'draw-line') return chapter.restart();
var entity = lineId && context.hasEntity(lineId);
var entity = tulipRoadId && context.hasEntity(tulipRoadId);
if (!entity) return chapter.restart();
context.map().centerEase(intersection);
context.map().centerEase(tulipRoadIntersection);
reveal('#surface', t('intro.lines.continue_line'));
@@ -287,7 +294,8 @@ export function uiIntroLine(context, reveal) {
function nameRoad() {
context.on('exit.intro', function() {
continueTo(play);
context.history().checkpoint('doneAddRoad');
continueTo(updateLine);
});
timeout(function() {
@@ -303,6 +311,217 @@ export function uiIntroLine(context, reveal) {
}
function updateLine() {
context.history().reset('doneAddRoad');
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return chapter.restart();
}
context.map().zoom(19).centerEase(woodRoadDragMidpoint, 500);
timeout(function() {
var padding = 250 * Math.pow(2, context.map().zoom() - 19);
var box = pad(woodRoadDragMidpoint, padding, context);
var advance = function() { continueTo(addNode); };
reveal(box, t('intro.lines.update_line'),
{ buttonText: t('intro.ok'), buttonCallback: advance }
);
context.map().on('move.intro drawn.intro', function() {
var box = pad(woodRoadDragMidpoint, padding, context);
reveal(box, t('intro.lines.update_line'),
{ duration: 0, buttonText: t('intro.ok'), buttonCallback: advance }
);
});
}, 550);
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
nextStep();
}
}
function addNode() {
context.history().reset('doneAddRoad');
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return chapter.restart();
}
var padding = 40 * Math.pow(2, context.map().zoom() - 19);
var box = pad(woodRoadAddNode, padding, context);
reveal(box, t('intro.lines.add_node'));
context.map().on('move.intro drawn.intro', function() {
var box = pad(woodRoadAddNode, padding, context);
reveal(box, t('intro.lines.add_node'), { duration: 0 });
});
context.history().on('change.intro', function(changed) {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
if (changed.created().length === 1) {
timeout(function() { continueTo(startDragEndpoint); }, 500);
}
});
context.on('enter.intro', function(mode) {
if (mode.id !== 'select') {
continueTo(updateLine);
}
});
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
context.history().on('change.intro', null);
context.on('enter.intro', null);
nextStep();
}
}
function startDragEndpoint() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var padding = 100 * Math.pow(2, context.map().zoom() - 19);
var box = pad(woodRoadDragEndpoint, padding, context);
reveal(box, t('intro.lines.start_drag_endpoint'));
context.map().on('move.intro drawn.intro', function() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var box = pad(woodRoadDragEndpoint, padding, context);
reveal(box, t('intro.lines.start_drag_endpoint'), { duration: 0 });
var entity = context.entity(woodRoadEndId);
if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 2) {
continueTo(finishDragEndpoint);
}
});
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
nextStep();
}
}
function finishDragEndpoint() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var padding = 100 * Math.pow(2, context.map().zoom() - 19);
var box = pad(woodRoadDragEndpoint, padding, context);
reveal(box, t('intro.lines.finish_drag_endpoint'));
context.map().on('move.intro drawn.intro', function() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var box = pad(woodRoadDragEndpoint, padding, context);
reveal(box, t('intro.lines.finish_drag_endpoint'), { duration: 0 });
var entity = context.entity(woodRoadEndId);
if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 2.5) {
continueTo(startDragEndpoint);
}
});
context.on('enter.intro', function() {
continueTo(startDragMidpoint);
});
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
context.on('enter.intro', null);
nextStep();
}
}
function startDragMidpoint() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var padding = 80 * Math.pow(2, context.map().zoom() - 19);
var box = pad(woodRoadDragMidpoint, padding, context);
reveal(box, t('intro.lines.start_drag_midpoint'));
context.map().on('move.intro drawn.intro', function() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var box = pad(woodRoadDragMidpoint, padding, context);
reveal(box, t('intro.lines.start_drag_midpoint'), { duration: 0 });
});
context.history().on('change.intro', function(changed) {
if (changed.created().length === 1) {
continueTo(continueDragMidpoint);
}
});
context.on('enter.intro', function(mode) {
if (mode.id !== 'select') {
// keep Wood Road selected so midpoint triangles are drawn..
context.enter(modeSelect(context, [woodRoadId]));
}
});
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
context.history().on('change.intro', null);
context.on('enter.intro', null);
nextStep();
}
}
function continueDragMidpoint() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var padding = 100 * Math.pow(2, context.map().zoom() - 19);
var box = pad(woodRoadDragEndpoint, padding, context);
box.height += 400;
var advance = function() { continueTo(play); };
reveal(box, t('intro.lines.continue_drag_midpoint'),
{ buttonText: t('intro.ok'), buttonCallback: advance }
);
context.map().on('move.intro drawn.intro', function() {
if (!context.hasEntity(woodRoadId) || !context.hasEntity(woodRoadEndId)) {
return continueTo(updateLine);
}
var box = pad(woodRoadDragEndpoint, padding, context);
box.height += 400;
reveal(box, t('intro.lines.continue_drag_midpoint'),
{ duration: 0, buttonText: t('intro.ok'), buttonCallback: advance }
);
});
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
nextStep();
}
}
function play() {
dispatch.call('done');
reveal('.intro-nav-wrap .chapter-building',
@@ -316,7 +535,7 @@ export function uiIntroLine(context, reveal) {
chapter.enter = function() {
context.history().reset('initial');
context.map().zoom(18.5).centerEase(start);
context.map().zoom(18.5).centerEase(tulipRoadStart);
addLine();
};
+2 -2
View File
@@ -6,8 +6,9 @@ import { icon, pointBox } from './helper';
export function uiIntroNavigation(context, reveal) {
var dispatch = d3.dispatch('done'),
timeouts = [],
hallId = 'n2061',
timeouts = [];
springSt = [-85.63585099140167, 41.942506848938926];
var chapter = {
@@ -174,7 +175,6 @@ export function uiIntroNavigation(context, reveal) {
function selectedStreet() {
var springSt = [-85.63585099140167, 41.942506848938926];
context.map().centerEase(springSt);
timeout(function() {