mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 05:30:35 +02:00
merge from 'master'
This commit is contained in:
@@ -219,7 +219,7 @@ Each imagery source should have the following properties:
|
||||
Optional properties:
|
||||
* `description` - A longer source description which, if included, will be displayed in a popup when viewing the background imagery list
|
||||
* `overlay` - If `true`, this is an overlay layer (a transparent layer rendered above base imagery layer). Defaults to `false`
|
||||
* `scaleExtent` - Allowable min and max zoom levels, defaults to `[0, 22]`
|
||||
* `zoomExtent` - Allowable min and max zoom levels, defaults to `[0, 22]`
|
||||
* `polygon` - Array of coordinate rings within which imagery is valid. If omitted, imagery is assumed to be valid worldwide
|
||||
* `overzoom` - Can this imagery be scaled up when zooming in beyond the max zoom? Defaults to `true`
|
||||
* `terms_url` - Url to link to when displaying the imagery terms
|
||||
|
||||
@@ -234,6 +234,13 @@ en:
|
||||
annotation:
|
||||
create: Added a turn restriction
|
||||
delete: Deleted a turn restriction
|
||||
detach_node:
|
||||
title: Detach
|
||||
key: E
|
||||
description: Detach this node from these lines/areas.
|
||||
annotation: Detached a node from parent lines/areas.
|
||||
restriction: "This node can't be detached because it would damage a \"{relation}\" relation."
|
||||
connected_to_hidden: This node can't be detached because it is connected to a hidden feature.
|
||||
restriction:
|
||||
controls:
|
||||
distance: Distance
|
||||
@@ -1142,6 +1149,7 @@ en:
|
||||
continue_line: "Continue a line at the selected node"
|
||||
merge: "Combine (merge) selected features"
|
||||
disconnect: "Disconnect features at the selected node"
|
||||
detach_node: "Detach selected node from parent lines/areas"
|
||||
split: "Split a line into two at the selected node"
|
||||
reverse: "Reverse a line"
|
||||
move: "Move selected features"
|
||||
|
||||
+795
-951
File diff suppressed because it is too large
Load Diff
+36
-64
@@ -123,23 +123,19 @@
|
||||
"text": "shortcuts.browsing.vertex_selected.previous"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["]","↘"
|
||||
],
|
||||
"shortcuts": ["]","↘"],
|
||||
"text": "shortcuts.browsing.vertex_selected.next"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["{","⇞"
|
||||
],
|
||||
"shortcuts": ["{","⇞"],
|
||||
"text": "shortcuts.browsing.vertex_selected.first"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["}","⇟"
|
||||
],
|
||||
"shortcuts": ["}","⇟"],
|
||||
"text": "shortcuts.browsing.vertex_selected.last"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["\\","shortcuts.key.pause"
|
||||
],
|
||||
"shortcuts": ["\\","shortcuts.key.pause"],
|
||||
"text": "shortcuts.browsing.vertex_selected.change_parent"
|
||||
}
|
||||
]
|
||||
@@ -157,33 +153,27 @@
|
||||
"text": "shortcuts.editing.drawing.title"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["1"
|
||||
],
|
||||
"shortcuts": ["1"],
|
||||
"text": "shortcuts.editing.drawing.add_point"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["2"
|
||||
],
|
||||
"shortcuts": ["2"],
|
||||
"text": "shortcuts.editing.drawing.add_line"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["3"
|
||||
],
|
||||
"shortcuts": ["3"],
|
||||
"text": "shortcuts.editing.drawing.add_area"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["Left-click","shortcuts.key.space"
|
||||
],
|
||||
"shortcuts": ["Left-click","shortcuts.key.space"],
|
||||
"text": "shortcuts.editing.drawing.place_point"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["⌥"
|
||||
],
|
||||
"shortcuts": ["⌥"],
|
||||
"text": "shortcuts.editing.drawing.disable_snap"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["↵","⎋"
|
||||
],
|
||||
"shortcuts": ["↵","⎋"],
|
||||
"text": "shortcuts.editing.drawing.stop_line"
|
||||
},
|
||||
{
|
||||
@@ -192,32 +182,27 @@
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘"],
|
||||
"shortcuts": ["C"
|
||||
],
|
||||
"shortcuts": ["C"],
|
||||
"text": "shortcuts.editing.commands.copy"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘"],
|
||||
"shortcuts": ["V"
|
||||
],
|
||||
"shortcuts": ["V"],
|
||||
"text": "shortcuts.editing.commands.paste"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘"],
|
||||
"shortcuts": ["Z"
|
||||
],
|
||||
"shortcuts": ["Z"],
|
||||
"text": "shortcuts.editing.commands.undo"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘","⇧"],
|
||||
"shortcuts": ["Z"
|
||||
],
|
||||
"shortcuts": ["Z"],
|
||||
"text": "shortcuts.editing.commands.redo"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘"],
|
||||
"shortcuts": ["S"
|
||||
],
|
||||
"shortcuts": ["S"],
|
||||
"text": "shortcuts.editing.commands.save"
|
||||
}
|
||||
]
|
||||
@@ -229,64 +214,56 @@
|
||||
"text": "shortcuts.editing.operations.title"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.continue.key"
|
||||
],
|
||||
"shortcuts": ["operations.continue.key"],
|
||||
"text": "shortcuts.editing.operations.continue_line"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.merge.key"
|
||||
],
|
||||
"shortcuts": ["operations.merge.key"],
|
||||
"text": "shortcuts.editing.operations.merge"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.disconnect.key"
|
||||
],
|
||||
"shortcuts": ["operations.disconnect.key"],
|
||||
"text": "shortcuts.editing.operations.disconnect"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.split.key"
|
||||
],
|
||||
"shortcuts": ["operations.detach_node.key"],
|
||||
"text": "shortcuts.editing.operations.detach_node"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.split.key"],
|
||||
"text": "shortcuts.editing.operations.split"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.reverse.key"
|
||||
],
|
||||
"shortcuts": ["operations.reverse.key"],
|
||||
"text": "shortcuts.editing.operations.reverse"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.move.key"
|
||||
],
|
||||
"shortcuts": ["operations.move.key"],
|
||||
"text": "shortcuts.editing.operations.move"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.rotate.key"
|
||||
],
|
||||
"shortcuts": ["operations.rotate.key"],
|
||||
"text": "shortcuts.editing.operations.rotate"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.orthogonalize.key"
|
||||
],
|
||||
"shortcuts": ["operations.orthogonalize.key"],
|
||||
"text": "shortcuts.editing.operations.orthogonalize"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.circularize.key"
|
||||
],
|
||||
"shortcuts": ["operations.circularize.key"],
|
||||
"text": "shortcuts.editing.operations.circularize"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.reflect.key.long"
|
||||
],
|
||||
"shortcuts": ["operations.reflect.key.long"],
|
||||
"text": "shortcuts.editing.operations.reflect_long"
|
||||
},
|
||||
{
|
||||
"shortcuts": ["operations.reflect.key.short"
|
||||
],
|
||||
"shortcuts": ["operations.reflect.key.short"],
|
||||
"text": "shortcuts.editing.operations.reflect_short"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘"],
|
||||
"shortcuts": ["⌫"
|
||||
],
|
||||
"shortcuts": ["⌫"],
|
||||
"text": "shortcuts.editing.operations.delete"
|
||||
}
|
||||
]
|
||||
@@ -305,32 +282,27 @@
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘"],
|
||||
"shortcuts": ["info_panels.key"
|
||||
],
|
||||
"shortcuts": ["info_panels.key"],
|
||||
"text": "shortcuts.tools.info.all"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘","⇧"],
|
||||
"shortcuts": ["info_panels.background.key"
|
||||
],
|
||||
"shortcuts": ["info_panels.background.key"],
|
||||
"text": "shortcuts.tools.info.background"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘","⇧"],
|
||||
"shortcuts": ["info_panels.history.key"
|
||||
],
|
||||
"shortcuts": ["info_panels.history.key"],
|
||||
"text": "shortcuts.tools.info.history"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘","⇧"],
|
||||
"shortcuts": ["info_panels.location.key"
|
||||
],
|
||||
"shortcuts": ["info_panels.location.key"],
|
||||
"text": "shortcuts.tools.info.location"
|
||||
},
|
||||
{
|
||||
"modifiers": ["⌘","⇧"],
|
||||
"shortcuts": ["info_panels.measurement.key"
|
||||
],
|
||||
"shortcuts": ["info_panels.measurement.key"],
|
||||
"text": "shortcuts.tools.info.measurement"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -91,6 +91,13 @@ sources.concat(whitelist).forEach(function(source) {
|
||||
template: source.url
|
||||
};
|
||||
|
||||
|
||||
// supports 512px tiles
|
||||
if (source.id === 'Mapbox') {
|
||||
im.template = im.template.replace('.jpg', '@2x.jpg');
|
||||
im.tileSize = 512;
|
||||
}
|
||||
|
||||
if (source.type === 'wms') {
|
||||
im.projection = supportedProjection;
|
||||
}
|
||||
@@ -116,7 +123,7 @@ sources.concat(whitelist).forEach(function(source) {
|
||||
|
||||
var extent = source.extent || {};
|
||||
if (extent.min_zoom || extent.max_zoom) {
|
||||
im.scaleExtent = [
|
||||
im.zoomExtent = [
|
||||
extent.min_zoom || 0,
|
||||
extent.max_zoom || 22
|
||||
];
|
||||
@@ -149,7 +156,7 @@ sources.concat(whitelist).forEach(function(source) {
|
||||
im.terms_html = attribution.html;
|
||||
}
|
||||
|
||||
['best', 'default', 'description', 'icon', 'overlay'].forEach(function(a) {
|
||||
['best', 'default', 'description', 'icon', 'overlay', 'tileSize'].forEach(function(a) {
|
||||
if (source[a]) {
|
||||
im[a] = source[a];
|
||||
}
|
||||
|
||||
Vendored
+9
@@ -303,6 +303,14 @@
|
||||
"create": "Added a turn restriction",
|
||||
"delete": "Deleted a turn restriction"
|
||||
}
|
||||
},
|
||||
"detach_node": {
|
||||
"title": "Detach",
|
||||
"key": "E",
|
||||
"description": "Detach this node from these lines/areas.",
|
||||
"annotation": "Detached a node from parent lines/areas.",
|
||||
"restriction": "This node can't be detached because it would damage a \"{relation}\" relation.",
|
||||
"connected_to_hidden": "This node can't be detached because it is connected to a hidden feature."
|
||||
}
|
||||
},
|
||||
"restriction": {
|
||||
@@ -1317,6 +1325,7 @@
|
||||
"continue_line": "Continue a line at the selected node",
|
||||
"merge": "Combine (merge) selected features",
|
||||
"disconnect": "Disconnect features at the selected node",
|
||||
"detach_node": "Detach selected node from parent lines/areas",
|
||||
"split": "Split a line into two at the selected node",
|
||||
"reverse": "Reverse a line",
|
||||
"move": "Move selected features",
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { osmNode } from '../osm';
|
||||
|
||||
|
||||
export function actionDetachNode(nodeID) {
|
||||
|
||||
var action = function(graph) {
|
||||
var node = graph.entity(nodeID);
|
||||
|
||||
// Create a new node to replace the one we will detach
|
||||
var replacement = osmNode({ loc: node.loc });
|
||||
graph = graph.replace(replacement);
|
||||
|
||||
// Process each way in turn, updating the graph as we go
|
||||
graph = graph.parentWays(node)
|
||||
.reduce(function(accGraph, parentWay) {
|
||||
return accGraph.replace(parentWay.replaceNode(nodeID, replacement.id));
|
||||
}, graph);
|
||||
|
||||
// Process any relations too
|
||||
return graph.parentRelations(node)
|
||||
.reduce(function(accGraph, parentRel) {
|
||||
return accGraph.replace(parentRel.replaceMember(node, replacement));
|
||||
}, graph);
|
||||
};
|
||||
|
||||
|
||||
action.disabled = function(graph) {
|
||||
var node = graph.entity(nodeID);
|
||||
var parentRels = graph.parentRelations(node);
|
||||
|
||||
for (var i = 0; i < parentRels.length; i++) {
|
||||
var relation = parentRels[i];
|
||||
if (!relation.isValidRestriction()) continue;
|
||||
|
||||
for (var j = 0; j < relation.members.length; j++) {
|
||||
var m = relation.members[j];
|
||||
if (m.id === nodeID && (m.role === 'via' || m.role === 'location_hint')) {
|
||||
return 'restriction';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
@@ -34,3 +34,4 @@ export { actionSplit } from './split';
|
||||
export { actionStraighten } from './straighten';
|
||||
export { actionUnrestrictTurn } from './unrestrict_turn';
|
||||
export { actionReflect } from './reflect.js';
|
||||
export { actionDetachNode } from './detach_node';
|
||||
@@ -119,7 +119,7 @@ export function coreContext() {
|
||||
return context;
|
||||
};
|
||||
|
||||
context.loadTiles = utilCallWhenIdle(function(projection, dimensions, callback) {
|
||||
context.loadTiles = utilCallWhenIdle(function(projection, callback) {
|
||||
var cid;
|
||||
function done(err, result) {
|
||||
if (connection.getConnectionId() !== cid) {
|
||||
@@ -131,11 +131,11 @@ export function coreContext() {
|
||||
}
|
||||
if (connection && context.editable()) {
|
||||
cid = connection.getConnectionId();
|
||||
connection.loadTiles(projection, dimensions, done);
|
||||
connection.loadTiles(projection, done);
|
||||
}
|
||||
});
|
||||
|
||||
context.loadEntity = function(entityId, callback) {
|
||||
context.loadEntity = function(entityID, callback) {
|
||||
var cid;
|
||||
function done(err, result) {
|
||||
if (connection.getConnectionId() !== cid) {
|
||||
@@ -147,24 +147,24 @@ export function coreContext() {
|
||||
}
|
||||
if (connection) {
|
||||
cid = connection.getConnectionId();
|
||||
connection.loadEntity(entityId, done);
|
||||
connection.loadEntity(entityID, done);
|
||||
}
|
||||
};
|
||||
|
||||
context.zoomToEntity = function(entityId, zoomTo) {
|
||||
context.zoomToEntity = function(entityID, zoomTo) {
|
||||
if (zoomTo !== false) {
|
||||
this.loadEntity(entityId, function(err, result) {
|
||||
this.loadEntity(entityID, function(err, result) {
|
||||
if (err) return;
|
||||
var entity = _find(result.data, function(e) { return e.id === entityId; });
|
||||
var entity = _find(result.data, function(e) { return e.id === entityID; });
|
||||
if (entity) { map.zoomTo(entity); }
|
||||
});
|
||||
}
|
||||
|
||||
map.on('drawn.zoomToEntity', function() {
|
||||
if (!context.hasEntity(entityId)) return;
|
||||
if (!context.hasEntity(entityID)) return;
|
||||
map.on('drawn.zoomToEntity', null);
|
||||
context.on('enter.zoomToEntity', null);
|
||||
context.enter(modeSelect(context, [entityId]));
|
||||
context.enter(modeSelect(context, [entityID]));
|
||||
});
|
||||
|
||||
context.on('enter.zoomToEntity', function() {
|
||||
|
||||
+18
-29
@@ -52,9 +52,8 @@ coreGraph.prototype = {
|
||||
|
||||
|
||||
transient: function(entity, key, fn) {
|
||||
var id = entity.id,
|
||||
transients = this.transients[id] ||
|
||||
(this.transients[id] = {});
|
||||
var id = entity.id;
|
||||
var transients = this.transients[id] || (this.transients[id] = {});
|
||||
|
||||
if (transients[key] !== undefined) {
|
||||
return transients[key];
|
||||
@@ -67,8 +66,8 @@ coreGraph.prototype = {
|
||||
|
||||
|
||||
parentWays: function(entity) {
|
||||
var parents = this._parentWays[entity.id],
|
||||
result = [];
|
||||
var parents = this._parentWays[entity.id];
|
||||
var result = [];
|
||||
|
||||
if (parents) {
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
@@ -92,8 +91,8 @@ coreGraph.prototype = {
|
||||
|
||||
|
||||
parentRelations: function(entity) {
|
||||
var parents = this._parentRels[entity.id],
|
||||
result = [];
|
||||
var parents = this._parentRels[entity.id];
|
||||
var result = [];
|
||||
|
||||
if (parents) {
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
@@ -134,8 +133,8 @@ coreGraph.prototype = {
|
||||
// data into each state. To external consumers, it should appear as if the
|
||||
// graph always contained the newly downloaded data.
|
||||
rebase: function(entities, stack, force) {
|
||||
var base = this.base(),
|
||||
i, j, k, id;
|
||||
var base = this.base();
|
||||
var i, j, k, id;
|
||||
|
||||
for (i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
@@ -168,8 +167,8 @@ coreGraph.prototype = {
|
||||
|
||||
|
||||
_updateRebased: function() {
|
||||
var base = this.base(),
|
||||
i, k, child, id, keys;
|
||||
var base = this.base();
|
||||
var i, k, child, id, keys;
|
||||
|
||||
keys = Object.keys(this._parentWays);
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
@@ -206,17 +205,13 @@ coreGraph.prototype = {
|
||||
|
||||
// Updates calculated properties (parentWays, parentRels) for the specified change
|
||||
_updateCalculated: function(oldentity, entity, parentWays, parentRels) {
|
||||
|
||||
parentWays = parentWays || this._parentWays;
|
||||
parentRels = parentRels || this._parentRels;
|
||||
|
||||
var type = entity && entity.type || oldentity && oldentity.type,
|
||||
removed, added, ways, rels, i;
|
||||
var type = entity && entity.type || oldentity && oldentity.type;
|
||||
var removed, added, ways, rels, i;
|
||||
|
||||
|
||||
if (type === 'way') {
|
||||
|
||||
// Update parentWays
|
||||
if (type === 'way') { // Update parentWays
|
||||
if (oldentity && entity) {
|
||||
removed = _difference(oldentity.nodes, entity.nodes);
|
||||
added = _difference(entity.nodes, oldentity.nodes);
|
||||
@@ -236,9 +231,7 @@ coreGraph.prototype = {
|
||||
parentWays[added[i]] = ways;
|
||||
}
|
||||
|
||||
} else if (type === 'relation') {
|
||||
|
||||
// Update parentRels
|
||||
} else if (type === 'relation') { // Update parentRels
|
||||
if (oldentity && entity) {
|
||||
removed = _difference(oldentity.members, entity.members);
|
||||
added = _difference(entity.members, oldentity);
|
||||
@@ -262,8 +255,7 @@ coreGraph.prototype = {
|
||||
|
||||
|
||||
replace: function(entity) {
|
||||
if (this.entities[entity.id] === entity)
|
||||
return this;
|
||||
if (this.entities[entity.id] === entity) return this;
|
||||
|
||||
return this.update(function() {
|
||||
this._updateCalculated(this.entities[entity.id], entity);
|
||||
@@ -281,11 +273,9 @@ coreGraph.prototype = {
|
||||
|
||||
|
||||
revert: function(id) {
|
||||
var baseEntity = this.base().entities[id],
|
||||
headEntity = this.entities[id];
|
||||
|
||||
if (headEntity === baseEntity)
|
||||
return this;
|
||||
var baseEntity = this.base().entities[id];
|
||||
var headEntity = this.entities[id];
|
||||
if (headEntity === baseEntity) return this;
|
||||
|
||||
return this.update(function() {
|
||||
this._updateCalculated(headEntity, baseEntity);
|
||||
@@ -296,7 +286,6 @@ coreGraph.prototype = {
|
||||
|
||||
update: function() {
|
||||
var graph = this.frozen ? coreGraph(this, true) : this;
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
arguments[i].call(graph, graph);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import _some from 'lodash-es/some';
|
||||
|
||||
import { actionDetachNode, actionMoveNode } from '../actions';
|
||||
import { behaviorOperation } from '../behavior';
|
||||
import { modeMove } from '../modes';
|
||||
import { t } from '../util/locale';
|
||||
|
||||
|
||||
export function operationDetachNode(selectedIDs, context) {
|
||||
var nodeID = selectedIDs.length && selectedIDs[0];
|
||||
var action = actionDetachNode(nodeID);
|
||||
|
||||
var operation = function () {
|
||||
context.perform(action); // do the detach
|
||||
|
||||
var mouse = context.map().mouseCoordinates();
|
||||
if (mouse.some(isNaN)) {
|
||||
enterMoveMode();
|
||||
|
||||
} else {
|
||||
// move detached node to the mouse location (transitioned)
|
||||
context.perform(actionMoveNode(nodeID, mouse));
|
||||
|
||||
// after transition completes, put at final mouse location and enter move mode.
|
||||
window.setTimeout(function() {
|
||||
mouse = context.map().mouseCoordinates();
|
||||
context.replace(actionMoveNode(nodeID, mouse));
|
||||
enterMoveMode();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function enterMoveMode() {
|
||||
var baseGraph = context.graph();
|
||||
context.enter(modeMove(context, [nodeID], baseGraph));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
operation.available = function () {
|
||||
if (selectedIDs.length !== 1) return false;
|
||||
|
||||
var graph = context.graph();
|
||||
var entity = graph.hasEntity(nodeID);
|
||||
if (!entity) return false;
|
||||
|
||||
return entity.type === 'node' &&
|
||||
entity.hasInterestingTags() &&
|
||||
graph.parentWays(entity).length > 0;
|
||||
};
|
||||
|
||||
|
||||
operation.disabled = function () {
|
||||
var reason;
|
||||
if (_some(selectedIDs, context.hasHiddenConnections)) {
|
||||
reason = 'connected_to_hidden';
|
||||
}
|
||||
return action.disabled(context.graph()) || reason;
|
||||
};
|
||||
|
||||
|
||||
operation.tooltip = function () {
|
||||
var disableReason = operation.disabled();
|
||||
if (disableReason) {
|
||||
return t('operations.detach_node.' + disableReason,
|
||||
{ relation: context.presets().item('type/restriction').name() });
|
||||
} else {
|
||||
return t('operations.detach_node.description');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
operation.annotation = function () {
|
||||
return t('operations.detach_node.annotation');
|
||||
};
|
||||
|
||||
|
||||
operation.id = 'detach-node';
|
||||
operation.keys = [t('operations.detach_node.key')];
|
||||
operation.title = t('operations.detach_node.title');
|
||||
operation.behavior = behaviorOperation(context).which(operation);
|
||||
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
@@ -10,3 +10,4 @@ export { operationReverse } from './reverse';
|
||||
export { operationRotate } from './rotate';
|
||||
export { operationSplit } from './split';
|
||||
export { operationStraighten } from './straighten';
|
||||
export { operationDetachNode } from './detach_node';
|
||||
|
||||
@@ -42,8 +42,7 @@ _extend(osmRelation.prototype, {
|
||||
|
||||
|
||||
copy: function(resolver, copies) {
|
||||
if (copies[this.id])
|
||||
return copies[this.id];
|
||||
if (copies[this.id]) return copies[this.id];
|
||||
|
||||
var copy = osmEntity.prototype.copy.call(this, resolver, copies);
|
||||
|
||||
|
||||
+18
-19
@@ -31,8 +31,7 @@ _extend(osmWay.prototype, {
|
||||
|
||||
|
||||
copy: function(resolver, copies) {
|
||||
if (copies[this.id])
|
||||
return copies[this.id];
|
||||
if (copies[this.id]) return copies[this.id];
|
||||
|
||||
var copy = osmEntity.prototype.copy.call(this, resolver, copies);
|
||||
|
||||
@@ -239,9 +238,9 @@ _extend(osmWay.prototype, {
|
||||
unclose: function() {
|
||||
if (!this.isClosed()) return this;
|
||||
|
||||
var nodes = this.nodes.slice(),
|
||||
connector = this.first(),
|
||||
i = nodes.length - 1;
|
||||
var nodes = this.nodes.slice();
|
||||
var connector = this.first();
|
||||
var i = nodes.length - 1;
|
||||
|
||||
// remove trailing connectors..
|
||||
while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
|
||||
@@ -260,9 +259,9 @@ _extend(osmWay.prototype, {
|
||||
// Consecutive duplicates are eliminated including existing ones.
|
||||
// Circularity is always preserved when adding a node.
|
||||
addNode: function(id, index) {
|
||||
var nodes = this.nodes.slice(),
|
||||
isClosed = this.isClosed(),
|
||||
max = isClosed ? nodes.length - 1 : nodes.length;
|
||||
var nodes = this.nodes.slice();
|
||||
var isClosed = this.isClosed();
|
||||
var max = isClosed ? nodes.length - 1 : nodes.length;
|
||||
|
||||
if (index === undefined) {
|
||||
index = max;
|
||||
@@ -309,9 +308,9 @@ _extend(osmWay.prototype, {
|
||||
// Consecutive duplicates are eliminated including existing ones.
|
||||
// Circularity is preserved when updating a node.
|
||||
updateNode: function(id, index) {
|
||||
var nodes = this.nodes.slice(),
|
||||
isClosed = this.isClosed(),
|
||||
max = nodes.length - 1;
|
||||
var nodes = this.nodes.slice();
|
||||
var isClosed = this.isClosed();
|
||||
var max = nodes.length - 1;
|
||||
|
||||
if (index === undefined || index < 0 || index > max) {
|
||||
throw new RangeError('index ' + index + ' out of range 0..' + max);
|
||||
@@ -353,13 +352,13 @@ _extend(osmWay.prototype, {
|
||||
// Replaces each occurrence of node id needle with replacement.
|
||||
// Consecutive duplicates are eliminated including existing ones.
|
||||
// Circularity is preserved.
|
||||
replaceNode: function(needle, replacement) {
|
||||
var nodes = this.nodes.slice(),
|
||||
isClosed = this.isClosed();
|
||||
replaceNode: function(needleID, replacementID) {
|
||||
var nodes = this.nodes.slice();
|
||||
var isClosed = this.isClosed();
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
if (nodes[i] === needle) {
|
||||
nodes[i] = replacement;
|
||||
if (nodes[i] === needleID) {
|
||||
nodes[i] = replacementID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,12 +373,12 @@ _extend(osmWay.prototype, {
|
||||
},
|
||||
|
||||
|
||||
// Removes each occurrence of node id needle with replacement.
|
||||
// Removes each occurrence of node id.
|
||||
// Consecutive duplicates are eliminated including existing ones.
|
||||
// Circularity is preserved.
|
||||
removeNode: function(id) {
|
||||
var nodes = this.nodes.slice(),
|
||||
isClosed = this.isClosed();
|
||||
var nodes = this.nodes.slice();
|
||||
var isClosed = this.isClosed();
|
||||
|
||||
nodes = nodes
|
||||
.filter(function(node) { return node !== id; })
|
||||
|
||||
@@ -48,10 +48,10 @@ export function rendererBackgroundSource(data) {
|
||||
var best = !!source.best;
|
||||
var template = source.template;
|
||||
|
||||
source.scaleExtent = data.scaleExtent || [0, 22];
|
||||
source.tileSize = data.tileSize || 256;
|
||||
source.zoomExtent = data.zoomExtent || [0, 22];
|
||||
source.overzoom = data.overzoom !== false;
|
||||
|
||||
|
||||
source.offset = function(_) {
|
||||
if (!arguments.length) return offset;
|
||||
offset = _;
|
||||
@@ -133,8 +133,8 @@ export function rendererBackgroundSource(data) {
|
||||
var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
|
||||
var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
|
||||
return template
|
||||
.replace('{width}', 256)
|
||||
.replace('{height}', 256)
|
||||
.replace('{width}', this.tileSize)
|
||||
.replace('{height}', this.tileSize)
|
||||
.replace('{proj}', this.projection)
|
||||
.replace('{bbox}', minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y);
|
||||
}
|
||||
@@ -171,8 +171,8 @@ export function rendererBackgroundSource(data) {
|
||||
|
||||
|
||||
source.validZoom = function(z) {
|
||||
return source.scaleExtent[0] <= z &&
|
||||
(source.overzoom || source.scaleExtent[1] > z);
|
||||
return source.zoomExtent[0] <= z &&
|
||||
(source.overzoom || source.zoomExtent[1] > z);
|
||||
};
|
||||
|
||||
|
||||
@@ -347,13 +347,13 @@ rendererBackgroundSource.Esri = function(data) {
|
||||
}
|
||||
|
||||
// if any tiles are missing at level 20 we restrict maxZoom to 19
|
||||
esri.scaleExtent[1] = (hasTiles ? 22 : 19);
|
||||
esri.zoomExtent[1] = (hasTiles ? 22 : 19);
|
||||
});
|
||||
};
|
||||
|
||||
esri.getMetadata = function(center, tileCoord, callback) {
|
||||
var tileId = tileCoord.slice(0, 3).join('/');
|
||||
var zoom = Math.min(tileCoord[2], esri.scaleExtent[1]);
|
||||
var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
|
||||
var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
|
||||
var unknown = t('info_panels.background.unknown');
|
||||
var metadataLayer;
|
||||
|
||||
@@ -6,10 +6,10 @@ import { utilPrefixCSSProperty, utilTiler } from '../util';
|
||||
|
||||
|
||||
export function rendererTileLayer(context) {
|
||||
var tileSize = 256;
|
||||
var transformProp = utilPrefixCSSProperty('Transform');
|
||||
var tiler = utilTiler();
|
||||
|
||||
var _tileSize = 256;
|
||||
var _projection;
|
||||
var _cache = {};
|
||||
var _tileOrigin;
|
||||
@@ -19,7 +19,7 @@ export function rendererTileLayer(context) {
|
||||
|
||||
function tileSizeAtZoom(d, z) {
|
||||
var EPSILON = 0.002;
|
||||
return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + EPSILON;
|
||||
return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export function rendererTileLayer(context) {
|
||||
|
||||
// Update tiles based on current state of `projection`.
|
||||
function background(selection) {
|
||||
_zoom = geoScaleToZoom(_projection.scale(), tileSize);
|
||||
_zoom = geoScaleToZoom(_projection.scale(), _tileSize);
|
||||
|
||||
var pixelOffset;
|
||||
if (_source) {
|
||||
@@ -140,7 +140,7 @@ export function rendererTileLayer(context) {
|
||||
}
|
||||
|
||||
function imageTransform(d) {
|
||||
var ts = tileSize * Math.pow(2, _zoom - d[2]);
|
||||
var ts = _tileSize * Math.pow(2, _zoom - d[2]);
|
||||
var scale = tileSizeAtZoom(d, _zoom);
|
||||
return 'translate(' +
|
||||
((d[0] * ts) - _tileOrigin[0]) + 'px,' +
|
||||
@@ -149,7 +149,7 @@ export function rendererTileLayer(context) {
|
||||
}
|
||||
|
||||
function tileCenter(d) {
|
||||
var ts = tileSize * Math.pow(2, _zoom - d[2]);
|
||||
var ts = _tileSize * Math.pow(2, _zoom - d[2]);
|
||||
return [
|
||||
((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
|
||||
((d[1] * ts) - _tileOrigin[1] + (ts / 2))
|
||||
@@ -270,8 +270,10 @@ export function rendererTileLayer(context) {
|
||||
background.source = function(_) {
|
||||
if (!arguments.length) return _source;
|
||||
_source = _;
|
||||
_tileSize = _source.tileSize;
|
||||
_cache = {};
|
||||
tiler.scaleExtent(_source.scaleExtent);
|
||||
// tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent); // not yet
|
||||
tiler.zoomExtent(_source.zoomExtent);
|
||||
return background;
|
||||
};
|
||||
|
||||
|
||||
@@ -17,12 +17,10 @@ import {
|
||||
|
||||
import rbush from 'rbush';
|
||||
|
||||
import { geoExtent } from '../geo';
|
||||
import { geoExtent, geoScaleToZoom } from '../geo';
|
||||
import { svgDefs } from '../svg';
|
||||
import { utilDetect } from '../util/detect';
|
||||
import { utilQsString, utilRebind, utilTiler } from '../util';
|
||||
|
||||
var geoTile = utilTiler().skipNullIsland(true);
|
||||
|
||||
var apibase = 'https://a.mapillary.com/v3/';
|
||||
var viewercss = 'mapillary-js/mapillary.min.css';
|
||||
@@ -30,7 +28,8 @@ var viewerjs = 'mapillary-js/mapillary.min.js';
|
||||
var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
|
||||
var maxResults = 1000;
|
||||
var tileZoom = 14;
|
||||
var dispatch = d3_dispatch('loadedImages', 'loadedSigns');
|
||||
var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
|
||||
var dispatch = d3_dispatch('loadedImages', 'loadedSigns', 'bearingChanged');
|
||||
var _mlyFallback = false;
|
||||
var _mlyCache;
|
||||
var _mlyClicks;
|
||||
@@ -54,11 +53,8 @@ function maxPageAtZoom(z) {
|
||||
|
||||
|
||||
function loadTiles(which, url, projection) {
|
||||
var s = projection.scale() * 2 * Math.PI;
|
||||
var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0));
|
||||
|
||||
var dimension = projection.clipExtent()[1];
|
||||
var tiles = geoTile.getTiles(projection, dimension, tileZoom);
|
||||
var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
|
||||
var tiles = tiler.getTiles(projection);
|
||||
|
||||
// abort inflight requests that are no longer needed
|
||||
var cache = _mlyCache[which];
|
||||
@@ -475,6 +471,7 @@ export default {
|
||||
|
||||
_mlyViewer = new Mapillary.Viewer('mly', clientId, null, opts);
|
||||
_mlyViewer.on('nodechanged', nodeChanged);
|
||||
_mlyViewer.on('bearingchanged', bearingChanged);
|
||||
_mlyViewer.moveToKey(imageKey)
|
||||
.catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console
|
||||
}
|
||||
@@ -509,6 +506,10 @@ export default {
|
||||
that.selectImage(undefined, node.key, true);
|
||||
}
|
||||
}
|
||||
|
||||
function bearingChanged(e) {
|
||||
dispatch.call('bearingChanged', undefined, e);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -21,22 +21,21 @@ import {
|
||||
|
||||
import rbush from 'rbush';
|
||||
|
||||
import { geoExtent } from '../geo';
|
||||
|
||||
import { utilTiler } from '../util';
|
||||
import { geoExtent, geoScaleToZoom } from '../geo';
|
||||
import { utilDetect } from '../util/detect';
|
||||
|
||||
import {
|
||||
utilQsString,
|
||||
utilRebind,
|
||||
utilSetTransform
|
||||
utilSetTransform,
|
||||
utilTiler
|
||||
} from '../util';
|
||||
|
||||
var geoTile = utilTiler().skipNullIsland(true);
|
||||
|
||||
var apibase = 'https://openstreetcam.org';
|
||||
var maxResults = 1000;
|
||||
var tileZoom = 14;
|
||||
var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
|
||||
var dispatch = d3_dispatch('loadedImages');
|
||||
var imgZoom = d3_zoom()
|
||||
.extent([[0, 0], [320, 240]])
|
||||
@@ -63,11 +62,8 @@ function maxPageAtZoom(z) {
|
||||
|
||||
|
||||
function loadTiles(which, url, projection) {
|
||||
var s = projection.scale() * 2 * Math.PI;
|
||||
var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0));
|
||||
|
||||
var dimension = projection.clipExtent()[1];
|
||||
var tiles = geoTile.getTiles(projection, dimension, tileZoom);
|
||||
var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
|
||||
var tiles = tiler.getTiles(projection);
|
||||
|
||||
// abort inflight requests that are no longer needed
|
||||
var cache = _oscCache[which];
|
||||
@@ -123,8 +119,8 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
}
|
||||
|
||||
var features = data.currentPageItems.map(function(item) {
|
||||
var loc = [+item.lng, +item.lat],
|
||||
d;
|
||||
var loc = [+item.lng, +item.lat];
|
||||
var d;
|
||||
|
||||
if (which === 'images') {
|
||||
d = {
|
||||
@@ -172,14 +168,14 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
function partitionViewport(psize, projection) {
|
||||
var dimensions = projection.clipExtent()[1];
|
||||
psize = psize || 16;
|
||||
var cols = d3_range(0, dimensions[0], psize),
|
||||
rows = d3_range(0, dimensions[1], psize),
|
||||
partitions = [];
|
||||
var cols = d3_range(0, dimensions[0], psize);
|
||||
var rows = d3_range(0, dimensions[1], psize);
|
||||
var partitions = [];
|
||||
|
||||
rows.forEach(function(y) {
|
||||
cols.forEach(function(x) {
|
||||
var min = [x, y + psize],
|
||||
max = [x + psize, y];
|
||||
var min = [x, y + psize];
|
||||
var max = [x + psize, y];
|
||||
partitions.push(
|
||||
geoExtent(projection.invert(min), projection.invert(max)));
|
||||
});
|
||||
|
||||
+67
-57
@@ -33,8 +33,8 @@ import {
|
||||
utilQsString
|
||||
} from '../util';
|
||||
|
||||
var geoTile = utilTiler();
|
||||
|
||||
var tiler = utilTiler();
|
||||
var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
|
||||
var urlroot = 'https://www.openstreetmap.org';
|
||||
var oauth = osmAuth({
|
||||
@@ -77,6 +77,17 @@ function abortRequest(i) {
|
||||
}
|
||||
|
||||
|
||||
function abortUnwantedRequests(cache, tiles) {
|
||||
_forEach(cache.inflight, function(v, k) {
|
||||
var wanted = _find(tiles, function(tile) { return k === tile.id; });
|
||||
if (!wanted) {
|
||||
abortRequest(v);
|
||||
delete cache.inflight[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getLoc(attrs) {
|
||||
var lon = attrs.lon && attrs.lon.value;
|
||||
var lat = attrs.lat && attrs.lat.value;
|
||||
@@ -775,78 +786,44 @@ export default {
|
||||
},
|
||||
|
||||
|
||||
// Load data (entities or notes) from the API in tiles
|
||||
// Load data (entities) from the API in tiles
|
||||
// GET /api/0.6/map?bbox=
|
||||
// GET /api/0.6/notes?bbox=
|
||||
loadTiles: function(projection, dimensions, callback, noteOptions) {
|
||||
loadTiles: function(projection, callback) {
|
||||
if (_off) return;
|
||||
|
||||
var that = this;
|
||||
|
||||
// are we loading entities or notes?
|
||||
var loadingNotes = (noteOptions !== undefined);
|
||||
var path, cache, tilezoom, throttleLoadUsers;
|
||||
|
||||
if (loadingNotes) {
|
||||
noteOptions = _extend({ limit: 10000, closed: 7}, noteOptions);
|
||||
path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
|
||||
cache = _noteCache;
|
||||
tilezoom = _noteZoom;
|
||||
throttleLoadUsers = _throttle(function() {
|
||||
var uids = Object.keys(_userCache.toLoad);
|
||||
if (!uids.length) return;
|
||||
that.loadUsers(uids, function() {}); // eagerly load user details
|
||||
}, 750);
|
||||
} else {
|
||||
path = '/api/0.6/map?bbox=';
|
||||
cache = _tileCache;
|
||||
tilezoom = _tileZoom;
|
||||
}
|
||||
var path = '/api/0.6/map?bbox=';
|
||||
|
||||
// determine the needed tiles to cover the view
|
||||
var tiles = geoTile.getTiles(projection, dimensions, tilezoom);
|
||||
var tiles = tiler.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection);
|
||||
|
||||
// abort inflight requests that are no longer needed
|
||||
var hadRequests = !_isEmpty(cache.inflight);
|
||||
_forEach(cache.inflight, function(v, k) {
|
||||
var wanted = _find(tiles, function(tile) { return k === tile.id; });
|
||||
if (!wanted) {
|
||||
abortRequest(v);
|
||||
delete cache.inflight[k];
|
||||
}
|
||||
});
|
||||
|
||||
if (hadRequests && !loadingNotes && _isEmpty(cache.inflight)) {
|
||||
var hadRequests = !_isEmpty(_tileCache.inflight);
|
||||
abortUnwantedRequests(_tileCache, tiles);
|
||||
if (hadRequests && _isEmpty(_tileCache.inflight)) {
|
||||
dispatch.call('loaded'); // stop the spinner
|
||||
}
|
||||
|
||||
// issue new requests..
|
||||
tiles.forEach(function(tile) {
|
||||
if (cache.loaded[tile.id] || cache.inflight[tile.id]) return;
|
||||
if (!loadingNotes && _isEmpty(cache.inflight)) {
|
||||
if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
|
||||
if (_isEmpty(_tileCache.inflight)) {
|
||||
dispatch.call('loading'); // start the spinner
|
||||
}
|
||||
|
||||
var options = { skipSeen: !loadingNotes };
|
||||
cache.inflight[tile.id] = that.loadFromAPI(
|
||||
var options = { skipSeen: true };
|
||||
_tileCache.inflight[tile.id] = that.loadFromAPI(
|
||||
path + tile.extent.toParam(),
|
||||
function(err, parsed) {
|
||||
delete cache.inflight[tile.id];
|
||||
delete _tileCache.inflight[tile.id];
|
||||
if (!err) {
|
||||
cache.loaded[tile.id] = true;
|
||||
_tileCache.loaded[tile.id] = true;
|
||||
}
|
||||
|
||||
if (loadingNotes) {
|
||||
throttleLoadUsers();
|
||||
dispatch.call('loadedNotes');
|
||||
|
||||
} else {
|
||||
if (callback) {
|
||||
callback(err, _extend({ data: parsed }, tile));
|
||||
}
|
||||
if (_isEmpty(cache.inflight)) {
|
||||
dispatch.call('loaded'); // stop the spinner
|
||||
}
|
||||
if (callback) {
|
||||
callback(err, _extend({ data: parsed }, tile));
|
||||
}
|
||||
if (_isEmpty(_tileCache.inflight)) {
|
||||
dispatch.call('loaded'); // stop the spinner
|
||||
}
|
||||
},
|
||||
options
|
||||
@@ -855,11 +832,44 @@ export default {
|
||||
},
|
||||
|
||||
|
||||
// Load notes from the API (just calls this.loadTiles)
|
||||
// Load notes from the API in tiles
|
||||
// GET /api/0.6/notes?bbox=
|
||||
loadNotes: function(projection, dimensions, noteOptions) {
|
||||
noteOptions = _extend({ limit: 10000, closed: 7}, noteOptions);
|
||||
this.loadTiles(projection, dimensions, null, noteOptions);
|
||||
loadNotes: function(projection, noteOptions) {
|
||||
noteOptions = _extend({ limit: 10000, closed: 7 }, noteOptions);
|
||||
if (_off) return;
|
||||
|
||||
var that = this;
|
||||
var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
|
||||
var throttleLoadUsers = _throttle(function() {
|
||||
var uids = Object.keys(_userCache.toLoad);
|
||||
if (!uids.length) return;
|
||||
that.loadUsers(uids, function() {}); // eagerly load user details
|
||||
}, 750);
|
||||
|
||||
// determine the needed tiles to cover the view
|
||||
var tiles = tiler.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection);
|
||||
|
||||
// abort inflight requests that are no longer needed
|
||||
abortUnwantedRequests(_noteCache, tiles);
|
||||
|
||||
// issue new requests..
|
||||
tiles.forEach(function(tile) {
|
||||
if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
|
||||
|
||||
var options = { skipSeen: false };
|
||||
_noteCache.inflight[tile.id] = that.loadFromAPI(
|
||||
path + tile.extent.toParam(),
|
||||
function(err) {
|
||||
delete _noteCache.inflight[tile.id];
|
||||
if (!err) {
|
||||
_noteCache.loaded[tile.id] = true;
|
||||
}
|
||||
throttleLoadUsers();
|
||||
dispatch.call('loadedNotes');
|
||||
},
|
||||
options
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import { utilQsString, utilRebind, utilTiler } from '../util';
|
||||
|
||||
import Q from 'q';
|
||||
|
||||
var geoTile = utilTiler().skipNullIsland(true);
|
||||
|
||||
var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
|
||||
var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
|
||||
@@ -42,10 +41,12 @@ var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
|
||||
var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
|
||||
var maxResults = 2000;
|
||||
var tileZoom = 16.5;
|
||||
var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
|
||||
var dispatch = d3_dispatch('loadedBubbles', 'viewerChanged');
|
||||
var minHfov = 10; // zoom in degrees: 20, 10, 5
|
||||
var maxHfov = 90; // zoom out degrees
|
||||
var defaultHfov = 45;
|
||||
|
||||
var _hires = false;
|
||||
var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
|
||||
var _currScene = 0;
|
||||
@@ -80,13 +81,7 @@ function localeTimestamp(s) {
|
||||
* loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
|
||||
*/
|
||||
function loadTiles(which, url, projection, margin) {
|
||||
var s = projection.scale() * 2 * Math.PI;
|
||||
var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0));
|
||||
|
||||
var dimension = projection.clipExtent()[1];
|
||||
var tiles = geoTile
|
||||
.margin(margin)
|
||||
.getTiles(projection, dimension, tileZoom);
|
||||
var tiles = tiler.margin(margin).getTiles(projection);
|
||||
|
||||
// abort inflight requests that are no longer needed
|
||||
var cache = _ssCache[which];
|
||||
@@ -100,14 +95,14 @@ function loadTiles(which, url, projection, margin) {
|
||||
});
|
||||
|
||||
tiles.forEach(function (tile) {
|
||||
loadNextTilePage(which, currZoom, url, tile);
|
||||
loadNextTilePage(which, url, tile);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* loadNextTilePage() load data for the next tile page in line.
|
||||
*/
|
||||
function loadNextTilePage(which, currZoom, url, tile) {
|
||||
function loadNextTilePage(which, url, tile) {
|
||||
var cache = _ssCache[which];
|
||||
var nextPage = cache.nextPage[tile.id] || 0;
|
||||
var id = tile.id + ',' + String(nextPage);
|
||||
@@ -121,7 +116,7 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
// [].shift() removes the first element, some statistics info, not a bubble point
|
||||
bubbles.shift();
|
||||
|
||||
var features = bubbles.map(function (bubble) {
|
||||
var features = bubbles.map(function(bubble) {
|
||||
if (cache.points[bubble.id]) return null; // skip duplicates
|
||||
|
||||
var loc = [bubble.lo, bubble.la];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
import _isNumber from 'lodash-es/isNumber';
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
import { svgPath, svgPointTransform } from './index';
|
||||
import { services } from '../services';
|
||||
@@ -11,6 +12,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
var minViewfieldZoom = 18;
|
||||
var layer = d3_select(null);
|
||||
var _mapillary;
|
||||
var viewerCompassAngle;
|
||||
|
||||
|
||||
function init() {
|
||||
@@ -24,6 +26,19 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
if (services.mapillary && !_mapillary) {
|
||||
_mapillary = services.mapillary;
|
||||
_mapillary.event.on('loadedImages', throttledRedraw);
|
||||
_mapillary.event.on('bearingChanged', function(e) {
|
||||
viewerCompassAngle = e;
|
||||
|
||||
// avoid updating if the map is currently transformed
|
||||
// e.g. during drags or easing.
|
||||
if (context.map().isTransformed()) return;
|
||||
|
||||
layer.selectAll('.viewfield-group.selected')
|
||||
.filter(function(d) {
|
||||
return d.pano;
|
||||
})
|
||||
.attr('transform', transform);
|
||||
});
|
||||
} else if (!services.mapillary && _mapillary) {
|
||||
_mapillary = null;
|
||||
}
|
||||
@@ -102,7 +117,9 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
function transform(d) {
|
||||
var t = svgPointTransform(projection)(d);
|
||||
if (d.ca) {
|
||||
if (d.pano && _isNumber(viewerCompassAngle)) {
|
||||
t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
|
||||
} else if (d.ca) {
|
||||
t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
|
||||
}
|
||||
return t;
|
||||
@@ -184,6 +201,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
viewfields.enter() // viewfields may or may not be drawn...
|
||||
.insert('path', 'circle') // but if they are, draw below the circles
|
||||
.attr('class', 'viewfield')
|
||||
.classed('pano', function() { return this.parentNode.__data__.pano; })
|
||||
.attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
|
||||
.attr('d', viewfieldPath);
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export function uiNoteEditor(context) {
|
||||
var editor = body.selectAll('.note-editor')
|
||||
.data([0]);
|
||||
|
||||
editor = editor.enter()
|
||||
editor.enter()
|
||||
.append('div')
|
||||
.attr('class', 'modal-section note-editor')
|
||||
.merge(editor)
|
||||
@@ -82,7 +82,7 @@ export function uiNoteEditor(context) {
|
||||
var footer = selection.selectAll('.footer')
|
||||
.data([0]);
|
||||
|
||||
footer = footer.enter()
|
||||
footer.enter()
|
||||
.append('div')
|
||||
.attr('class', 'footer')
|
||||
.merge(footer)
|
||||
|
||||
@@ -12,7 +12,7 @@ export { utilFunctor } from './util';
|
||||
export { utilGetAllNodes } from './util';
|
||||
export { utilGetPrototypeOf } from './util';
|
||||
export { utilGetSetValue } from './get_set_value';
|
||||
export { utilIdleWorker} from './idle_worker';
|
||||
export { utilIdleWorker } from './idle_worker';
|
||||
export { utilNoAuto } from './util';
|
||||
export { utilPrefixCSSProperty } from './util';
|
||||
export { utilPrefixDOMProperty } from './util';
|
||||
@@ -25,4 +25,4 @@ export { utilSuggestNames } from './suggest_names';
|
||||
export { utilTagText } from './util';
|
||||
export { utilTiler } from './tiler';
|
||||
export { utilTriggerEvent } from './trigger_event';
|
||||
export { utilWrap } from './util';
|
||||
export { utilWrap } from './util';
|
||||
+26
-21
@@ -1,18 +1,19 @@
|
||||
import { range as d3_range } from 'd3-array';
|
||||
import { geoExtent } from '../geo';
|
||||
import { geoExtent, geoScaleToZoom } from '../geo';
|
||||
|
||||
|
||||
export function utilTiler() {
|
||||
var _size = [960, 500];
|
||||
var _size = [256, 256];
|
||||
var _scale = 256;
|
||||
var _scaleExtent = [0, 20];
|
||||
var _tileSize = 256;
|
||||
var _zoomExtent = [0, 20];
|
||||
var _translate = [_size[0] / 2, _size[1] / 2];
|
||||
var _margin = 0;
|
||||
var _skipNullIsland = false;
|
||||
|
||||
|
||||
function bound(val) {
|
||||
return Math.min(_scaleExtent[1], Math.max(_scaleExtent[0], val));
|
||||
return Math.min(_zoomExtent[1], Math.max(_zoomExtent[0], val));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +33,7 @@ export function utilTiler() {
|
||||
|
||||
|
||||
function tiler() {
|
||||
var z = Math.max(Math.log(_scale) / Math.LN2 - 8, 0);
|
||||
var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
|
||||
var z0 = bound(Math.round(z));
|
||||
var k = Math.pow(2, z - z0 + 8);
|
||||
var origin = [
|
||||
@@ -72,26 +73,23 @@ export function utilTiler() {
|
||||
|
||||
|
||||
/**
|
||||
* getTiles() returns array of d3 geo tiles.
|
||||
* Using d3.geo.tiles.js from lib, gets tile extents for each grid tile in a grid created from
|
||||
* an area around (and including) the current map view extents.
|
||||
* getTiles() returns an array of tiles that cover the map view
|
||||
*/
|
||||
tiler.getTiles = function(projection, dimensions, tilezoom) {
|
||||
var s = projection.scale() * 2 * Math.PI;
|
||||
var z = Math.max(Math.log(s) / Math.log(2) - 8, 0);
|
||||
var ts = 256 * Math.pow(2, z - tilezoom);
|
||||
tiler.getTiles = function(projection) {
|
||||
var origin = [
|
||||
s / 2 - projection.translate()[0],
|
||||
s / 2 - projection.translate()[1]
|
||||
projection.scale() * Math.PI - projection.translate()[0],
|
||||
projection.scale() * Math.PI - projection.translate()[1]
|
||||
];
|
||||
|
||||
this
|
||||
.scaleExtent([tilezoom, tilezoom])
|
||||
.scale(s)
|
||||
.size(dimensions)
|
||||
.size(projection.clipExtent()[1])
|
||||
.scale(projection.scale() * 2 * Math.PI)
|
||||
.translate(projection.translate());
|
||||
|
||||
return tiler()
|
||||
var tiles = tiler();
|
||||
var ts = tiles.scale;
|
||||
|
||||
return tiles
|
||||
.map(function(tile) {
|
||||
if (_skipNullIsland && nearNullIsland(tile)) {
|
||||
return false;
|
||||
@@ -110,9 +108,16 @@ export function utilTiler() {
|
||||
};
|
||||
|
||||
|
||||
tiler.scaleExtent = function(val) {
|
||||
if (!arguments.length) return _scaleExtent;
|
||||
_scaleExtent = val;
|
||||
tiler.tileSize = function(val) {
|
||||
if (!arguments.length) return _tileSize;
|
||||
_tileSize = val;
|
||||
return tiler;
|
||||
};
|
||||
|
||||
|
||||
tiler.zoomExtent = function(val) {
|
||||
if (!arguments.length) return _zoomExtent;
|
||||
_zoomExtent = val;
|
||||
return tiler;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" width="20" height="20" viewBox="0 0 20 20">
|
||||
<path d="M9.414,4.929 L9.414,6.343 L10.828,7.757 L9.281,9.305 C9.576,9.447 9.877,9.635 10.121,9.879 C10.373,10.13 10.553,10.415 10.695,10.719 L12.243,9.172 L13.657,10.586 L15.071,10.586 L15.071,4.929 z" fill="currentColor"/>
|
||||
<path d="M8,9.987 C6.895,9.987 6,10.883 6,11.987 C6,13.092 6.895,13.987 8,13.987 C9.105,13.987 10,13.092 10,11.987 C10,10.883 9.105,9.987 8,9.987 z" fill="inherit"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 654 B |
+10
-5
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<title>Mocha Tests</title>
|
||||
@@ -7,6 +8,7 @@
|
||||
<link rel='stylesheet' href='../dist/iD.css'>
|
||||
<!-- <script src='../node_modules/d3/build/d3.js'></script> -->
|
||||
</head>
|
||||
|
||||
<body style="overflow:scroll">
|
||||
<div id='mocha'></div>
|
||||
|
||||
@@ -17,9 +19,9 @@
|
||||
<script src='../node_modules/happen/happen.js'></script>
|
||||
|
||||
<script>
|
||||
if (typeof initMochaPhantomJS === 'function') {
|
||||
initMochaPhantomJS()
|
||||
}
|
||||
if (typeof initMochaPhantomJS === 'function') {
|
||||
initMochaPhantomJS()
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
@@ -60,6 +62,7 @@
|
||||
<script src='spec/actions/straighten.js'></script>
|
||||
<script src='spec/actions/unrestrict_turn.js'></script>
|
||||
<script src='spec/actions/reflect.js'></script>
|
||||
<script src='spec/actions/detach_node.js'></script>
|
||||
|
||||
<script src='spec/behavior/hash.js'></script>
|
||||
<script src='spec/behavior/hover.js'></script>
|
||||
@@ -140,8 +143,10 @@
|
||||
<script src='spec/util/suggest_names.js'></script>
|
||||
<script src='spec/util/util.js'></script>
|
||||
|
||||
<script src='spec/operations/detach_node.js'></script>
|
||||
<script>
|
||||
window.mocha.run();
|
||||
window.mocha.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,624 @@
|
||||
describe('iD.actionDetachNode', function () {
|
||||
var tags = { 'name': 'test' };
|
||||
|
||||
function createTargetNode(id, lonlat) {
|
||||
return iD.osmNode({ id: id, loc: lonlat, tags: tags });
|
||||
}
|
||||
|
||||
describe('linear way', function () {
|
||||
var graph;
|
||||
beforeEach(function () {
|
||||
//
|
||||
// a -- b -- c -- d
|
||||
//
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode({ id: 'a', loc: [0, 0] }),
|
||||
iD.osmNode({ id: 'b', loc: [1, 0] }),
|
||||
iD.osmNode({ id: 'c', loc: [2, 0] }),
|
||||
iD.osmNode({ id: 'd', loc: [3, 0] }),
|
||||
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd'] })
|
||||
]);
|
||||
});
|
||||
|
||||
describe('target in first position', function () {
|
||||
beforeEach(function () {
|
||||
// Swap target into the location & position of A
|
||||
var targetNode = createTargetNode('a', graph.entity('a').loc);
|
||||
graph = graph.replace(targetNode);
|
||||
});
|
||||
|
||||
it('does not change length of way', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// Confirm that the way still has 4 nodes
|
||||
var target = assertionGraph.entity('-');
|
||||
expect(target.nodes.length).to.eql(4);
|
||||
});
|
||||
|
||||
it('does not change order of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// Confirm that the way is ordered correctly
|
||||
var target = assertionGraph.entity('-');
|
||||
// Note that we can't be sure of the id of the replacement node
|
||||
// so we only assert the nodes we know the ids for
|
||||
// As we have already confirmed the size of the array we can assume
|
||||
// that the replacement node is in the correct posisiton by a process of elimination
|
||||
expect(target.nodes[1]).to.eql('b');
|
||||
expect(target.nodes[2]).to.eql('c');
|
||||
expect(target.nodes[3]).to.eql('d');
|
||||
});
|
||||
|
||||
it('does not change location of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// Confirm that the nodes have not moved, including the replacement node
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 0]);
|
||||
expect(assertionGraph.entity(nodes[3]).loc).to.eql([3, 0]);
|
||||
});
|
||||
|
||||
it('does replace target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
// Confirm that the target is no longer "a"
|
||||
expect(nodes[0]).not.to.eql('a');
|
||||
// and that the tags are not present
|
||||
expect(assertionGraph.entity(nodes[0]).tags).to.eql({});
|
||||
});
|
||||
|
||||
it('does detach target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// confirm that a still exists
|
||||
var targetNode = assertionGraph.entity('a');
|
||||
expect(targetNode).not.to.eql(undefined);
|
||||
// ... and that the location is correct
|
||||
expect(targetNode.loc).to.eql([0, 0]);
|
||||
// ... and that the tags are intact
|
||||
expect(targetNode.tags).to.eql(tags);
|
||||
// ... and that the parentWay is empty
|
||||
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('target in second position', function () {
|
||||
beforeEach(function () {
|
||||
// Swap target into the location & position of B
|
||||
var targetNode = createTargetNode('b', graph.entity('b').loc);
|
||||
graph = graph.replace(targetNode);
|
||||
});
|
||||
|
||||
it('does not change length of way', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Confirm that the way still has 4 nodes
|
||||
var target = assertionGraph.entity('-');
|
||||
expect(target.nodes.length).to.eql(4);
|
||||
});
|
||||
|
||||
it('does not change order of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Confirm that the way is ordered correctly
|
||||
var target = assertionGraph.entity('-');
|
||||
// Note that we can't be sure of the id of the replacement node
|
||||
// so we only assert the nodes we know the ids for
|
||||
// As we have already confirmed the size of the array we can assume
|
||||
// that the replacement node is in the correct posisiton by a process of elimination
|
||||
expect(target.nodes[0]).to.eql('a');
|
||||
expect(target.nodes[2]).to.eql('c');
|
||||
expect(target.nodes[3]).to.eql('d');
|
||||
});
|
||||
|
||||
it('does not change location of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Confirm that the nodes have not moved, including the replacement node
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 0]);
|
||||
expect(assertionGraph.entity(nodes[3]).loc).to.eql([3, 0]);
|
||||
});
|
||||
|
||||
it('does replace target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
// Confirm that the target is no longer "a"
|
||||
expect(nodes[1]).not.to.eql('b');
|
||||
// and that the tags are not present
|
||||
expect(assertionGraph.entity(nodes[1]).tags).to.eql({});
|
||||
});
|
||||
|
||||
it('does detach target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// confirm that a still exists
|
||||
var targetNode = assertionGraph.entity('b');
|
||||
expect(targetNode).not.to.eql(undefined);
|
||||
// ... and that the location is correct
|
||||
expect(targetNode.loc).to.eql([1, 0]);
|
||||
// ... and that the tags are intact
|
||||
expect(targetNode.tags).to.eql(tags);
|
||||
// ... and that the parentWay is empty
|
||||
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('closed way', function () {
|
||||
var graph;
|
||||
beforeEach(function () {
|
||||
//
|
||||
// d -- c
|
||||
// | |
|
||||
// a -- b
|
||||
//
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode({ id: 'a', loc: [0, 0] }),
|
||||
iD.osmNode({ id: 'b', loc: [1, 0] }),
|
||||
iD.osmNode({ id: 'c', loc: [1, 1] }),
|
||||
iD.osmNode({ id: 'd', loc: [0, 1] }),
|
||||
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd', 'a'] })
|
||||
]);
|
||||
});
|
||||
|
||||
describe('target in first position', function () {
|
||||
beforeEach(function () {
|
||||
// Swap target into the location & position of A
|
||||
var targetNode = createTargetNode('a', graph.entity('a').loc);
|
||||
graph = graph.replace(targetNode);
|
||||
});
|
||||
|
||||
it('does not change length of way', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// Confirm that the way still has 5 nodes
|
||||
var target = assertionGraph.entity('-');
|
||||
expect(target.nodes.length).to.eql(5);
|
||||
});
|
||||
|
||||
it('does not change order of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// Confirm that the way is ordered correctly
|
||||
var target = assertionGraph.entity('-');
|
||||
// Note that we can't be sure of the id of the replacement node
|
||||
// so we only assert the nodes we know the ids for
|
||||
// As we have already confirmed the size of the array we can assume
|
||||
// that the replacement node is in the correct posisiton by a process of elimination
|
||||
expect(target.nodes[1]).to.eql('b');
|
||||
expect(target.nodes[2]).to.eql('c');
|
||||
expect(target.nodes[3]).to.eql('d');
|
||||
// Need to confirm that the id of the first & last node is the same so that the way remains closed
|
||||
expect(target.nodes[0]).to.eql(target.nodes[4]);
|
||||
});
|
||||
|
||||
it('does not change location of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// Confirm that the nodes have not moved, including the replacement node
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([1, 1]);
|
||||
expect(assertionGraph.entity(nodes[3]).loc).to.eql([0, 1]);
|
||||
// We don't need to assert node[4] location as we've already confirmed that it is the same as node 0
|
||||
});
|
||||
|
||||
it('does replace target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
// Confirm that the target is no longer "a"
|
||||
expect(nodes[0]).not.to.eql('a');
|
||||
// .. also in the tail position
|
||||
expect(nodes[4]).not.to.eql('a');
|
||||
// and that the tags are not present (already confirmed same node in position 0 & 4, so only need to check tags once)
|
||||
expect(assertionGraph.entity(nodes[0]).tags).to.eql({});
|
||||
});
|
||||
|
||||
it('does detach target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('a')(graph);
|
||||
|
||||
// confirm that a still exists
|
||||
var targetNode = assertionGraph.entity('a');
|
||||
expect(targetNode).not.to.eql(undefined);
|
||||
// ... and that the location is correct
|
||||
expect(targetNode.loc).to.eql([0, 0]);
|
||||
// ... and that the tags are intact
|
||||
expect(targetNode.tags).to.eql(tags);
|
||||
// ... and that the parentWay is empty
|
||||
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('target in second position', function () {
|
||||
beforeEach(function () {
|
||||
// Swap target into the location & position of B
|
||||
var targetNode = createTargetNode('b', graph.entity('b').loc);
|
||||
graph = graph.replace(targetNode);
|
||||
});
|
||||
|
||||
it('does not change length of way', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Confirm that the way still has 5 nodes
|
||||
var target = assertionGraph.entity('-');
|
||||
expect(target.nodes.length).to.eql(5);
|
||||
});
|
||||
|
||||
it('does not change order of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Confirm that the way is ordered correctly
|
||||
var target = assertionGraph.entity('-');
|
||||
// Note that we can't be sure of the id of the replacement node
|
||||
// so we only assert the nodes we know the ids for
|
||||
// As we have already confirmed the size of the array we can assume
|
||||
// that the replacement node is in the correct posisiton by a process of elimination
|
||||
expect(target.nodes[0]).to.eql('a');
|
||||
expect(target.nodes[2]).to.eql('c');
|
||||
expect(target.nodes[3]).to.eql('d');
|
||||
expect(target.nodes[4]).to.eql('a');
|
||||
});
|
||||
|
||||
it('does not change location of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Confirm that the nodes have not moved, including the replacement node
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([1, 1]);
|
||||
expect(assertionGraph.entity(nodes[3]).loc).to.eql([0, 1]);
|
||||
// Confirmed already that node[4] is node[0] so no further assertion needed
|
||||
});
|
||||
|
||||
it('does replace target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
// Confirm that the target is no longer "a"
|
||||
expect(nodes[1]).not.to.eql('b');
|
||||
// and that the tags are not present
|
||||
expect(assertionGraph.entity(nodes[1]).tags).to.eql({});
|
||||
});
|
||||
|
||||
it('does detach target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// confirm that a still exists
|
||||
var targetNode = assertionGraph.entity('b');
|
||||
expect(targetNode).not.to.eql(undefined);
|
||||
// ... and that the location is correct
|
||||
expect(targetNode.loc).to.eql([1, 0]);
|
||||
// ... and that the tags are intact
|
||||
expect(targetNode.tags).to.eql(tags);
|
||||
// ... and that the parentWay is empty
|
||||
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('intersecting linear ways', function () {
|
||||
var graph;
|
||||
beforeEach(function () {
|
||||
//
|
||||
// f
|
||||
// ‖
|
||||
// e
|
||||
// ‖
|
||||
// a -- b -- c -- d
|
||||
//
|
||||
// Node c represents the target
|
||||
//
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode({ id: 'a', loc: [0, 0] }),
|
||||
iD.osmNode({ id: 'b', loc: [1, 0] }),
|
||||
iD.osmNode({ id: 'c', loc: [2, 0], tags: tags }),
|
||||
iD.osmNode({ id: 'd', loc: [3, 0] }),
|
||||
iD.osmNode({ id: 'e', loc: [2, 1] }),
|
||||
iD.osmNode({ id: 'f', loc: [2, 2] }),
|
||||
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd'] }),
|
||||
iD.osmWay({ id: '=', nodes: ['c', 'e', 'f'] })
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not change length of ways', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// Confirm that the way still has 4 nodes
|
||||
var target = assertionGraph.entity('-');
|
||||
expect(target.nodes.length).to.eql(4);
|
||||
// .. and second way has 3
|
||||
target = assertionGraph.entity('=');
|
||||
expect(target.nodes.length).to.eql(3);
|
||||
});
|
||||
|
||||
it('does not change order of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// Confirm that the way is ordered correctly
|
||||
var target = assertionGraph.entity('-');
|
||||
// Note that we can't be sure of the id of the replacement node
|
||||
// so we only assert the nodes we know the ids for
|
||||
// As we have already confirmed the size of the array we can assume
|
||||
// that the replacement node is in the correct posisiton by a process of elimination
|
||||
expect(target.nodes[0]).to.eql('a');
|
||||
expect(target.nodes[1]).to.eql('b');
|
||||
expect(target.nodes[3]).to.eql('d');
|
||||
// and second way
|
||||
target = assertionGraph.entity('=');
|
||||
expect(target.nodes[1]).to.eql('e');
|
||||
expect(target.nodes[2]).to.eql('f');
|
||||
});
|
||||
|
||||
it('does not change location of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// Confirm that the nodes have not moved, including the replacement node
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 0]);
|
||||
expect(assertionGraph.entity(nodes[3]).loc).to.eql([3, 0]);
|
||||
// and second way
|
||||
nodes = assertionGraph.entity('=').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([2, 0]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([2, 1]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 2]);
|
||||
});
|
||||
|
||||
it('uses same replacement node at intersection', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
// Confirm both ways have the same replacement node
|
||||
expect(assertionGraph.entity('-').nodes[2]).to.eql(assertionGraph.entity('=').nodes[0]);
|
||||
});
|
||||
|
||||
it('does replace target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
// Confirm that the target is no longer "c"
|
||||
expect(nodes[2]).not.to.eql('c');
|
||||
// and that the tags are not present
|
||||
expect(assertionGraph.entity(nodes[2]).tags).to.eql({});
|
||||
// Confirm that the second way's first node is the same
|
||||
expect(assertionGraph.entity('=').nodes[0]).to.eql(nodes[2]);
|
||||
});
|
||||
|
||||
it('does detach target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// confirm that a still exists
|
||||
var targetNode = assertionGraph.entity('c');
|
||||
expect(targetNode).not.to.eql(undefined);
|
||||
// ... and that the location is correct
|
||||
expect(targetNode.loc).to.eql([2, 0]);
|
||||
// ... and that the tags are intact
|
||||
expect(targetNode.tags).to.eql(tags);
|
||||
// ... and that the parentWay is empty
|
||||
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('intersecting closed way', function () {
|
||||
var graph;
|
||||
beforeEach(function () {
|
||||
//
|
||||
// g == f
|
||||
// ‖ ‖
|
||||
// d -- c == e
|
||||
// | |
|
||||
// a -- b
|
||||
//
|
||||
// c is the target node
|
||||
//
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode({ id: 'a', loc: [0, 0] }),
|
||||
iD.osmNode({ id: 'b', loc: [1, 0] }),
|
||||
iD.osmNode({ id: 'c', loc: [1, 1], tags: tags }),
|
||||
iD.osmNode({ id: 'd', loc: [0, 1] }),
|
||||
iD.osmNode({ id: 'e', loc: [2, 1] }),
|
||||
iD.osmNode({ id: 'f', loc: [2, 2] }),
|
||||
iD.osmNode({ id: 'g', loc: [1, 2] }),
|
||||
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd', 'a'] }),
|
||||
iD.osmWay({ id: '=', nodes: ['c', 'e', 'f', 'g', 'c'] })
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not change length of ways', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// Confirm that the way still has 5 nodes
|
||||
var target = assertionGraph.entity('-');
|
||||
expect(target.nodes.length).to.eql(5);
|
||||
// and the second
|
||||
target = assertionGraph.entity('=');
|
||||
expect(target.nodes.length).to.eql(5);
|
||||
});
|
||||
|
||||
it('does not change order of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// Confirm that the way is ordered correctly
|
||||
var target = assertionGraph.entity('-');
|
||||
// Note that we can't be sure of the id of the replacement node
|
||||
// so we only assert the nodes we know the ids for
|
||||
// As we have already confirmed the size of the array we can assume
|
||||
// that the replacement node is in the correct posisiton by a process of elimination
|
||||
expect(target.nodes[0]).to.eql('a');
|
||||
expect(target.nodes[1]).to.eql('b');
|
||||
expect(target.nodes[3]).to.eql('d');
|
||||
// Need to confirm that the id of the first & last node is the same so that the way remains closed
|
||||
expect(target.nodes[0]).to.eql(target.nodes[4]);
|
||||
// and the same for the other way
|
||||
target = assertionGraph.entity('=');
|
||||
expect(target.nodes[1]).to.eql('e');
|
||||
expect(target.nodes[2]).to.eql('f');
|
||||
expect(target.nodes[3]).to.eql('g');
|
||||
expect(target.nodes[0]).to.eql(target.nodes[4]);
|
||||
});
|
||||
|
||||
it('does not change location of nodes', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// Confirm that the nodes have not moved, including the replacement node
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([1, 1]);
|
||||
expect(assertionGraph.entity(nodes[3]).loc).to.eql([0, 1]);
|
||||
// We don't need to assert node[4] location as we've already confirmed that it is the same as node 0
|
||||
// and the other way
|
||||
nodes = assertionGraph.entity('=').nodes;
|
||||
expect(assertionGraph.entity(nodes[0]).loc).to.eql([1, 1]);
|
||||
expect(assertionGraph.entity(nodes[1]).loc).to.eql([2, 1]);
|
||||
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 2]);
|
||||
expect(assertionGraph.entity(nodes[3]).loc).to.eql([1, 2]);
|
||||
});
|
||||
|
||||
it('uses same replacement node at intersection', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
// Confirm both ways have the same replacement node
|
||||
expect(assertionGraph.entity('-').nodes[2]).to.eql(assertionGraph.entity('=').nodes[0]);
|
||||
});
|
||||
|
||||
it('does replace target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
var nodes = assertionGraph.entity('-').nodes;
|
||||
// Confirm that the target is no longer "c"
|
||||
expect(nodes[0]).not.to.eql('c');
|
||||
// .. also in the tail position
|
||||
expect(nodes[4]).not.to.eql('c');
|
||||
// and that the tags are not present (already confirmed same node in position 0 & 4, so only need to check tags once)
|
||||
expect(assertionGraph.entity(nodes[0]).tags).to.eql({});
|
||||
// Don't need to check for way 2 since we've already confirmed it is the same node
|
||||
});
|
||||
|
||||
it('does detach target node', function () {
|
||||
// Act
|
||||
var assertionGraph = iD.actionDetachNode('c')(graph);
|
||||
|
||||
// confirm that a still exists
|
||||
var targetNode = assertionGraph.entity('c');
|
||||
expect(targetNode).not.to.eql(undefined);
|
||||
// ... and that the location is correct
|
||||
expect(targetNode.loc).to.eql([1, 1]);
|
||||
// ... and that the tags are intact
|
||||
expect(targetNode.tags).to.eql(tags);
|
||||
// ... and that the parentWay is empty
|
||||
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('with relation', function () {
|
||||
var graph;
|
||||
|
||||
beforeEach(function () {
|
||||
//
|
||||
// a -- b -- c
|
||||
//
|
||||
// Node b represents the target
|
||||
// With a relationship for the way including b
|
||||
//
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode({ id: 'a', loc: [0, 0] }),
|
||||
iD.osmNode({ id: 'b', loc: [1, 0], tags: tags }),
|
||||
iD.osmNode({ id: 'c', loc: [2, 0] }),
|
||||
iD.osmWay({ id: '-', nodes: ['a', 'b', 'c'] }),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'route', route: 'foot'},
|
||||
members: [
|
||||
{ id: 'a', type: 'node', role: 'point' },
|
||||
{ id: 'b', type: 'node', role: 'point' },
|
||||
{ id: 'c', type: 'node', role: 'point' }
|
||||
]
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it('detached node not a member of relation', function () {
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
var targetNode = assertionGraph.entity('b');
|
||||
// Confirm is not a member of the relation
|
||||
expect(assertionGraph.parentRelations(targetNode).length).to.eql(0);
|
||||
});
|
||||
|
||||
it('new node is a member of relation', function () {
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Find the new node
|
||||
var targetWay = assertionGraph.entity('-');
|
||||
var newNodeId = targetWay.nodes.filter(function (m) {
|
||||
return m !== 'a' && m !== 'b' && m !== 'c';
|
||||
})[0];
|
||||
var newNode = assertionGraph.entity(newNodeId);
|
||||
|
||||
// Confirm is a member of the relation
|
||||
expect(assertionGraph.parentRelations(newNode).length).to.eql(1);
|
||||
expect(assertionGraph.parentRelations(newNode)[0].id).to.eql('r');
|
||||
});
|
||||
|
||||
it('Relation membership has the same properties', function () {
|
||||
var assertionGraph = iD.actionDetachNode('b')(graph);
|
||||
|
||||
// Find the new node
|
||||
var targetWay = assertionGraph.entity('-');
|
||||
var newNodeId = targetWay.nodes.filter(function (m) {
|
||||
return m !== 'a' && m !== 'b' && m !== 'c';
|
||||
})[0];
|
||||
|
||||
// Get the relation
|
||||
var targetRelation = assertionGraph.entity('r');
|
||||
// Find the member
|
||||
var targetMember = targetRelation.memberById(newNodeId);
|
||||
|
||||
// Confirm membership is the same as original (except for the new id)
|
||||
expect(targetMember).to.eql({ id: newNodeId, index: 1, type: 'node', role: 'point' });
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,166 @@
|
||||
describe('iD.operationDetachNode', function () {
|
||||
var fakeContext;
|
||||
var graph;
|
||||
|
||||
// Set up the fake context
|
||||
fakeContext = {};
|
||||
fakeContext.graph = function () { return graph; };
|
||||
fakeContext.hasHiddenConnections = function () { return false; };
|
||||
|
||||
var fakeTags = { 'name': 'fake' };
|
||||
|
||||
// Set up graph
|
||||
var createFakeNode = function (id, hasTags) {
|
||||
return hasTags
|
||||
? { id: id, type: 'node', tags: fakeTags }
|
||||
: { id: id, type: 'node' };
|
||||
};
|
||||
|
||||
describe('available', function () {
|
||||
beforeEach(function () {
|
||||
// a - node with tags & parent way
|
||||
// b - node with tags & 2 parent ways
|
||||
// c - node with no tags, parent way
|
||||
// d - node with no tags, 2 parent ways
|
||||
// e - node with tags, no parent way
|
||||
// f - node with no tags, no parent way
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode(createFakeNode('a', true)),
|
||||
iD.osmNode(createFakeNode('b', true)),
|
||||
iD.osmNode(createFakeNode('c', false)),
|
||||
iD.osmNode(createFakeNode('d', false)),
|
||||
iD.osmNode(createFakeNode('e', true)),
|
||||
iD.osmNode(createFakeNode('f', false)),
|
||||
iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c', 'd'] }),
|
||||
iD.osmWay({ id: 'y', nodes: ['b', 'd'] })
|
||||
]);
|
||||
});
|
||||
|
||||
it('is not available for no selected ids', function () {
|
||||
var result = iD.operationDetachNode([], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for two selected ids', function () {
|
||||
var result = iD.operationDetachNode(['a', 'b'], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for unknown selected id', function () {
|
||||
var result = iD.operationDetachNode(['z'], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected way', function () {
|
||||
var result = iD.operationDetachNode(['x'], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with tags, no parent way', function () {
|
||||
var result = iD.operationDetachNode(['e'], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with no tags, no parent way', function () {
|
||||
var result = iD.operationDetachNode(['f'], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with no tags, parent way', function () {
|
||||
var result = iD.operationDetachNode(['c'], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with no tags, two parent ways', function () {
|
||||
var result = iD.operationDetachNode(['d'], fakeContext).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is available for selected node with tags, parent way', function () {
|
||||
var result = iD.operationDetachNode(['a'], fakeContext).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for selected node with tags, two parent ways', function () {
|
||||
var result = iD.operationDetachNode(['b'], fakeContext).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('disabled', function () {
|
||||
it('returns enabled for non-related node', function () {
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode(createFakeNode('a', false)),
|
||||
iD.osmNode(createFakeNode('b', true)),
|
||||
iD.osmNode(createFakeNode('c', false)),
|
||||
iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] })
|
||||
]);
|
||||
var result = iD.operationDetachNode(['b'], fakeContext).disabled();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('returns enabled for non-restriction related node', function () {
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode(createFakeNode('a', false)),
|
||||
iD.osmNode(createFakeNode('b', true)),
|
||||
iD.osmNode(createFakeNode('c', false)),
|
||||
iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] }),
|
||||
iD.osmRelation({ id: 'r', members: [{ id: 'b', role: 'label' }] })
|
||||
]);
|
||||
var result = iD.operationDetachNode(['b'], fakeContext).disabled();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('returns not-enabled for via node in restriction', function () {
|
||||
// https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that
|
||||
// from & to roles are only appropriate for Ways
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode(createFakeNode('a', false)),
|
||||
iD.osmNode(createFakeNode('b', false)),
|
||||
iD.osmNode(createFakeNode('c', false)),
|
||||
iD.osmNode(createFakeNode('d', true)),
|
||||
iD.osmNode(createFakeNode('e', false)),
|
||||
iD.osmNode(createFakeNode('f', false)),
|
||||
iD.osmNode(createFakeNode('g', false)),
|
||||
iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] }),
|
||||
iD.osmWay({ id: 'y', nodes: ['e', 'f', 'g'] }),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction', restriction: 'no_right_turn'},
|
||||
members: [
|
||||
{ id: 'x', type: 'way', role: 'from' },
|
||||
{ id: 'd', type: 'node', role: 'via' },
|
||||
{ id: 'z', type: 'way', role: 'to' }
|
||||
]
|
||||
})
|
||||
]);
|
||||
var result = iD.operationDetachNode(['d'], fakeContext).disabled();
|
||||
expect(result).to.eql('restriction');
|
||||
});
|
||||
|
||||
it('returns not-enabled for location_hint node in restriction', function () {
|
||||
// https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that
|
||||
// from & to roles are only appropriate for Ways
|
||||
graph = iD.coreGraph([
|
||||
iD.osmNode(createFakeNode('a', false)),
|
||||
iD.osmNode(createFakeNode('b', false)),
|
||||
iD.osmNode(createFakeNode('c', false)),
|
||||
iD.osmNode(createFakeNode('d', true)),
|
||||
iD.osmNode(createFakeNode('e', false)),
|
||||
iD.osmNode(createFakeNode('f', false)),
|
||||
iD.osmNode(createFakeNode('g', false)),
|
||||
iD.osmWay({ id: 'x', nodes: ['a', 'b'] }),
|
||||
iD.osmWay({ id: 'y', nodes: ['e', 'f', 'g'] }),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction', restriction: 'no_right_turn'},
|
||||
members: [
|
||||
{ id: 'x', type: 'way', role: 'from' },
|
||||
{ id: 'c', type: 'node', role: 'via' },
|
||||
{ id: 'd', type: 'node', role: 'location_hint' },
|
||||
{ id: 'z', type: 'way', role: 'to' }
|
||||
]
|
||||
})
|
||||
]);
|
||||
var result = iD.operationDetachNode(['d'], fakeContext).disabled();
|
||||
expect(result).to.eql('restriction');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -44,28 +44,28 @@ describe('iD.rendererBackgroundSource', function() {
|
||||
});
|
||||
|
||||
it('correctly displays an overlay with no overzoom specified', function() {
|
||||
var source = iD.rendererBackgroundSource({ scaleExtent: [6,16] });
|
||||
var source = iD.rendererBackgroundSource({ zoomExtent: [6,16] });
|
||||
expect(source.validZoom(10)).to.be.true;
|
||||
expect(source.validZoom(3)).to.be.false;
|
||||
expect(source.validZoom(17)).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly displays an overlay with an invalid overzoom', function() {
|
||||
var source = iD.rendererBackgroundSource({ scaleExtent: [6,16], overzoom: 'gibberish'});
|
||||
var source = iD.rendererBackgroundSource({ zoomExtent: [6,16], overzoom: 'gibberish'});
|
||||
expect(source.validZoom(10)).to.be.true;
|
||||
expect(source.validZoom(3)).to.be.false;
|
||||
expect(source.validZoom(17)).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly displays an overlay with overzoom:true', function() {
|
||||
var source = iD.rendererBackgroundSource({ scaleExtent: [6,16], overzoom: true});
|
||||
var source = iD.rendererBackgroundSource({ zoomExtent: [6,16], overzoom: true});
|
||||
expect(source.validZoom(10)).to.be.true;
|
||||
expect(source.validZoom(3)).to.be.false;
|
||||
expect(source.validZoom(17)).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly displays an overlay with overzoom:false', function() {
|
||||
var source = iD.rendererBackgroundSource({ scaleExtent: [6,16], overzoom: false});
|
||||
var source = iD.rendererBackgroundSource({ zoomExtent: [6,16], overzoom: false});
|
||||
expect(source.validZoom(10)).to.be.true;
|
||||
expect(source.validZoom(3)).to.be.false;
|
||||
expect(source.validZoom(17)).to.be.false;
|
||||
|
||||
Reference in New Issue
Block a user