From 3bad09d497fdfec17781166b69d3b761c935623e Mon Sep 17 00:00:00 2001 From: Jon D Date: Fri, 6 Jul 2018 22:20:19 +0100 Subject: [PATCH 01/16] Merge Work-In-Progress 4320 to 4320 --- data/core.yaml | 5 + dist/locales/en.json | 6 + modules/actions/detach_node.js | 27 + modules/actions/index.js | 1 + modules/operations/detach_node.js | 50 ++ modules/operations/index.js | 1 + .../operations/operation-detachNode.svg | 61 ++ test/index.html | 14 +- test/spec/actions/detach_node.js | 540 ++++++++++++++++++ 9 files changed, 700 insertions(+), 5 deletions(-) create mode 100644 modules/actions/detach_node.js create mode 100644 modules/operations/detach_node.js create mode 100644 svg/iD-sprite/operations/operation-detachNode.svg create mode 100644 test/spec/actions/detach_node.js diff --git a/data/core.yaml b/data/core.yaml index 5b544040b..820e0fc0d 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -229,6 +229,11 @@ en: annotation: create: Added a turn restriction delete: Deleted a turn restriction + detachNode: + title: Detach + key: D + description: Detach this node from these lines/areas. + annotation: Detached a node from owning lines/areas. restriction: controls: distance: Distance diff --git a/dist/locales/en.json b/dist/locales/en.json index 3412491a4..8abaa9db5 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -297,6 +297,12 @@ "create": "Added a turn restriction", "delete": "Deleted a turn restriction" } + }, + "detachNode": { + "title": "Detach", + "key": "D", + "description": "Detach this node from these lines/areas.", + "annotation": "Detached a node from owning lines/areas." } }, "restriction": { diff --git a/modules/actions/detach_node.js b/modules/actions/detach_node.js new file mode 100644 index 000000000..d83fd46ed --- /dev/null +++ b/modules/actions/detach_node.js @@ -0,0 +1,27 @@ +import { osmNode } from '../osm'; + +export function actionDetachNode(nodeId) { + return function (graph) { + // Get the point in question + var node = graph.entity(nodeId); + // Get all of the ways it's currently attached to + var parentWays = graph.parentWays(node); + // Create a new node to replace the one we will detach + var replacementNode = osmNode({ loc: node.loc }); + // We need to process each way in turn, updating the graph as we go + return parentWays + .reduce(function (accGraph, parentWay) { + // Make a note of where in the way our target node is inside this way + var originalIndex = parentWay.nodes.indexOf(nodeId); + // Swap out the target node for the replacement + var updatedWay = parentWay + .removeNode(nodeId) // Remove our target node from the parent way + .addNode(replacementNode.id, originalIndex); // Add in the replacement node in its place + // Update the graph with the updated way + return accGraph.replace(updatedWay); + }, + // Seed the reduction with the input graph, updated to include the replacementNode so + // that is accessible to the ways when we add it in to them + graph.replace(replacementNode)); + }; +} diff --git a/modules/actions/index.js b/modules/actions/index.js index 330690db2..4f15456d8 100644 --- a/modules/actions/index.js +++ b/modules/actions/index.js @@ -33,3 +33,4 @@ export { actionSplit } from './split'; export { actionStraighten } from './straighten'; export { actionUnrestrictTurn } from './unrestrict_turn'; export { actionReflect } from './reflect.js'; +export { actionDetachNode } from './detach_node'; \ No newline at end of file diff --git a/modules/operations/detach_node.js b/modules/operations/detach_node.js new file mode 100644 index 000000000..28ca9b600 --- /dev/null +++ b/modules/operations/detach_node.js @@ -0,0 +1,50 @@ +import { actionDetachNode } from '../actions/index'; +import { behaviorOperation } from '../behavior/index'; +import { modeMove } from '../modes/index'; +import { t } from '../util/locale'; + +export function operationDetachNode(selectedIDs, context) { + var selectedNode = selectedIDs[0]; + var operation = function () { + context.perform(actionDetachNode(selectedNode)); + context.enter(modeMove(context, [selectedNode], context.graph)); + }; + var hasTags = function (entity) { + return Object.keys(entity.tags).length > 0; + }; + operation.available = function () { + // Check multiple items aren't selected + if (selectedIDs.length !== 1) { + return false; + } + // Get the entity itself + var graph = context.graph(); + var entity = graph.hasEntity(selectedNode); + if (!entity) { + // This probably isn't possible + return false; + } + // Confirm entity is a node with tags + if (entity.type === 'node' && hasTags(entity)) { + // Confirm that the node is owned by at least 1 parent way + var parentWays = graph.parentWays(entity); + return parentWays && parentWays.length > 0; + } + // Not appropriate + return false; + }; + operation.disabled = function () { + return false; + }; + operation.tooltip = function () { + return t('operations.detachNode.description'); + }; + operation.annotation = function () { + return t('operations.detachNode.annotation'); + }; + operation.id = 'detachNode'; + operation.keys = [t('operations.detachNode.key')]; + operation.title = t('operations.detachNode.title'); + operation.behavior = behaviorOperation(context).which(operation); + return operation; +} diff --git a/modules/operations/index.js b/modules/operations/index.js index 1ad24cd97..8339d8205 100644 --- a/modules/operations/index.js +++ b/modules/operations/index.js @@ -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'; diff --git a/svg/iD-sprite/operations/operation-detachNode.svg b/svg/iD-sprite/operations/operation-detachNode.svg new file mode 100644 index 000000000..fea5ba873 --- /dev/null +++ b/svg/iD-sprite/operations/operation-detachNode.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/test/index.html b/test/index.html index 188b9084c..73fd6cc59 100644 --- a/test/index.html +++ b/test/index.html @@ -1,5 +1,6 @@ + Mocha Tests @@ -7,6 +8,7 @@ +
@@ -17,9 +19,9 @@ @@ -60,6 +62,7 @@ + @@ -138,7 +141,8 @@ - + + \ No newline at end of file diff --git a/test/spec/actions/detach_node.js b/test/spec/actions/detach_node.js new file mode 100644 index 000000000..70739b640 --- /dev/null +++ b/test/spec/actions/detach_node.js @@ -0,0 +1,540 @@ +describe('iD.actionDetachNode', function () { + var tags = { 'name': 'test' }; + function createTargetNode(id, lonlat) { + return iD.Node({ id: id, loc: lonlat, tags: tags }); + } + describe('simple way', function () { + var graph; + beforeEach(function () { + // Set up a simple way + // a-b-c-d + // (0,0)-(1,0)-(2,0)-(3,0) + graph = iD.Graph([ + iD.Node({ id: 'a', loc: [0, 0] }), + iD.Node({ id: 'b', loc: [1, 0] }), + iD.Node({ id: 'c', loc: [2, 0] }), + iD.Node({ id: 'd', loc: [3, 0] }), + iD.Way({ id: 'w', 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('w'); + 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('w'); + // 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('w').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('w').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('w'); + 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('w'); + // 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('w').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('w').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 () { + // Set up a closed way + // a-b (0,0)-(1,0) + // | | + // d-c (0,1)-(1,1) + graph = iD.Graph([ + iD.Node({ id: 'a', loc: [0, 0] }), + iD.Node({ id: 'b', loc: [1, 0] }), + iD.Node({ id: 'c', loc: [1, 1] }), + iD.Node({ id: 'd', loc: [0, 1] }), + iD.Way({ id: 'w', 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('w'); + 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('w'); + // 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('w').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('w').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('w'); + 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('w'); + // 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('w').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('w').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 simple ways', function () { + var graph; + beforeEach(function () { + // Set up two simple ways + // a-b-c-d (0,0)-(1,0)-(2,0)-(3,0) + // e (2,1) + // f (2,2) + // Node c represents the target + graph = iD.Graph([ + iD.Node({ id: 'a', loc: [0, 0] }), + iD.Node({ id: 'b', loc: [1, 0] }), + iD.Node({ id: 'c', loc: [2, 0], tags: tags }), + iD.Node({ id: 'd', loc: [3, 0] }), + iD.Node({ id: 'e', loc: [2, 1] }), + iD.Node({ id: 'f', loc: [2, 2] }), + iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd'] }), + iD.Way({ id: 'x', 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('w'); + expect(target.nodes.length).to.eql(4); + // .. and second way has 3 + target = assertionGraph.entity('x'); + 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('w'); + // 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('x'); + 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('w').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('x').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('w').nodes[2]).to.eql(assertionGraph.entity('x').nodes[0]); + }); + + it('does replace target node', function () { + // Act + var assertionGraph = iD.actionDetachNode('c')(graph); + + var nodes = assertionGraph.entity('w').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('x').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 () { + // Set up two intersecting closed ways + // a-b (0,0)-(1,0) + // | | + // d-c-e (0,1)-(1,1)-(2,1) + // | | + // g f (0,2) - (1,2) + // C is the target node + graph = iD.Graph([ + iD.Node({ id: 'a', loc: [0, 0] }), + iD.Node({ id: 'b', loc: [1, 0] }), + iD.Node({ id: 'c', loc: [1, 1], tags: tags }), + iD.Node({ id: 'd', loc: [0, 1] }), + iD.Node({ id: 'e', loc: [2, 1] }), + iD.Node({ id: 'f', loc: [1, 2] }), + iD.Node({ id: 'g', loc: [0, 2] }), + iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd', 'a'] }), + iD.Way({ id: 'x', 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('w'); + expect(target.nodes.length).to.eql(5); + // and the second + target = assertionGraph.entity('x'); + 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('w'); + // 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('x'); + 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('w').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('x').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([1, 2]); + expect(assertionGraph.entity(nodes[3]).loc).to.eql([0, 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('w').nodes[2]).to.eql(assertionGraph.entity('x').nodes[0]); + }); + + it('does replace target node', function () { + // Act + var assertionGraph = iD.actionDetachNode('c')(graph); + + var nodes = assertionGraph.entity('w').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([]); + }); + }); +}); \ No newline at end of file From 3b6b4bb7be3fd1d02d8ca881733e7e6cc13ab816 Mon Sep 17 00:00:00 2001 From: Jon D Date: Sat, 7 Jul 2018 10:37:58 +0100 Subject: [PATCH 02/16] Clean up SVG --- .../operations/operation-detachNode.svg | 66 ++----------------- 1 file changed, 7 insertions(+), 59 deletions(-) diff --git a/svg/iD-sprite/operations/operation-detachNode.svg b/svg/iD-sprite/operations/operation-detachNode.svg index fea5ba873..504acc2cb 100644 --- a/svg/iD-sprite/operations/operation-detachNode.svg +++ b/svg/iD-sprite/operations/operation-detachNode.svg @@ -1,61 +1,9 @@ - + + - - - - image/svg+xml - - - - - - - - - + xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" width="20" height="20" viewBox="0 0 20 20" id="svg838"> + + + + From 3fe6f16526a4421db684b398b2efc63341f1d648 Mon Sep 17 00:00:00 2001 From: Jon D Date: Sat, 7 Jul 2018 10:50:44 +0100 Subject: [PATCH 03/16] Fix keyboard shortcut clash --- data/core.yaml | 2 +- dist/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 820e0fc0d..20d827514 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -231,7 +231,7 @@ en: delete: Deleted a turn restriction detachNode: title: Detach - key: D + key: T description: Detach this node from these lines/areas. annotation: Detached a node from owning lines/areas. restriction: diff --git a/dist/locales/en.json b/dist/locales/en.json index 8abaa9db5..15dbd00c9 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -300,7 +300,7 @@ }, "detachNode": { "title": "Detach", - "key": "D", + "key": "T", "description": "Detach this node from these lines/areas.", "annotation": "Detached a node from owning lines/areas." } From dd3de593ca85653ab82d0f3162df75320f34e887 Mon Sep 17 00:00:00 2001 From: Jon D Date: Sat, 7 Jul 2018 11:12:41 +0100 Subject: [PATCH 04/16] Add tests for Detach Node operation --- test/index.html | 1 + test/spec/operations/detach_node.js | 85 +++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 test/spec/operations/detach_node.js diff --git a/test/index.html b/test/index.html index 73fd6cc59..f0a1f7189 100644 --- a/test/index.html +++ b/test/index.html @@ -140,6 +140,7 @@ + diff --git a/test/spec/operations/detach_node.js b/test/spec/operations/detach_node.js new file mode 100644 index 000000000..6604ce051 --- /dev/null +++ b/test/spec/operations/detach_node.js @@ -0,0 +1,85 @@ +describe('iD.operationDetachNode', function () { + var fakeContext; + var graph; + var fakeTags = { 'name': 'fake' }; + beforeEach(function () { + // Set up graph + var createFakeNode = function (id, hasTags) { + return hasTags + ? { id: id, type: 'node', tags: fakeTags } + : { id: id, type: 'node' }; + }; + // 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.Graph([ + iD.Node(createFakeNode('a', true)), + iD.Node(createFakeNode('b', true)), + iD.Node(createFakeNode('c', false)), + iD.Node(createFakeNode('d', false)), + iD.Node(createFakeNode('e', true)), + iD.Node(createFakeNode('f', false)), + iD.Way({ id: 'x', nodes: ['a', 'b', 'c', 'd'] }), + iD.Way({ id: 'y', nodes: ['b', 'd'] }) + ]); + + // Set up the fake context + fakeContext = {}; + fakeContext.graph = function () { + return graph; + }; + }); + + it('is not available for no selected ids', function () { + var result = iD.operationDetachNode([], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for two selected ids', function () { + var result = iD.operationDetachNode(['a', 'b'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for unkown selected id', function () { + var result = iD.operationDetachNode(['z'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected way', function () { + var result = iD.operationDetachNode(['x'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with tags, no parent way', function () { + var result = iD.operationDetachNode(['e'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with no tags, no parent way', function () { + var result = iD.operationDetachNode(['f'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with no tags, parent way', function () { + var result = iD.operationDetachNode(['c'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with no tags, two parent ways', function () { + var result = iD.operationDetachNode(['d'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is available for selected node with tags, parent way', function () { + var result = iD.operationDetachNode(['a'], fakeContext).available(); + expect(result).to.eql(true); + }); + + it('is available for selected node with tags, two parent ways', function () { + var result = iD.operationDetachNode(['b'], fakeContext).available(); + expect(result).to.eql(true); + }); +}); From aac394a48723fb14356f6e24aebe5ea1b807ef69 Mon Sep 17 00:00:00 2001 From: Matias Volpe Date: Wed, 18 Jul 2018 14:19:07 -0300 Subject: [PATCH 05/16] Update directional indicator based off of current bearing in Mapillary JS --- modules/services/mapillary.js | 7 ++++++- modules/svg/mapillary_images.js | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index e264d5cca..c1c2dd24a 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -29,7 +29,7 @@ var viewerjs = 'mapillary-js/mapillary.min.js'; var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; var maxResults = 1000; var tileZoom = 14; -var dispatch = d3_dispatch('loadedImages', 'loadedSigns'); +var dispatch = d3_dispatch('loadedImages', 'loadedSigns', 'bearingChanged'); var _mlyFallback = false; var _mlyCache; var _mlyClicks; @@ -511,6 +511,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 } @@ -545,6 +546,10 @@ export default { that.selectImage(undefined, node.key, true); } } + + function bearingChanged(e) { + dispatch.call('bearingChanged', undefined, e); + } }, diff --git a/modules/svg/mapillary_images.js b/modules/svg/mapillary_images.js index fb8a4b4b8..449b768f8 100644 --- a/modules/svg/mapillary_images.js +++ b/modules/svg/mapillary_images.js @@ -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; From c000da07afe062fe46da179a8a18bda71bc319b9 Mon Sep 17 00:00:00 2001 From: Matias Volpe Date: Thu, 19 Jul 2018 09:46:25 -0300 Subject: [PATCH 06/16] Leave directional indicator only for current image --- css/60_photos.css | 7 +++++++ modules/svg/mapillary_images.js | 1 + 2 files changed, 8 insertions(+) diff --git a/css/60_photos.css b/css/60_photos.css index cc853900c..c1e4a0d9e 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -177,6 +177,13 @@ .layer-mapillary-images .viewfield-group * { fill: #55ff22; } +.layer-mapillary-images .viewfield-group .viewfield { + display: none; +} +.layer-mapillary-images .viewfield-group.selected .viewfield, +.layer-mapillary-images .viewfield-group .viewfield.pano { + display: inline; +} .layer-mapillary-images .sequence { stroke: #55ff22; } diff --git a/modules/svg/mapillary_images.js b/modules/svg/mapillary_images.js index 449b768f8..641882790 100644 --- a/modules/svg/mapillary_images.js +++ b/modules/svg/mapillary_images.js @@ -201,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); From cd3d57627643da1cb28c3dd20f5f00f91b1f905d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 21 Jul 2018 21:27:44 -0400 Subject: [PATCH 07/16] rename `geoTile` -> `tiler` --- modules/services/mapillary.js | 4 ++-- modules/services/openstreetcam.js | 4 ++-- modules/services/osm.js | 4 ++-- modules/services/streetside.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index 3338211ab..0f664ffe4 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -22,7 +22,7 @@ import { svgDefs } from '../svg'; import { utilDetect } from '../util/detect'; import { utilQsString, utilRebind, utilTiler } from '../util'; -var geoTile = utilTiler().skipNullIsland(true); +var tiler = utilTiler().skipNullIsland(true); var apibase = 'https://a.mapillary.com/v3/'; var viewercss = 'mapillary-js/mapillary.min.css'; @@ -58,7 +58,7 @@ function loadTiles(which, url, projection) { 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 tiles = tiler.getTiles(projection, dimension, tileZoom); // abort inflight requests that are no longer needed var cache = _mlyCache[which]; diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index f7577b51d..6094be376 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -32,7 +32,7 @@ import { utilSetTransform } from '../util'; -var geoTile = utilTiler().skipNullIsland(true); +var tiler = utilTiler().skipNullIsland(true); var apibase = 'https://openstreetcam.org'; var maxResults = 1000; @@ -67,7 +67,7 @@ function loadTiles(which, url, projection) { 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 tiles = tiler.getTiles(projection, dimension, tileZoom); // abort inflight requests that are no longer needed var cache = _oscCache[which]; diff --git a/modules/services/osm.js b/modules/services/osm.js index 2aecabb6e..d5a812b80 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -33,7 +33,7 @@ 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'; @@ -778,7 +778,7 @@ export default { } // determine the needed tiles to cover the view - var tiles = geoTile.getTiles(projection, dimensions, tilezoom); + var tiles = tiler.getTiles(projection, dimensions, tilezoom); // abort inflight requests that are no longer needed var hadRequests = !_isEmpty(cache.inflight); diff --git a/modules/services/streetside.js b/modules/services/streetside.js index b7b592cba..f028e11b3 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -33,7 +33,7 @@ import { utilQsString, utilRebind, utilTiler } from '../util'; import Q from 'q'; -var geoTile = utilTiler().skipNullIsland(true); +var tiler = utilTiler().skipNullIsland(true); var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?'; var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/'; @@ -84,7 +84,7 @@ function loadTiles(which, url, projection, margin) { var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); var dimension = projection.clipExtent()[1]; - var tiles = geoTile + var tiles = tiler .margin(margin) .getTiles(projection, dimension, tileZoom); From c0b77d8226b2b5048d16817bf37cc804bca5b5dd Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 21 Jul 2018 21:44:14 -0400 Subject: [PATCH 08/16] remove unneeded dimensions argument (projection clipExtent has it) --- modules/core/context.js | 18 +++++++++--------- modules/renderer/map.js | 2 +- modules/services/mapillary.js | 7 ++----- modules/services/openstreetcam.js | 20 +++++++++----------- modules/services/osm.js | 10 +++++----- modules/services/streetside.js | 9 +++------ modules/util/tiler.js | 3 ++- 7 files changed, 31 insertions(+), 38 deletions(-) diff --git a/modules/core/context.js b/modules/core/context.js index 4d758066e..31dbe7deb 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -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() { diff --git a/modules/renderer/map.js b/modules/renderer/map.js index 1118488b7..097a17665 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -483,7 +483,7 @@ export function rendererMap(context) { // OSM if (map.editable()) { - context.loadTiles(projection, dimensions); + context.loadTiles(projection); drawVector(difference, extent); } else { editOff(); diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index 0f664ffe4..866a9778e 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -19,10 +19,8 @@ import rbush from 'rbush'; import { geoExtent } from '../geo'; import { svgDefs } from '../svg'; -import { utilDetect } from '../util/detect'; import { utilQsString, utilRebind, utilTiler } from '../util'; -var tiler = utilTiler().skipNullIsland(true); var apibase = 'https://a.mapillary.com/v3/'; var viewercss = 'mapillary-js/mapillary.min.css'; @@ -30,6 +28,7 @@ var viewerjs = 'mapillary-js/mapillary.min.js'; var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; var maxResults = 1000; var tileZoom = 14; +var tiler = utilTiler().skipNullIsland(true); var dispatch = d3_dispatch('loadedImages', 'loadedSigns'); var _mlyFallback = false; var _mlyCache; @@ -56,9 +55,7 @@ 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 = tiler.getTiles(projection, dimension, tileZoom); + var tiles = tiler.getTiles(projection, tileZoom); // abort inflight requests that are no longer needed var cache = _mlyCache[which]; diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index 6094be376..8fa1574e7 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -32,11 +32,11 @@ import { utilSetTransform } from '../util'; -var tiler = utilTiler().skipNullIsland(true); var apibase = 'https://openstreetcam.org'; var maxResults = 1000; var tileZoom = 14; +var tiler = utilTiler().skipNullIsland(true); var dispatch = d3_dispatch('loadedImages'); var imgZoom = d3_zoom() .extent([[0, 0], [320, 240]]) @@ -65,9 +65,7 @@ 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 = tiler.getTiles(projection, dimension, tileZoom); + var tiles = tiler.getTiles(projection, tileZoom); // abort inflight requests that are no longer needed var cache = _oscCache[which]; @@ -123,8 +121,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 +170,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))); }); diff --git a/modules/services/osm.js b/modules/services/osm.js index d5a812b80..fe02d626d 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -33,8 +33,8 @@ import { utilQsString } from '../util'; -var tiler = utilTiler(); +var tiler = utilTiler(); var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes'); var urlroot = 'https://www.openstreetmap.org'; var oauth = osmAuth({ @@ -752,7 +752,7 @@ export default { // Load data (entities or notes) 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, noteOptions) { if (_off) return; var that = this; @@ -778,7 +778,7 @@ export default { } // determine the needed tiles to cover the view - var tiles = tiler.getTiles(projection, dimensions, tilezoom); + var tiles = tiler.getTiles(projection, tilezoom); // abort inflight requests that are no longer needed var hadRequests = !_isEmpty(cache.inflight); @@ -831,9 +831,9 @@ export default { // Load notes from the API (just calls this.loadTiles) // GET /api/0.6/notes?bbox= - loadNotes: function(projection, dimensions, noteOptions) { + loadNotes: function(projection, noteOptions) { noteOptions = _extend({ limit: 10000, closed: 7}, noteOptions); - this.loadTiles(projection, dimensions, null, noteOptions); + this.loadTiles(projection, null, noteOptions); }, diff --git a/modules/services/streetside.js b/modules/services/streetside.js index f028e11b3..ea9971f2d 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -33,7 +33,6 @@ import { utilQsString, utilRebind, utilTiler } from '../util'; import Q from 'q'; -var tiler = 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().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; @@ -82,11 +83,7 @@ function localeTimestamp(s) { 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 = tiler - .margin(margin) - .getTiles(projection, dimension, tileZoom); + var tiles = tiler.margin(margin).getTiles(projection, tileZoom); // abort inflight requests that are no longer needed var cache = _ssCache[which]; diff --git a/modules/util/tiler.js b/modules/util/tiler.js index d66e29933..a869a3472 100644 --- a/modules/util/tiler.js +++ b/modules/util/tiler.js @@ -76,7 +76,8 @@ export function utilTiler() { * 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. */ - tiler.getTiles = function(projection, dimensions, tilezoom) { + tiler.getTiles = function(projection, tilezoom) { + var dimensions = projection.clipExtent()[1]; 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); From 7be9439c35b4ea5feeb39c33358a629e0f3f1abd Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 21 Jul 2018 21:59:35 -0400 Subject: [PATCH 09/16] Use geoScaleToZoom to calculate zoom in Mapillary, OpenStreetCam --- modules/services/mapillary.js | 5 ++--- modules/services/openstreetcam.js | 10 ++++------ modules/services/streetside.js | 8 +++----- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index 866a9778e..ddf72eabc 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -17,7 +17,7 @@ import { import rbush from 'rbush'; -import { geoExtent } from '../geo'; +import { geoExtent, geoScaleToZoom } from '../geo'; import { svgDefs } from '../svg'; import { utilQsString, utilRebind, utilTiler } from '../util'; @@ -53,8 +53,7 @@ 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 currZoom = Math.floor(geoScaleToZoom(projection.scale())); var tiles = tiler.getTiles(projection, tileZoom); // abort inflight requests that are no longer needed diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index 8fa1574e7..4c0c4cd28 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -21,15 +21,14 @@ 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'; @@ -63,8 +62,7 @@ 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 currZoom = Math.floor(geoScaleToZoom(projection.scale())); var tiles = tiler.getTiles(projection, tileZoom); // abort inflight requests that are no longer needed diff --git a/modules/services/streetside.js b/modules/services/streetside.js index ea9971f2d..39d5f7990 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -81,8 +81,6 @@ 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 tiles = tiler.margin(margin).getTiles(projection, tileZoom); // abort inflight requests that are no longer needed @@ -97,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); @@ -118,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]; From 88119330099c3447ca2cb8e7bd2312631669aa6d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 21 Jul 2018 22:31:04 -0400 Subject: [PATCH 10/16] Split up loadTiles and loadNotes code Code is similar but different enough that I'd rather have 2 separate functions rather than a single function with a bunch of ifs --- modules/services/osm.js | 118 +++++++++++++++++++++----------------- modules/ui/note_editor.js | 4 +- 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/modules/services/osm.js b/modules/services/osm.js index fe02d626d..4a3c0bc5c 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -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; @@ -749,78 +760,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, 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 = tiler.getTiles(projection, tilezoom); + var tiles = tiler.getTiles(projection, _tileZoom); // 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 @@ -829,11 +806,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, noteOptions) { noteOptions = _extend({ limit: 10000, closed: 7}, noteOptions); - this.loadTiles(projection, null, 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.getTiles(projection, _noteZoom); + + // 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 + ); + }); }, diff --git a/modules/ui/note_editor.js b/modules/ui/note_editor.js index 1d96d80f1..901419b72 100644 --- a/modules/ui/note_editor.js +++ b/modules/ui/note_editor.js @@ -59,7 +59,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) @@ -71,7 +71,7 @@ export function uiNoteEditor(context) { var footer = selection.selectAll('.footer') .data([0]); - footer = footer.enter() + footer.enter() .append('div') .attr('class', 'footer') .merge(footer) From 2fa593421f20046ac998225eb017540305458be1 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 22 Jul 2018 01:15:44 -0400 Subject: [PATCH 11/16] Rename scaleExtent to zoomExtent, slightly simplify getTiles() --- API.md | 2 +- data/imagery.json | 1642 ++++++++++------------- data/update_imagery.js | 2 +- modules/renderer/background_source.js | 10 +- modules/renderer/tile_layer.js | 2 +- modules/services/mapillary.js | 4 +- modules/services/openstreetcam.js | 4 +- modules/services/osm.js | 6 +- modules/services/streetside.js | 4 +- modules/util/tiler.js | 48 +- test/spec/renderer/background_source.js | 8 +- 11 files changed, 739 insertions(+), 993 deletions(-) diff --git a/API.md b/API.md index 83a68bc8a..b624db79f 100644 --- a/API.md +++ b/API.md @@ -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 diff --git a/data/imagery.json b/data/imagery.json index 892539d24..8716aaeca 100644 --- a/data/imagery.json +++ b/data/imagery.json @@ -7,7 +7,7 @@ "template": "https://sjcgis.org/arcgis/rest/services/Basemaps/Aerials_2013_WM/MapServer/tile/{zoom}/{y}/{x}", "endDate": "2013-06-01T00:00:00.000Z", "startDate": "2013-05-01T00:00:00.000Z", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-123.02167, 48.44667], @@ -60,7 +60,7 @@ "template": "https://sjcgis.org/arcgis/rest/services/Basemaps/Aerials_2016_WM/MapServer/tile/{zoom}/{y}/{x}", "endDate": "2016-07-01T00:00:00.000Z", "startDate": "2016-05-01T00:00:00.000Z", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-123.02167, 48.44667], @@ -110,7 +110,7 @@ "name": "7th Series (OS7)", "type": "tms", "template": "https://ooc.openstreetmap.org/os7/{zoom}/{x}/{y}.jpg", - "scaleExtent": [6, 14], + "zoomExtent": [6, 14], "polygon": [ [ [-3.04697, 54.83947], @@ -412,7 +412,7 @@ "projection": "EPSG:4326", "endDate": "2017-05-01T00:00:00.000Z", "startDate": "2017-05-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [149.085, -35.1171], @@ -481,6 +481,88 @@ ] ], "terms_url": "https://actmapi-actgov.opendata.arcgis.com/datasets/884456bde6fd46d68e0c05479f55d548", + "terms_text": "© Jacobs Group (Australia) Pty Ltd and Australian Capital Territory" + }, + { + "id": "ACT2018", + "name": "ACTmapi Imagery 2018", + "type": "wms", + "template": "https://data.actmapi.act.gov.au/arcgis/services/actmapi/imagery2018mga/ImageServer/WMSServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=0&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", + "projection": "EPSG:4326", + "endDate": "2018-03-19T00:00:00.000Z", + "startDate": "2018-03-19T00:00:00.000Z", + "zoomExtent": [0, 21], + "polygon": [ + [ + [149.2695, -35.4381], + [149.2669, -35.348], + [149.3329, -35.3468], + [149.3334, -35.3648], + [149.3774, -35.364], + [149.3769, -35.3459], + [149.3989, -35.3455], + [149.3984, -35.3275], + [149.4094, -35.3273], + [149.4088, -35.3092], + [149.3978, -35.3095], + [149.3973, -35.2914], + [149.3533, -35.2923], + [149.3528, -35.2743], + [149.3089, -35.2751], + [149.3084, -35.2571], + [149.2644, -35.2579], + [149.2634, -35.2219], + [149.2415, -35.2223], + [149.241, -35.2043], + [149.219, -35.2047], + [149.218, -35.1687], + [149.1961, -35.1691], + [149.1956, -35.151], + [149.1737, -35.1514], + [149.1732, -35.1334], + [149.1512, -35.1338], + [149.1508, -35.1158], + [149.085, -35.1169], + [149.0854, -35.135], + [149.0635, -35.1353], + [149.0639, -35.1534], + [149.0201, -35.1541], + [149.0205, -35.1721], + [148.9985, -35.1725], + [148.999, -35.1905], + [148.9331, -35.1916], + [148.934, -35.2276], + [148.912, -35.228], + [148.9124, -35.246], + [148.8685, -35.2467], + [148.8689, -35.2647], + [148.8469, -35.265], + [148.8473, -35.2831], + [148.8034, -35.2837], + [148.8038, -35.3018], + [148.7818, -35.3021], + [148.7838, -35.3922], + [148.8058, -35.3919], + [148.8086, -35.5181], + [148.7976, -35.5182], + [148.7994, -35.5993], + [148.8766, -35.5982], + [148.8747, -35.517], + [148.8527, -35.5174], + [148.8508, -35.4363], + [148.8398, -35.4364], + [148.8388, -35.3914], + [149.0039, -35.3888], + [149.0048, -35.4248], + [149.0268, -35.4244], + [149.0277, -35.4605], + [149.0497, -35.4601], + [149.0511, -35.5142], + [149.1613, -35.5122], + [149.1594, -35.4402], + [149.2695, -35.4381] + ] + ], "terms_text": "© Jacobs Group (Australia) Pty Ltd and Australian Capital Territory", "best": true }, @@ -605,7 +687,7 @@ "name": "AGIV Flanders GRB", "type": "tms", "template": "https://tile.informatievlaanderen.be/ws/raadpleegdiensten/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=grb_bsk&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={zoom}&tileRow={y}&tileCol={x}", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [4.40434, 50.78177], @@ -735,7 +817,7 @@ "name": "AGIV Flanders most recent aerial imagery", "type": "tms", "template": "https://tile.informatievlaanderen.be/ws/raadpleegdiensten/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=omwrgbmrvl&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={zoom}&tileRow={y}&tileCol={x}", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [4.7737, 50.79368], @@ -904,7 +986,7 @@ "template": "https://{switch:a,b,c}.agri.openstreetmap.org/layer/au_ga_agri/{zoom}/{x}/{y}.png", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2006-01-01T00:00:00.000Z", - "scaleExtent": [0, 16], + "zoomExtent": [0, 16], "polygon": [ [ [112.28778, -28.78459], @@ -1554,7 +1636,7 @@ "template": "https://tiles.craig.fr/osm/wmts/1.0.0/ortho_2013/webmercator/{zoom}/{x}/{y}.jpeg", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [2.94012, 44.63388], @@ -2053,7 +2135,7 @@ "template": "https://tiles.craig.fr/ortho/wmts/1.0.0/ortho_2016/webmercator/{zoom}/{x}/{y}.jpeg", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [2.49389, 46.66829], @@ -2098,7 +2180,7 @@ "name": "BANO", "type": "tms", "template": "https://{switch:a,b,c}.layers.openstreetmap.fr/bano/{zoom}/{x}/{y}.png", - "scaleExtent": [12, 20], + "zoomExtent": [12, 20], "polygon": [ [ [-2.7, 43.9], @@ -2679,7 +2761,7 @@ "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/basemap/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.png", "endDate": "2010-07-20T00:00:00.000Z", "startDate": "2013-07-19T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -2919,7 +3001,7 @@ "name": "basemap.at", "type": "tms", "template": "https://maps{switch:1,2,3,4}.wien.gv.at/basemap/geolandbasemap/normal/google3857/{zoom}/{y}/{x}.png", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [16.50733, 46.99293], @@ -3125,7 +3207,7 @@ "name": "basemap.at Beschriftungen (annotations)", "type": "tms", "template": "https://maps{switch:1,2,3,4}.wien.gv.at/basemap/bmapoverlay/normal/google3857/{zoom}/{y}/{x}.png", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [16.50733, 46.99293], @@ -3332,7 +3414,7 @@ "name": "basemap.at Orthofoto", "type": "tms", "template": "https://maps{switch:1,2,3,4}.wien.gv.at/basemap/bmaporthofoto30cm/normal/google3857/{zoom}/{y}/{x}.jpeg", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [16.50733, 46.99293], @@ -3534,326 +3616,13 @@ "description": "Orthofoto layer provided by basemap.at. \"Successor\" of geoimage.at imagery.", "icon": "https://www.basemap.at/images/logo_basemap.jpg" }, - { - "id": "bavaria-2m", - "name": "Bavaria (2 m)", - "type": "wms", - "template": "https://geodaten.bayern.de/ogc/ogc_dop200_oa.cgi?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=adv_dop200c&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", - "projection": "EPSG:4326", - "scaleExtent": [8, 18], - "polygon": [ - [ - [10.12359, 50.56846], - [10.14286, 50.55078], - [10.20281, 50.55742], - [10.25205, 50.51796], - [10.32698, 50.49345], - [10.41048, 50.41848], - [10.60317, 50.33109], - [10.62244, 50.2271], - [10.72521, 50.21066], - [10.72949, 50.24765], - [10.85153, 50.24354], - [10.71879, 50.32015], - [10.71236, 50.36524], - [10.85581, 50.39664], - [10.93717, 50.39664], - [10.99069, 50.36661], - [11.12772, 50.36661], - [11.1791, 50.31332], - [11.16197, 50.29417], - [11.24119, 50.2928], - [11.24975, 50.37344], - [11.24119, 50.47982], - [11.35895, 50.5234], - [11.43816, 50.50979], - [11.44244, 50.48936], - [11.42532, 50.4771], - [11.42532, 50.44166], - [11.48955, 50.42257], - [11.49169, 50.39801], - [11.51952, 50.39801], - [11.52594, 50.37617], - [11.59874, 50.40347], - [11.63727, 50.38845], - [11.79357, 50.4212], - [11.83639, 50.39255], - [11.92203, 50.42802], - [11.98626, 50.38709], - [11.98412, 50.35705], - [12.04835, 50.33109], - [12.09331, 50.32972], - [12.1297, 50.29828], - [12.14041, 50.27228], - [12.10615, 50.25586], - [12.11258, 50.23532], - [12.14897, 50.23669], - [12.19822, 50.20107], - [12.22391, 50.16406], - [12.20464, 50.14348], - [12.20678, 50.10779], - [12.24318, 50.09955], - [12.27743, 50.07208], - [12.49367, 49.98543], - [12.49796, 49.94136], - [12.55576, 49.92206], - [12.54934, 49.86827], - [12.48083, 49.78817], - [12.41018, 49.75775], - [12.46156, 49.70655], - [12.5472, 49.68023], - [12.58788, 49.55261], - [12.65425, 49.53455], - [12.66281, 49.43302], - [12.75274, 49.41073], - [12.7977, 49.34661], - [12.90475, 49.35638], - [12.99681, 49.33685], - [13.05462, 49.27543], - [13.13169, 49.21952], - [13.19164, 49.14395], - [13.2366, 49.12153], - [13.29655, 49.12293], - [13.37148, 49.08088], - [13.4143, 49.02897], - [13.4143, 48.97981], - [13.57916, 48.96997], - [13.63483, 48.94326], - [13.67765, 48.88698], - [13.73759, 48.89261], - [13.7847, 48.83346], - [13.84036, 48.77423], - [13.81681, 48.70646], - [13.84464, 48.70081], - [13.8425, 48.60038], - [13.76543, 48.5423], - [13.75258, 48.50401], - [13.67122, 48.50543], - [13.64339, 48.54371], - [13.45712, 48.5508], - [13.45712, 48.41598], - [13.40574, 48.36053], - [13.2837, 48.27511], - [13.09315, 48.26941], - [12.95827, 48.19097], - [12.87691, 48.18526], - [12.772, 48.09382], - [12.86407, 48.01368], - [12.89832, 47.95492], - [12.94543, 47.95636], - [12.99681, 47.88461], - [13.01394, 47.83434], - [12.93472, 47.7322], - [13.0589, 47.72499], - [13.11885, 47.63851], - [13.06532, 47.56922], - [13.05676, 47.47379], - [13.00323, 47.45208], - [12.76772, 47.55044], - [12.76986, 47.63274], - [12.73989, 47.67312], - [12.6671, 47.67024], - [12.57503, 47.6212], - [12.48083, 47.61975], - [12.41446, 47.67024], - [12.24318, 47.67745], - [12.2132, 47.69186], - [12.19179, 47.68177], - [12.2132, 47.66591], - [12.21106, 47.60388], - [12.17467, 47.59521], - [12.13827, 47.60388], - [11.89205, 47.60388], - [11.85137, 47.57933], - [11.63942, 47.58222], - [11.59445, 47.54899], - [11.59017, 47.51285], - [11.51738, 47.49839], - [11.4403, 47.50417], - [11.39534, 47.47524], - [11.42746, 47.44484], - [11.3461, 47.44339], - [11.27973, 47.39559], - [11.21336, 47.38834], - [11.24761, 47.43181], - [11.10203, 47.39269], - [10.965, 47.38979], - [10.97785, 47.43615], - [10.9179, 47.47524], - [10.8708, 47.47524], - [10.85581, 47.49405], - [10.90077, 47.5143], - [10.87294, 47.53598], - [10.81085, 47.51285], - [10.64385, 47.54899], - [10.59461, 47.55477], - [10.57962, 47.52876], - [10.46187, 47.54032], - [10.46615, 47.48392], - [10.48756, 47.47813], - [10.48756, 47.41298], - [10.45973, 47.40283], - [10.45973, 47.37529], - [10.41048, 47.37384], - [10.40834, 47.34339], - [10.32056, 47.28678], - [10.28202, 47.27806], - [10.28416, 47.26208], - [10.14714, 47.26208], - [10.1921, 47.30275], - [10.19424, 47.37384], - [10.16641, 47.37384], - [10.16641, 47.34629], - [10.10004, 47.34339], - [10.0615, 47.36369], - [10.06792, 47.41877], - [10.09361, 47.42601], - [10.09576, 47.44194], - [9.978, 47.48537], - [9.95659, 47.52731], - [9.8945, 47.52876], - [9.85596, 47.50851], - [9.81743, 47.54465], - [9.82171, 47.57644], - [9.77461, 47.58222], - [9.73821, 47.52586], - [9.67398, 47.53454], - [9.58406, 47.56488], - [9.63972, 47.60532], - [9.7168, 47.60388], - [9.85596, 47.676], - [9.978, 47.65582], - [10.02938, 47.68177], - [10.10004, 47.66735], - [10.13215, 47.676], - [10.14286, 47.70195], - [10.0615, 47.7725], - [10.11288, 47.8099], - [10.08291, 47.85302], - [10.1086, 47.90902], - [10.07649, 47.96496], - [10.13001, 48.02084], - [10.13429, 48.10669], - [10.10004, 48.12813], - [10.05508, 48.26228], - [9.96944, 48.36765], - [10.03153, 48.42593], - [10.02938, 48.46144], - [10.12359, 48.47705], - [10.15356, 48.4515], - [10.23492, 48.51252], - [10.31628, 48.51678], - [10.29915, 48.61878], - [10.24563, 48.6683], - [10.27346, 48.70646], - [10.3698, 48.68385], - [10.43189, 48.69939], - [10.45116, 48.72765], - [10.40192, 48.746], - [10.44046, 48.84896], - [10.43403, 48.95873], - [10.33769, 49.02055], - [10.24991, 49.03599], - [10.24991, 49.07387], - [10.20066, 49.10331], - [10.25205, 49.13274], - [10.12359, 49.19714], - [10.11931, 49.26285], - [10.15142, 49.28939], - [10.10432, 49.34522], - [10.14072, 49.39401], - [10.1086, 49.44555], - [10.11074, 49.50537], - [10.0722, 49.53316], - [10.01654, 49.47616], - [9.92662, 49.47894], - [9.92448, 49.55678], - [9.89878, 49.58177], - [9.85596, 49.53872], - [9.80672, 49.55678], - [9.86667, 49.60675], - [9.85382, 49.6442], - [9.81743, 49.66083], - [9.83455, 49.68993], - [9.79602, 49.72039], - [9.75748, 49.70793], - [9.74035, 49.68577], - [9.70609, 49.71624], - [9.67826, 49.71624], - [9.68254, 49.68854], - [9.62045, 49.69131], - [9.64615, 49.78955], - [9.55836, 49.77434], - [9.57121, 49.73561], - [9.50698, 49.75222], - [9.49199, 49.77987], - [9.46844, 49.76051], - [9.42562, 49.77849], - [9.40421, 49.76466], - [9.33356, 49.7702], - [9.32928, 49.73423], - [9.4085, 49.72593], - [9.42776, 49.69824], - [9.41492, 49.6442], - [9.38066, 49.63865], - [9.35925, 49.64974], - [9.33998, 49.63727], - [9.31215, 49.64836], - [9.27789, 49.62617], - [9.28432, 49.60814], - [9.2415, 49.57483], - [9.09805, 49.57205], - [9.06594, 49.60814], - [9.10019, 49.65113], - [9.09163, 49.6927], - [9.13017, 49.71208], - [9.13873, 49.74253], - [9.10876, 49.75637], - [9.13659, 49.79093], - [9.10019, 49.78955], - [9.07236, 49.82824], - [9.03596, 49.83514], - [9.01669, 50.02671], - [8.96317, 50.03084], - [8.95674, 50.05971], - [9.00171, 50.0707], - [9.02097, 50.11054], - [9.1216, 50.12289], - [9.15586, 50.11328], - [9.19654, 50.11878], - [9.18583, 50.13525], - [9.23507, 50.1476], - [9.37638, 50.12701], - [9.4085, 50.0817], - [9.52197, 50.09543], - [9.50484, 50.14211], - [9.53267, 50.16406], - [9.48985, 50.16954], - [9.49413, 50.24354], - [9.61403, 50.22163], - [9.66541, 50.23532], - [9.63544, 50.24901], - [9.66756, 50.27228], - [9.74249, 50.30922], - [9.72964, 50.35841], - [9.77032, 50.42939], - [9.86881, 50.40074], - [9.91805, 50.40893], - [10.03581, 50.47982], - [10.03795, 50.51115], - [10.12359, 50.56846] - ] - ], - "terms_url": "https://www.ldbv.bayern.de/", - "terms_text": "Bayerische Vermessungsverwaltung" - }, { "id": "bavaria-80cm", "name": "Bavaria (80 cm)", "type": "wms", - "template": "https://www.geodaten.bayern.de/ogc/ogc_dop80_oa.cgi?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=by_dop80c&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", + "template": "https://geoservices.bayern.de/wms/v1/ogc_dop80_oa.cgi?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=by_dop80c&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [7, 18], + "zoomExtent": [7, 18], "polygon": [ [ [10.12359, 50.56846], @@ -4166,7 +3935,7 @@ "name": "Bavaria DOP 80cm", "type": "tms", "template": "https://mapproxy.osm.ch/tiles/BAYERNDOP80/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", - "scaleExtent": [7, 18], + "zoomExtent": [7, 18], "polygon": [ [ [10.12359, 50.56846], @@ -4479,7 +4248,7 @@ "name": "BD Carthage", "type": "tms", "template": "https://{switch:a,b,c}.tile.openstreetmap.fr/route500hydro/{zoom}/{x}/{y}.png", - "scaleExtent": [6, 20], + "zoomExtent": [6, 20], "polygon": [ [ [-2.7, 43.9], @@ -5058,7 +4827,7 @@ "name": "BDOrtho IGN", "type": "tms", "template": "https://proxy-ign.openstreetmap.fr/94GjiyqD/bdortho/{zoom}/{x}/{y}.jpg", - "scaleExtent": [2, 21], + "zoomExtent": [2, 21], "polygon": [ [ [-2.7, 43.9], @@ -5695,7 +5464,7 @@ "template": "https://geoxxx.agrocampus-ouest.fr/owsifl/gwc/service/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=Benin:cotonou_pleiade_2016&STYLE=&FORMAT=image/jpeg&tileMatrixSet=EPSG:3857&tileMatrix=EPSG:3857:{zoom}&tileRow={y}&tileCol={x}", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [2.31954, 6.55745], @@ -6313,7 +6082,7 @@ "name": "Bing aerial imagery", "type": "bing", "template": "https://www.bing.com/maps", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "default": true, "description": "Satellite and aerial imagery.", "icon": "https://osmlab.github.io/editor-layer-index/sources/world/Bing.png" @@ -6325,7 +6094,7 @@ "template": "https://sitmappe.comune.bologna.it/tms/tileserver/Ortofoto2017/{zoom}/{x}/{y}.png", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2017-01-01T00:00:00.000Z", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [11.22962, 44.53077], @@ -6400,7 +6169,7 @@ "template": "http://osmdata.asitvd.ch/tiles/bonvillars2013/{zoom}/{x}/{y}.png", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [6.66713, 46.83358], @@ -6421,7 +6190,7 @@ "template": "http://wms.openstreetmap.fr/tms/1.0.0/bordeaux_2012/{zoom}/{x}/{y}", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-0.59923, 45.04193], @@ -6500,7 +6269,7 @@ "template": "http://tms.bordeaux.inria.fr/bdx2016/{zoom}/{x}/{y}.jpg", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [-0.59923, 45.04193], @@ -6655,7 +6424,7 @@ "template": "http://{switch:a,b,c,d}.imagery.paulnorman.ca/tiles/bc_mosaic/{zoom}/{x}/{y}.png", "endDate": "2013-06-01T00:00:00.000Z", "startDate": "2009-01-01T00:00:00.000Z", - "scaleExtent": [9, 20], + "zoomExtent": [9, 20], "polygon": [ [ [-123.3176, 49.32726], @@ -7128,7 +6897,7 @@ "name": "Cadastre", "type": "tms", "template": "http://tms.cadastre.openstreetmap.fr/*/tout/{zoom}/{x}/{y}.png", - "scaleExtent": [12, 22], + "zoomExtent": [12, 22], "polygon": [ [ [-2.7, 43.9], @@ -7707,7 +7476,7 @@ "name": "Cadastre geoportail.lu", "type": "tms", "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/cadastre/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -8090,7 +7859,7 @@ "name": "Cambodia, Laos, Thailand, Vietnam, Malaysia, Myanmar bilingual", "type": "tms", "template": "https://{switch:a,b,c,d}.tile.osm-tools.org/osm/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [92.10238, 20.81356], @@ -8187,7 +7956,7 @@ "template": "https://tiles.openaerialmap.org/5ac65a9f91b5310010e0d489/0/5ac65a9f91b5310010e0d48a/{zoom}/{x}/{y}.png", "endDate": "2017-12-20T00:00:00.000Z", "startDate": "2017-12-11T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [-72.26734, 18.63561], @@ -8533,7 +8302,7 @@ "template": "https://mapproxy.osm.ch/tiles/fribourg_2016/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [6.79478, 46.57542], @@ -9505,7 +9274,7 @@ "projection": "EPSG:3857", "endDate": "2010-01-01T00:00:00.000Z", "startDate": "2010-01-01T00:00:00.000Z", - "scaleExtent": [14, 18], + "zoomExtent": [14, 18], "polygon": [ [ [-7.31278, 36.98391], @@ -9576,7 +9345,7 @@ "template": "http://osmdata.asitvd.ch/tiles/cartoriviera2012/{zoom}/{x}/{y}.png", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [7.02235, 46.42856], @@ -10332,7 +10101,7 @@ "template": "https://{switch:a,b,c}.coct.aerial.openstreetmap.org.za/layer/za_coct_aerial_2013/{zoom}/{x}/{y}.jpg", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [18.44866, -33.89362], @@ -10545,7 +10314,7 @@ "template": "https://{switch:a,b,c}.coct.aerial.openstreetmap.org.za/layer/za_coct_aerial_2015/{zoom}/{x}/{y}.jpg", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [18.44866, -33.89362], @@ -10760,7 +10529,7 @@ "projection": "EPSG:3857", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2011-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [8.44624, 47.44143], @@ -10778,7 +10547,7 @@ "type": "wms", "template": "https://www.gis.stadt-zuerich.ch/maps/services/wms/WMS-ZH-STZH-OGD/MapServer/WmsServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Uebersichtsplan&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [8.45788, 47.44582], @@ -10980,7 +10749,7 @@ "name": "Czech CUZK:KM tiles proxy", "type": "tms", "template": "https://osm-{switch:a,b,c}.zby.cz/tiles_cuzk.php/{zoom}/{x}/{y}.png", - "scaleExtent": [13, 18], + "zoomExtent": [13, 18], "polygon": [ [ [15.00637, 49.01774], @@ -11321,7 +11090,7 @@ "name": "Czech RUIAN budovy", "type": "tms", "template": "https://tile.poloha.net/budovy/{zoom}/{x}/{y}.png", - "scaleExtent": [12, 20], + "zoomExtent": [12, 20], "polygon": [ [ [15.00637, 49.01774], @@ -11493,7 +11262,7 @@ "name": "Czech RUIAN parcely", "type": "tms", "template": "https://tile.poloha.net/parcely/{zoom}/{x}/{y}.png", - "scaleExtent": [12, 20], + "zoomExtent": [12, 20], "polygon": [ [ [15.00637, 49.01774], @@ -11769,7 +11538,7 @@ "template": "http://e.tile.openstreetmap.hu/dunai-arviz-2013/{zoom}/{x}/{y}.jpg", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [10, 20], + "zoomExtent": [10, 20], "polygon": [ [ [19.07732, 47.69597], @@ -12344,7 +12113,7 @@ "name": "DigitalGlobe Premium Imagery", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/digitalglobe.316c9a2e/{zoom}/{x}/{y}.png?access_token=pk.eyJ1IjoiZGlnaXRhbGdsb2JlIiwiYSI6ImNqZGFrZ2c2dzFlMWgyd2x0ZHdmMDB6NzYifQ.9Pl3XOO82ArX94fHV289Pg", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "terms_url": "https://wiki.openstreetmap.org/wiki/DigitalGlobe", "terms_text": "Terms & Feedback", "default": true, @@ -12356,7 +12125,7 @@ "name": "DigitalGlobe Premium Imagery Vintage", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/digitalglobe.2850d66c/{zoom}/{x}/{y}.png?access_token=pk.eyJ1IjoiZGlnaXRhbGdsb2JlIiwiYSI6ImNqOTBkcmZjNzJ5ZnozNHF6NnVkOGd6ODYifQ.grAnqgpCjOaeq-ozqt4QNw", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "terms_url": "https://wiki.openstreetmap.org/wiki/DigitalGlobe", "terms_text": "Terms & Feedback", "description": "Imagery boundaries and capture dates. Labels appear at zoom level 13 and higher.", @@ -12368,7 +12137,7 @@ "name": "DigitalGlobe Standard Imagery", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/digitalglobe.0a8e44ba/{zoom}/{x}/{y}.png?access_token=pk.eyJ1IjoiZGlnaXRhbGdsb2JlIiwiYSI6ImNqZGFrZ3pjczNpaHYycXFyMGo0djY3N2IifQ.90uebT4-ow1uqZKTUrf6RQ", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "terms_url": "https://wiki.openstreetmap.org/wiki/DigitalGlobe", "terms_text": "Terms & Feedback", "default": true, @@ -12380,7 +12149,7 @@ "name": "DigitalGlobe Standard Imagery Vintage", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/digitalglobe.1412531a/{zoom}/{x}/{y}.png?access_token=pk.eyJ1IjoiZGlnaXRhbGdsb2JlIiwiYSI6ImNqOTBlYWJ1ZDAza2YyeG14NWVodTA4OWUifQ.wVc8ZOuPuYVw39lhS2j3_g", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "terms_url": "https://wiki.openstreetmap.org/wiki/DigitalGlobe", "terms_text": "Terms & Feedback", "description": "Imagery boundaries and capture dates. Labels appear at zoom level 13 and higher.", @@ -14635,7 +14404,7 @@ "name": "Esri World Imagery", "type": "tms", "template": "https://{switch:services,server}.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{zoom}/{y}/{x}", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "terms_url": "https://wiki.openstreetmap.org/wiki/Esri", "terms_text": "Terms & Feedback", "default": true, @@ -14647,7 +14416,7 @@ "name": "Esri World Imagery (Clarity) Beta", "type": "tms", "template": "https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{zoom}/{y}/{x}", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "terms_url": "https://wiki.openstreetmap.org/wiki/Esri", "terms_text": "Terms & Feedback", "default": true, @@ -17779,7 +17548,7 @@ "name": "Estonia Ortho (Maaamet)", "type": "tms", "template": "https://tiles.maaamet.ee/tm/tms/1.0.0/foto@GMC/{zoom}/{x}/{-y}.png", - "scaleExtent": [14, 18], + "zoomExtent": [14, 18], "polygon": [ [ [22.35364, 58.85385], @@ -18579,7 +18348,7 @@ "template": "http://osmdata.asitvd.ch/tiles/fiez2013/{zoom}/{x}/{y}.png", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [6.62313, 46.82339], @@ -18600,7 +18369,7 @@ "type": "wms", "template": "https://ogc.fiskeridir.no/wms.ashx?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=layer_262&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [3, 22], + "zoomExtent": [3, 22], "polygon": [ [ [31.90425, 70.43681], @@ -18644,7 +18413,7 @@ "template": "http://e.tile.openstreetmap.hu/ortofoto2000/{zoom}/{x}/{y}.jpg", "endDate": "2000-01-01T00:00:00.000Z", "startDate": "2000-01-01T00:00:00.000Z", - "scaleExtent": [0, 17], + "zoomExtent": [0, 17], "polygon": [ [ [16.11391, 46.8691], @@ -19232,7 +19001,7 @@ "template": "http://e.tile.openstreetmap.hu/ortofoto2005/{zoom}/{x}/{y}.jpg", "endDate": "2005-01-01T00:00:00.000Z", "startDate": "2005-01-01T00:00:00.000Z", - "scaleExtent": [0, 17], + "zoomExtent": [0, 17], "polygon": [ [ [16.11391, 46.8691], @@ -19818,7 +19587,7 @@ "name": "Freemap.sk Car", "type": "tms", "template": "https://{switch:a,b,c,d}.freemap.sk/A/{zoom}/{x}/{y}.jpeg", - "scaleExtent": [8, 16], + "zoomExtent": [8, 16], "polygon": [ [ [19.83682, 49.25529], @@ -19877,7 +19646,7 @@ "name": "Freemap.sk Cyclo", "type": "tms", "template": "https://{switch:a,b,c,d}.freemap.sk/C/{zoom}/{x}/{y}.jpeg", - "scaleExtent": [8, 16], + "zoomExtent": [8, 16], "polygon": [ [ [19.83682, 49.25529], @@ -19936,7 +19705,7 @@ "name": "Freemap.sk Hiking", "type": "tms", "template": "https://{switch:a,b,c,d}.freemap.sk/T/{zoom}/{x}/{y}.jpeg", - "scaleExtent": [8, 16], + "zoomExtent": [8, 16], "polygon": [ [ [19.83682, 49.25529], @@ -19995,7 +19764,7 @@ "name": "Freemap.sk Ski", "type": "tms", "template": "https://{switch:a,b,c,d}.freemap.sk/K/{zoom}/{x}/{y}.jpeg", - "scaleExtent": [8, 16], + "zoomExtent": [8, 16], "polygon": [ [ [19.83682, 49.25529], @@ -20055,7 +19824,7 @@ "type": "wms", "template": "https://extranet.liikennevirasto.fi/inspirepalvelu/beta/wms?SERVICE=WMS&FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=dr_tielinkki_toim_lk&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [3, 20], + "zoomExtent": [3, 20], "polygon": [ [ [27.96569, 70.0988], @@ -20169,7 +19938,7 @@ "template": "https://imagery.openstreetmap.fr/tms/1.0.0/gaza_pleiades_20140706/{zoom}/{x}/{y}", "endDate": "2014-07-06T00:00:00.000Z", "startDate": "2014-07-06T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [34.49022, 31.59487], @@ -20248,7 +20017,7 @@ "template": "https://imagery.openstreetmap.fr/tms/1.0.0/gaza_pleiades_20140706_nir/{zoom}/{x}/{y}", "endDate": "2014-07-06T00:00:00.000Z", "startDate": "2014-07-06T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [34.49022, 31.59487], @@ -20846,7 +20615,7 @@ "type": "wms", "template": "https://maps.geogratis.gc.ca/wms/roads_en?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=roads&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [4, 20], + "zoomExtent": [4, 20], "polygon": [ [ [-141.0678, 60.2442], @@ -21106,7 +20875,7 @@ "type": "wms", "template": "https://cartes.geogratis.gc.ca/wms/roads_fr?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=routes&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [4, 20], + "zoomExtent": [4, 20], "polygon": [ [ [-141.0678, 60.2442], @@ -22823,7 +22592,7 @@ "template": "http://{switch:wmts1,wmts2}.geoportail.lu/opendata/wmts/ortho_10cm_proto_lidar/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", "endDate": "2017-04-09T00:00:00.000Z", "startDate": "2017-04-09T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [6.05001, 49.82175], @@ -22843,7 +22612,7 @@ "type": "wms", "template": "http://mapy.geoportal.gov.pl/wss/service/img/guest/ORTO/MapServer/WMSServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Raster&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}{header(User-Agent,Mozilla/5.0 (JOSM)}", "projection": "EPSG:4326", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [15.9751, 54.37092], @@ -23067,7 +22836,7 @@ "name": "Geoportal.gov.pl (Orthophotomap)", "type": "tms", "template": "https://wms.misek.pl/geoportal.orto/tms/{zoom}/{x}/{y}", - "scaleExtent": [6, 24], + "zoomExtent": [6, 24], "polygon": [ [ [15.9751, 54.37092], @@ -23529,7 +23298,7 @@ "template": "http://wms.openstreetmap.fr/tms/1.0.0/nancy_2012/{zoom}/{x}/{y}", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [6.06066, 48.60554], @@ -23547,7 +23316,7 @@ "name": "GURS: Building outlines", "type": "tms", "template": "http://wms.openstreetmap.de/tms/GURS-building-outlines/{zoom}/{x}/{y}.png", - "scaleExtent": [8, 19], + "zoomExtent": [8, 19], "polygon": [ [ [15.17101, 45.41273], @@ -23740,7 +23509,7 @@ "name": "GURS: Road lines", "type": "tms", "template": "http://wms.openstreetmap.de/tms/GURS-road-lines/{zoom}/{x}/{y}.png", - "scaleExtent": [8, 19], + "zoomExtent": [8, 19], "polygon": [ [ [15.17101, 45.41273], @@ -24203,7 +23972,7 @@ "template": "https://{switch:a,b,c}.hampshire.aerial.openstreetmap.org.uk/layer/gb_hampshire_aerial_fcir/{zoom}/{x}/{y}.png", "endDate": "2014-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [8, 20], + "zoomExtent": [8, 20], "polygon": [ [ [-1.31567, 50.77809], @@ -24393,7 +24162,7 @@ "template": "https://{switch:a,b,c}.hampshire.aerial.openstreetmap.org.uk/layer/gb_hampshire_aerial_rgb/{zoom}/{x}/{y}.png", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [8, 20], + "zoomExtent": [8, 20], "polygon": [ [ [-1.31567, 50.77809], @@ -24581,7 +24350,7 @@ "name": "Hillshade Hungary", "type": "tms", "template": "https://{switch:a,b,c}.map.turistautak.hu/tiles/shading/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [[[15, 45], [24, 45], [24, 49], [15, 49], [15, 45]]], "terms_text": "SRTM", "overlay": true @@ -24591,7 +24360,7 @@ "name": "IBGE Distrito Federal", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/styles/v1/wille/cirnnxni1000jg8nfppc8g7pm/tiles/256/{zoom}/{x}/{y}?access_token=pk.eyJ1Ijoid2lsbGUiLCJhIjoicFNVWk5VWSJ9.hluCd0YGvYHNlFi_utWe2g", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-48.2444, -16.0508], @@ -24610,7 +24379,7 @@ "name": "IBGE Mapa de Setores Rurais", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/tmpsantos.i00mo1kj/{zoom}/{x}/{y}.png?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJncjlmd0t3In0.DmZsIeOW-3x-C5eX-wAqTw", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-69.94793, -4.23168], @@ -25700,7 +25469,7 @@ "name": "IBGE Mapa de Setores Urbanos", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/tmpsantos.hgda0m6h/{zoom}/{x}/{y}.png?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJncjlmd0t3In0.DmZsIeOW-3x-C5eX-wAqTw", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-69.94793, -4.23168], @@ -28311,7 +28080,7 @@ "name": "IGN topographical map (TMS)", "type": "tms", "template": "https://ide.ign.gob.ar/geoservicios/rest/services/Mapas_IGN/mapa_topografico/MapServer/tile/{zoom}/{y}/{x}", - "scaleExtent": [1, 20], + "zoomExtent": [1, 20], "polygon": [ [ [-55.5438, -35.77219], @@ -28696,7 +28465,7 @@ "name": "Imagerie Drone (Haiti)", "type": "tms", "template": "http://wms.openstreetmap.fr/tms/1.0.0/iomhaiti/{zoom}/{x}/{y}", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [-72.15474, 19.6879], @@ -29333,7 +29102,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R119_N09_20160327T050917&z={zoom}&x={x}&y={-y}", "endDate": "2016-03-27T00:00:00.000Z", "startDate": "2016-03-27T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [79.01779, 8.82757], @@ -29355,7 +29124,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80700162014211LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2014-07-31T00:00:00.000Z", "startDate": "2014-07-31T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [-152.70873, 62.30357], @@ -29378,7 +29147,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=aral2&z={zoom}&x={x}&y={-y}", "endDate": "2016-03-03T00:00:00.000Z", "startDate": "2016-03-03T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [58.049, 43.2623], @@ -29403,7 +29172,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=aral1&z={zoom}&x={x}&y={-y}", "endDate": "2016-09-09T00:00:00.000Z", "startDate": "2016-09-09T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [58.049, 43.2623], @@ -29428,7 +29197,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R067_S40_20170417T140051&z={zoom}&x={x}&y={-y}", "endDate": "2017-04-17T00:00:00.000Z", "startDate": "2017-04-17T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-62.9988, -40.7327], @@ -29450,7 +29219,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R067_S40_20170127T140051&z={zoom}&x={x}&y={-y}", "endDate": "2017-01-27T00:00:00.000Z", "startDate": "2017-01-27T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-62.9988, -40.7327], @@ -29472,7 +29241,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81190582014075LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2014-03-16T00:00:00.000Z", "startDate": "2014-03-16T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [114.36, 2.02846], @@ -29495,7 +29264,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81250592016107LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [104.00155, -0.00008], @@ -29517,7 +29286,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80770232017156LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2017-06-05T00:00:00.000Z", "startDate": "2017-06-05T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-168.2544, 53.8749], @@ -29539,7 +29308,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81800982013291LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2013-10-18T00:00:00.000Z", "startDate": "2013-10-18T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [3.24653, -54.47047], @@ -29562,7 +29331,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R017_S67_20170223T022551&z={zoom}&x={x}&y={-y}", "endDate": "2017-02-23T00:00:00.000Z", "startDate": "2017-02-23T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [96.1197, -67.6542], @@ -29586,7 +29355,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R065_N47_20160929T102022&z={zoom}&x={x}&y={-y}", "endDate": "2016-09-29T00:00:00.000Z", "startDate": "2016-09-29T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [10.55906, 45.95485], @@ -29611,7 +29380,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82050982015344LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-12-10T00:00:00.000Z", "startDate": "2015-12-10T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-34.17701, -55.29693], @@ -29634,7 +29403,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R086_N60_20160831T213532&z={zoom}&x={x}&y={-y}", "endDate": "2016-08-31T00:00:00.000Z", "startDate": "2016-08-31T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-154.5102, 59.4577], @@ -29658,7 +29427,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=EO1A0040712016264110KF&z={zoom}&x={x}&y={-y}", "endDate": "2016-09-21T00:00:00.000Z", "startDate": "2016-09-21T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-72.75945, -15.68684], @@ -29682,7 +29451,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R022_N06_20151221T103009&z={zoom}&x={x}&y={-y}", "endDate": "2015-12-21T00:00:00.000Z", "startDate": "2015-12-21T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [1.83975, 6.2578], @@ -29705,7 +29474,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R040_N01_20160311T164128&z={zoom}&x={x}&y={-y}", "endDate": "2016-03-11T00:00:00.000Z", "startDate": "2016-03-11T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-92.05216, 1.3213], @@ -29727,7 +29496,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80360072014245LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2014-09-02T00:00:00.000Z", "startDate": "2014-09-02T00:00:00.000Z", - "scaleExtent": [0, 11], + "zoomExtent": [0, 11], "polygon": [ [ [-84.34799, 74.38946], @@ -29749,7 +29518,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82160152013239LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2013-08-27T00:00:00.000Z", "startDate": "2013-08-27T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [-13.04701, 64.21109], @@ -29771,7 +29540,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=AST_L1T_00302052007154424_20150518041444_91492&z={zoom}&x={x}&y={-y}", "endDate": "2012-02-05T00:00:00.000Z", "startDate": "2012-02-05T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-78.532, -1.80509], @@ -29793,7 +29562,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R009_S61_20160109&z={zoom}&x={x}&y={-y}", "endDate": "2016-01-09T00:00:00.000Z", "startDate": "2016-01-09T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-56.13476, -61.63472], @@ -29816,7 +29585,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=enderby&z={zoom}&x={x}&y={-y}", "endDate": "2017-03-27T00:00:00.000Z", "startDate": "2017-01-25T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [45.4547, -68.5091], @@ -29841,7 +29610,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82100502015347LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-12-13T00:00:00.000Z", "startDate": "2015-12-13T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-24.75878, 14.74814], @@ -29863,7 +29632,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=greenland&z={zoom}&x={x}&y={-y}", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [-43.9774, 59.7171], @@ -30038,7 +29807,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R047_S54_20160411T044330&z={zoom}&x={x}&y={-y}", "endDate": "2016-04-12T00:00:00.000Z", "startDate": "2016-04-12T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [73.06897, -53.27059], @@ -30061,7 +29830,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82280982013259LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2013-09-16T00:00:00.000Z", "startDate": "2013-09-16T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [-69.8568, -55.55949], @@ -30086,7 +29855,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_kangerlussuaq_20151008&z={zoom}&x={x}&y={-y}", "endDate": "2015-10-08T00:00:00.000Z", "startDate": "2015-10-08T00:00:00.000Z", - "scaleExtent": [0, 17], + "zoomExtent": [0, 17], "polygon": [ [ [-50.6992, 66.9888], @@ -30116,7 +29885,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_kangerlussuaq_20160518&z={zoom}&x={x}&y={-y}", "endDate": "2016-05-18T00:00:00.000Z", "startDate": "2016-05-18T00:00:00.000Z", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [ [ [-50.7519, 66.9996], @@ -30142,7 +29911,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R021_N44_20160807T083013&z={zoom}&x={x}&y={-y}", "endDate": "2016-08-07T00:00:00.000Z", "startDate": "2016-08-07T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [35.93259, 44.96237], @@ -30164,7 +29933,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R021_N44_20180429T082601&z={zoom}&x={x}&y={-y}", "endDate": "2018-04-29T00:00:00.000Z", "startDate": "2018-04-29T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [35.8787, 45.0348], @@ -30186,7 +29955,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=ls_polar2&z={zoom}&x={x}&y={-y}", "endDate": "2016-07-17T00:00:00.000Z", "startDate": "2016-07-17T00:00:00.000Z", - "scaleExtent": [0, 10], + "zoomExtent": [0, 10], "polygon": [ [ [-79.05175, 81.91484], @@ -30211,7 +29980,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=larsen_2018&z={zoom}&x={x}&y={-y}", "endDate": "2018-01-06T00:00:00.000Z", "startDate": "2018-01-06T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [-62.7282, -68.9652], @@ -30237,7 +30006,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=AST_L1T_00311162013112731_20150618142416_109190&z={zoom}&x={x}&y={-y}", "endDate": "2013-11-16T00:00:00.000Z", "startDate": "2013-11-16T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-28.21075, -56.72108], @@ -30259,7 +30028,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81991002015286LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-10-13T00:00:00.000Z", "startDate": "2015-10-13T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-27.99293, -56.73479], @@ -30282,7 +30051,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2B_R005_S69_20180222T061749&z={zoom}&x={x}&y={-y}", "endDate": "2018-02-22T00:00:00.000Z", "startDate": "2018-02-22T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [35.124, -70.3693], @@ -30305,7 +30074,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=ls_polar&z={zoom}&x={x}&y={-y}", "endDate": "2013-05-17T00:00:00.000Z", "startDate": "2013-05-17T00:00:00.000Z", - "scaleExtent": [0, 10], + "zoomExtent": [0, 10], "polygon": [ [ [-85.76109, 81.39333], @@ -30335,7 +30104,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R042_S78_20170214T202521&z={zoom}&x={x}&y={-y}", "endDate": "2017-02-14T00:00:00.000Z", "startDate": "2017-02-14T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [162.9125, -78.4514], @@ -30360,7 +30129,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R092_S02_20160613T075613&z={zoom}&x={x}&y={-y}", "endDate": "2016-06-13T00:00:00.000Z", "startDate": "2016-06-13T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [37.20666, -0.26685], @@ -30382,7 +30151,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R092_S05_20160802T075556&z={zoom}&x={x}&y={-y}", "endDate": "2016-08-02T00:00:00.000Z", "startDate": "2016-08-02T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [37.24769, -3.22921], @@ -30404,7 +30173,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80940622015159LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-06-08T00:00:00.000Z", "startDate": "2015-06-08T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [150.38853, -2.80053], @@ -30426,7 +30195,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=northsea_s2_2016&z={zoom}&x={x}&y={-y}", "endDate": "2016-09-25T00:00:00.000Z", "startDate": "2016-09-25T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [5.1562, 52.8755], @@ -30450,7 +30219,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=northsea_s2_2017&z={zoom}&x={x}&y={-y}", "endDate": "2017-06-02T00:00:00.000Z", "startDate": "2017-06-02T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [5.1713, 53.0918], @@ -30473,7 +30242,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=northsea_s2_2018&z={zoom}&x={x}&y={-y}", "endDate": "2018-05-08T00:00:00.000Z", "startDate": "2018-05-08T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [5.3179, 53.0918], @@ -30497,7 +30266,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=caspian_2018&z={zoom}&x={x}&y={-y}", "endDate": "2018-05-16T00:00:00.000Z", "startDate": "2018-05-16T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [51.0243, 45.0729], @@ -30520,7 +30289,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=ural_s2_2016&z={zoom}&x={x}&y={-y}", "endDate": "2016-08-12T00:00:00.000Z", "startDate": "2016-08-12T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [59.19898, 64.89205], @@ -30544,7 +30313,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=ndvina&z={zoom}&x={x}&y={-y}", "endDate": "2015-09-13T00:00:00.000Z", "startDate": "2015-09-13T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [37.7291, 64.1971], @@ -30568,7 +30337,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=nellesmere_ast&z={zoom}&x={x}&y={-y}", "endDate": "2012-07-09T00:00:00.000Z", "startDate": "2012-07-09T00:00:00.000Z", - "scaleExtent": [0, 10], + "zoomExtent": [0, 10], "polygon": [ [ [-81.62923, 82.4597], @@ -30592,7 +30361,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=nellesmere_ast_2016&z={zoom}&x={x}&y={-y}", "endDate": "2012-07-15T00:00:00.000Z", "startDate": "2012-07-08T00:00:00.000Z", - "scaleExtent": [0, 10], + "zoomExtent": [0, 10], "polygon": [ [ [-78.89729, 82.17577], @@ -30617,7 +30386,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81960222015233LGN00vis&z={zoom}&x={x}&y={-y}", "endDate": "2015-08-21T00:00:00.000Z", "startDate": "2015-08-21T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [7.63568, 53.28027], @@ -30640,7 +30409,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81960222015233LGN00ir&z={zoom}&x={x}&y={-y}", "endDate": "2015-08-21T00:00:00.000Z", "startDate": "2015-08-21T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [7.63568, 53.28027], @@ -30663,7 +30432,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=ngreenland_ast&z={zoom}&x={x}&y={-y}", "endDate": "2012-08-13T00:00:00.000Z", "startDate": "2005-06-21T00:00:00.000Z", - "scaleExtent": [0, 10], + "zoomExtent": [0, 10], "polygon": [ [ [-52.49222, 82.48972], @@ -30690,7 +30459,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=EO1A1350972013086110KF&z={zoom}&x={x}&y={-y}", "endDate": "2013-03-13T00:00:00.000Z", "startDate": "2013-03-13T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [73.2279, -53.20333], @@ -30714,7 +30483,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R111_N09_20160604T154554&z={zoom}&x={x}&y={-y}", "endDate": "2016-06-07T00:00:00.000Z", "startDate": "2016-06-07T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-80.01654, 8.84898], @@ -30736,7 +30505,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=EO1A0120532016364110KF&z={zoom}&x={x}&y={-y}", "endDate": "2016-12-30T00:00:00.000Z", "startDate": "2016-12-30T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-79.62539, 8.77083], @@ -30760,7 +30529,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R078_N68_20160930T081002&z={zoom}&x={x}&y={-y}", "endDate": "2016-09-30T00:00:00.000Z", "startDate": "2016-09-30T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [53.1802, 67.5344], @@ -30787,7 +30556,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81511242016033LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2016-02-02T00:00:00.000Z", "startDate": "2016-02-02T00:00:00.000Z", - "scaleExtent": [0, 10], + "zoomExtent": [0, 10], "polygon": [ [ [-53.20922, -84.12525], @@ -30811,7 +30580,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z={zoom}&x={x}&y={-y}", "endDate": "2015-08-31T00:00:00.000Z", "startDate": "2015-08-31T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [19.11233, 42.15316], @@ -30833,7 +30602,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=DMS_1142622_03746_20110415_17533956&z={zoom}&x={x}&y={-y}", "endDate": "2011-04-15T00:00:00.000Z", "startDate": "2011-04-15T00:00:00.000Z", - "scaleExtent": [0, 15], + "zoomExtent": [0, 15], "polygon": [ [ [-51.23857, 68.79972], @@ -30855,7 +30624,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81510432015030LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [67.9684, 22.36265], @@ -30878,7 +30647,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R093_N41_20150828T092005&z={zoom}&x={x}&y={-y}", "endDate": "2015-08-28T00:00:00.000Z", "startDate": "2015-08-28T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [23.80811, 41.58488], @@ -30901,7 +30670,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81730602015040LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-02-09T00:00:00.000Z", "startDate": "2015-02-09T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [29.7663, 0.20689], @@ -30923,7 +30692,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R078_N01_20160702T082522&z={zoom}&x={x}&y={-y}", "endDate": "2016-07-02T00:00:00.000Z", "startDate": "2016-07-02T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [29.80514, 0.23586], @@ -30945,7 +30714,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80611072014036LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2014-02-05T00:00:00.000Z", "startDate": "2014-02-05T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-180, -67.42635], @@ -30967,7 +30736,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82100972015347LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-12-13T00:00:00.000Z", "startDate": "2015-12-13T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-42.12875, -53.7205], @@ -30989,7 +30758,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81130622013270LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2013-09-27T00:00:00.000Z", "startDate": "2013-09-27T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [120.84382, -3.59545], @@ -31013,7 +30782,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80281222016035LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2016-02-04T00:00:00.000Z", "startDate": "2016-02-04T00:00:00.000Z", - "scaleExtent": [0, 10], + "zoomExtent": [0, 10], "polygon": [ [ [156.96951, -84.50098], @@ -31040,7 +30809,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81030632015286LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2015-10-13T00:00:00.000Z", "startDate": "2015-10-13T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [136.4226, -4.2853], @@ -31062,7 +30831,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R088_S05_20160812T011732&z={zoom}&x={x}&y={-y}", "endDate": "2016-08-12T00:00:00.000Z", "startDate": "2016-08-12T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [136.8044, -4.2585], @@ -31084,7 +30853,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=s2sval&z={zoom}&x={x}&y={-y}", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [16.6108, 76.4137], @@ -31124,7 +30893,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=DMS_1142636_160xx_20110507_1822xxxx&z={zoom}&x={x}&y={-y}", "endDate": "2011-05-07T00:00:00.000Z", "startDate": "2011-05-07T00:00:00.000Z", - "scaleExtent": [0, 15], + "zoomExtent": [0, 15], "polygon": [ [ [-68.93977, 76.51133], @@ -31148,7 +30917,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_thule2_2015.09.25&z={zoom}&x={x}&y={-y}", "endDate": "2015-09-25T00:00:00.000Z", "startDate": "2015-09-25T00:00:00.000Z", - "scaleExtent": [0, 17], + "zoomExtent": [0, 17], "polygon": [ [ [-68.74292, 76.52636], @@ -31174,7 +30943,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_thule_2015.10.06&z={zoom}&x={x}&y={-y}", "endDate": "2015-10-06T00:00:00.000Z", "startDate": "2015-10-06T00:00:00.000Z", - "scaleExtent": [0, 16], + "zoomExtent": [0, 16], "polygon": [ [ [-68.81924, 76.5251], @@ -31199,7 +30968,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_thule_2015.09.25&z={zoom}&x={x}&y={-y}", "endDate": "2015-09-25T00:00:00.000Z", "startDate": "2015-09-25T00:00:00.000Z", - "scaleExtent": [0, 16], + "zoomExtent": [0, 16], "polygon": [ [ [-68.77771, 76.50688], @@ -31223,7 +30992,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R094_N79_20160812T105622&z={zoom}&x={x}&y={-y}", "endDate": "2016-08-12T00:00:00.000Z", "startDate": "2016-08-12T00:00:00.000Z", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [78.45886, 80.72643], @@ -31245,7 +31014,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80910682014358LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2014-12-24T00:00:00.000Z", "startDate": "2014-12-24T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [153.06138, -11.78923], @@ -31268,7 +31037,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82330892016031LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2016-01-31T00:00:00.000Z", "startDate": "2016-01-31T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-72.86696, -41.51741], @@ -31291,7 +31060,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R089_N52_20160623T024048&z={zoom}&x={x}&y={-y}", "endDate": "2016-06-23T00:00:00.000Z", "startDate": "2016-06-23T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [126.36143, 51.37553], @@ -31313,7 +31082,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=walps_autumn_2017&z={zoom}&x={x}&y={-y}", "endDate": "2017-10-17T00:00:00.000Z", "startDate": "2017-10-17T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [4.6412, 43.2493], @@ -31342,7 +31111,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81490352013282LGN00&z={zoom}&x={x}&y={-y}", "endDate": "2013-10-09T00:00:00.000Z", "startDate": "2013-10-09T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [75.98364, 34.97851], @@ -31364,7 +31133,7 @@ "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R039_S15_20160510T145731&z={zoom}&x={x}&y={-y}", "endDate": "2016-05-10T00:00:00.000Z", "startDate": "2016-05-10T00:00:00.000Z", - "scaleExtent": [0, 14], + "zoomExtent": [0, 14], "polygon": [ [ [-71.18071, -14.49785], @@ -31384,7 +31153,7 @@ "name": "IPR ortofoto LAST (tmsproxy)", "type": "tms", "template": "https://osm-{switch:a,b,c}.zby.cz/tiles_ipr_last.php/{zoom}/{x}/{y}.jpg", - "scaleExtent": [1, 20], + "zoomExtent": [1, 20], "polygon": [ [ [14.81232, 49.93089], @@ -31400,7 +31169,7 @@ "name": "IPR ortofoto Low-Vegetation (tmsproxy)", "type": "tms", "template": "https://osm-{switch:a,b,c}.zby.cz/tiles_ipr_vege.php/{zoom}/{x}/{y}.jpg", - "scaleExtent": [1, 20], + "zoomExtent": [1, 20], "polygon": [ [ [14.30454, 49.99538], @@ -31450,7 +31219,7 @@ "name": "Ireland British War Office 1:25k GSGS 3906", "type": "tms", "template": "https://mapwarper.net/layers/tile/101/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [ [ [-9.31139, 51.43828], @@ -31509,7 +31278,7 @@ "name": "Israel Hiking", "type": "tms", "template": "https://israelhiking.osm.org.il/Tiles/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 16], + "zoomExtent": [7, 16], "polygon": [ [ [34.64563, 32.92073], @@ -31564,7 +31333,7 @@ "name": "Israel MTB", "type": "tms", "template": "https://israelhiking.osm.org.il/MTBTiles/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 16], + "zoomExtent": [7, 16], "polygon": [ [ [34.64563, 32.92073], @@ -31993,7 +31762,7 @@ "name": "Japan GSI ortho Imagery", "type": "tms", "template": "https://cyberjapandata.gsi.go.jp/xyz/ort/{zoom}/{x}/{y}.jpg", - "scaleExtent": [5, 19], + "zoomExtent": [5, 19], "polygon": [ [ [130.35021, 31.45741], @@ -33982,7 +33751,7 @@ "name": "Japan GSI Standard Map", "type": "tms", "template": "https://cyberjapandata.gsi.go.jp/xyz/std/{zoom}/{x}/{y}.png", - "scaleExtent": [5, 18], + "zoomExtent": [5, 18], "polygon": [ [ [141.85547, 44.64912], @@ -34019,7 +33788,7 @@ "template": "https://mapproxy.osm.ch/tiles/AGIS2014/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2014-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [8, 19], + "zoomExtent": [8, 19], "polygon": [ [ [8.09602, 47.57882], @@ -34636,7 +34405,7 @@ "template": "https://mapproxy.osm.ch/tiles/AGIS2016/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [8, 19], + "zoomExtent": [8, 19], "polygon": [ [ [7.70438, 47.55794], @@ -34727,7 +34496,7 @@ "template": "https://mapproxy.osm.ch/tiles/KTBASELLANDSCHAFT2015/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [18, 21], + "zoomExtent": [18, 21], "polygon": [ [ [7.37028, 47.41368], @@ -34956,7 +34725,7 @@ "template": "https://mapproxy.osm.ch/tiles/KTBASELSTADT2015/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [8, 21], + "zoomExtent": [8, 21], "polygon": [ [ [7.492, 47.4817], @@ -34975,7 +34744,7 @@ "template": "https://mapproxy.osm.ch/tiles/KTBASELSTADT2017/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2017-01-01T00:00:00.000Z", - "scaleExtent": [8, 21], + "zoomExtent": [8, 21], "polygon": [ [ [7.67138, 47.59522], @@ -35036,7 +34805,7 @@ "template": "https://mapproxy.osm.ch/tiles/sogis2014/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [15, 19], + "zoomExtent": [15, 19], "polygon": [ [ [7.95595, 47.47162], @@ -35215,7 +34984,7 @@ "projection": "EPSG:4326", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [15, 19], + "zoomExtent": [15, 19], "polygon": [ [ [7.57042, 47.15792], @@ -35491,7 +35260,7 @@ "name": "Kanton Thurgau OF 2017", "type": "tms", "template": "https://mapproxy.osm.ch/tiles/KTTHURGAU2017/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", - "scaleExtent": [8, 21], + "zoomExtent": [8, 21], "polygon": [ [ [8.63768, 47.70041], @@ -35573,7 +35342,7 @@ "template": "https://mapproxy.osm.ch/tiles/KTZUERICH2015/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [8, 21], + "zoomExtent": [8, 21], "polygon": [ [ [8.71338, 47.21388], @@ -36084,7 +35853,7 @@ "projection": "EPSG:3857", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [8, 21], + "zoomExtent": [8, 21], "polygon": [ [ [8.71338, 47.21388], @@ -36593,7 +36362,7 @@ "type": "wms", "template": "https://openwms.statkart.no/skwms1/wms.adm_enheter?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=avtaltavgrensningslinje,territorialgrense,riksgrense,fylker,kommuner&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [31.90425, 70.43681], @@ -36636,7 +36405,7 @@ "type": "wms", "template": "https://openwms.statkart.no/skwms1/wms.nrl?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=nrl3_wms&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [31.90425, 70.43681], @@ -36679,7 +36448,7 @@ "type": "wms", "template": "https://wms.geonorge.no/skwms1/wms.matrikkel?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=matrikkel_WMS&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [31.90425, 70.43681], @@ -36722,7 +36491,7 @@ "type": "wms", "template": "https://openwms.statkart.no/skwms1/wms.topo4?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=hoydetall5linje,hoydetall5punkt,hoydepunkt,vannpunkt,hoydekurver_1m,hoydekurver_5m,N50Hoydekurver,N250Hoydekurver,N500Hoydekurver,N1000Hoydekurver,N2000Hoydekurver&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [9, 22], + "zoomExtent": [9, 22], "polygon": [ [ [31.90425, 70.43681], @@ -36765,7 +36534,7 @@ "type": "wms", "template": "https://wms.geonorge.no/skwms1/wms.hoyde-dom_somlos_skyggerelieff?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=las_dom_skyggerelieff_somlos&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 24], + "zoomExtent": [0, 24], "polygon": [ [ [11.53568, 58.86659], @@ -36930,7 +36699,7 @@ "type": "wms", "template": "https://wms.geonorge.no/skwms1/wms.hoyde-dtm_somlos_skyggerelieff?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=las_dtm_skyggerelieff_somlos&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 24], + "zoomExtent": [0, 24], "polygon": [ [ [11.53568, 58.86659], @@ -37095,7 +36864,7 @@ "type": "wms", "template": "https://wms.geonorge.no/skwms1/wms.n5raster2?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=n5raster_foerstegang_metadata,n5raster_foerstegang&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [11, 20], + "zoomExtent": [11, 20], "polygon": [ [ [31.90425, 70.43681], @@ -37137,7 +36906,7 @@ "type": "wms", "template": "https://wms.geonorge.no/skwms1/wms.friluftsruter2?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Fotrute&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [6, 24], + "zoomExtent": [6, 24], "polygon": [ [ [11.53568, 58.86659], @@ -37302,7 +37071,7 @@ "name": "Kartverket Hillshade overlay", "type": "tms", "template": "https://opencache{switch:,2,3}.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=fjellskygge&zoom={zoom}&x={x}&y={y}", - "scaleExtent": [9, 15], + "zoomExtent": [9, 15], "polygon": [ [ [31.90425, 70.43681], @@ -37345,7 +37114,7 @@ "type": "wms", "template": "https://wms.geonorge.no/skwms1/wms.historiskekart?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=amt1&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [5, 15], + "zoomExtent": [5, 15], "polygon": [ [ [31.90425, 70.43681], @@ -37386,7 +37155,7 @@ "name": "Kartverket N50 topo", "type": "tms", "template": "https://opencache{switch:,2,3}.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=topo4&zoom={zoom}&x={x}&y={y}", - "scaleExtent": [3, 15], + "zoomExtent": [3, 15], "polygon": [ [ [11.53568, 58.86659], @@ -37550,7 +37319,7 @@ "name": "Kartverket Nautical Charts", "type": "tms", "template": "https://opencache{switch:,2,3}.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=sjokartraster&zoom={zoom}&x={x}&y={y}", - "scaleExtent": [3, 20], + "zoomExtent": [3, 20], "polygon": [ [ [-15.01273, -60.16205], @@ -37605,7 +37374,7 @@ "type": "wms", "template": "https://openwms.statkart.no/skwms1/wms.vegnett?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=all&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [3, 24], + "zoomExtent": [3, 24], "polygon": [ [ [11.53568, 58.86659], @@ -37824,7 +37593,7 @@ "type": "wms", "template": "http://mapserver.um.katowice.pl/services/ortowms/MapServer/WMSServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=2&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [18.90884, 50.24006], @@ -37879,7 +37648,7 @@ "template": "http://{switch:a,b,c,d}.tile.paulnorman.ca/kelowna2012/{zoom}/{x}/{y}.png", "endDate": "2012-05-14T00:00:00.000Z", "startDate": "2012-05-13T00:00:00.000Z", - "scaleExtent": [9, 20], + "zoomExtent": [9, 20], "polygon": [ [ [-119.58673, 49.79281], @@ -37987,7 +37756,7 @@ "name": "Kelowna Roads overlay", "type": "tms", "template": "http://{switch:a,b,c,d}.tile.paulnorman.ca/kelowna_overlay/{zoom}/{x}/{y}.png", - "scaleExtent": [9, 20], + "zoomExtent": [9, 20], "polygon": [ [ [-119.58673, 49.79281], @@ -38096,7 +37865,7 @@ "type": "wms", "template": "https://nfs.kystverket.no/arcgis/services/nfs/NFSSistOperativ/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=17,16,15,14,12,10,9,8,7,4&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [12, 19], + "zoomExtent": [12, 19], "polygon": [ [ [3.24857, 56.08599], @@ -38139,7 +37908,7 @@ "template": "http://{switch:a,b,c,d}.tile.paulnorman.ca/landsat_233055/{zoom}/{x}/{y}.png", "endDate": "2013-09-03T00:00:00.000Z", "startDate": "2013-09-03T00:00:00.000Z", - "scaleExtent": [5, 14], + "zoomExtent": [5, 14], "polygon": [ [ [-60.855, 6.1765], @@ -38156,7 +37925,7 @@ "name": "Lantmäteriet Economic Map (historic)", "type": "tms", "template": "https://mapproxy.openstreetmap.se/tms/1.0.0/ek_EPSG3857/{zoom}/{x}/{-y}.jpeg", - "scaleExtent": [3, 17], + "zoomExtent": [3, 17], "polygon": [ [ [12.71117, 55.2666], @@ -38187,7 +37956,7 @@ "type": "wms", "template": "https://api.lantmateriet.se/historiska-ortofoton/wms/v1/token/9b342b7d9f12d4ddb92277be9869d860/?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=OI.Histortho_60&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [5, 19], + "zoomExtent": [5, 19], "polygon": [ [ [12.80182, 55.19612], @@ -38222,7 +37991,7 @@ "type": "wms", "template": "https://api.lantmateriet.se/historiska-ortofoton/wms/v1/token/9b342b7d9f12d4ddb92277be9869d860/?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=OI.Histortho_75&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [5, 19], + "zoomExtent": [5, 19], "polygon": [ [ [12.80182, 55.19612], @@ -38250,7 +38019,7 @@ "template": "http://{switch:wmts1,wmts2}.geoportail.lu/opendata/wmts/ortho_latest/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", "endDate": "2017-06-22T00:00:00.000Z", "startDate": "2017-06-14T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -38493,7 +38262,7 @@ "template": "http://{switch:a,b,c,d}.tile.paulnorman.ca/landsat_047026/{zoom}/{x}/{y}.png", "endDate": "2013-09-12T00:00:00.000Z", "startDate": "2013-09-12T00:00:00.000Z", - "scaleExtent": [5, 13], + "zoomExtent": [5, 13], "polygon": [ [ [-121.93555, 47.78206], @@ -38520,7 +38289,7 @@ "template": "http://osmdata.asitvd.ch/tiles/lausanne2012/{zoom}/{x}/{y}.png", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [6.66668, 46.49441], @@ -38548,7 +38317,7 @@ "name": "LINZ NZ Aerial Imagery", "type": "tms", "template": "https://tiles-a.data-cdn.linz.govt.nz/services;key=3197c6d0e5cb494a95d58dc2de3216c2/tiles/v4/set=2/EPSG:3857/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [167.25037, -47.21957], @@ -38638,7 +38407,7 @@ "name": "LINZ NZ Topo50 Gridless Maps", "type": "tms", "template": "https://tiles-a.data-cdn.linz.govt.nz/services;key=3197c6d0e5cb494a95d58dc2de3216c2/tiles/v4/layer=2343/EPSG:3857/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [167.25037, -47.21957], @@ -38729,7 +38498,7 @@ "template": "https://ort10lt.openmap.lt/g16/{zoom}/{x}/{y}.jpeg", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2010-01-01T00:00:00.000Z", - "scaleExtent": [4, 18], + "zoomExtent": [4, 18], "polygon": [ [ [26.21384, 55.85075], @@ -38845,7 +38614,7 @@ "name": "Locator Overlay", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/openstreetmap.map-inh76ba2/{zoom}/{x}/{y}.png?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJjaml5MjVyb3MwMWV0M3hxYmUzdGdwbzE4In0.q548FjhsSJzvXsGlPsFxAQ", - "scaleExtent": [0, 16], + "zoomExtent": [0, 16], "overzoom": false, "terms_url": "https://www.mapbox.com/about/maps", "terms_text": "Terms & Feedback", @@ -38929,7 +38698,7 @@ "projection": "EPSG:4326", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [19.55046, 51.68509], @@ -39000,7 +38769,7 @@ "projection": "EPSG:4326", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2017-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [19.55046, 51.68509], @@ -39070,7 +38839,7 @@ "template": "http://{switch:a,b,c,d,e,f,g,h}.tiles.cg44.makina-corpus.net/ortho-2012/{zoom}/{x}/{-y}.jpg", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-1.48638, 46.87691], @@ -39408,7 +39177,7 @@ "template": "https://siglon.londrina.pr.gov.br/arcgis/rest/services/Imagens/Ortofotos_2011_Paranacidade/MapServer/WMTS/tile/1.0.0/Imagens_Ortofotos_2011_Paranacidade/default/GoogleMapsCompatible/{zoom}/{y}/{x}", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2011-01-01T00:00:00.000Z", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-51.10903, -23.39275], @@ -39714,7 +39483,7 @@ "type": "wms", "template": "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=4&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE", "projection": "EPSG:3857", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [159.00339, -31.48767], @@ -39769,7 +39538,7 @@ "type": "wms", "template": "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=6&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE", "projection": "EPSG:3857", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [159.00339, -31.48767], @@ -39824,7 +39593,7 @@ "type": "wms", "template": "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=1&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE", "projection": "EPSG:3857", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [159.00339, -31.48767], @@ -39879,7 +39648,7 @@ "type": "wms", "template": "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=3&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE", "projection": "EPSG:3857", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [159.00339, -31.48767], @@ -39934,7 +39703,7 @@ "type": "wms", "template": "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=2&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE", "projection": "EPSG:3857", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [159.00339, -31.48767], @@ -39989,7 +39758,7 @@ "type": "wms", "template": "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=7&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE", "projection": "EPSG:3857", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [159.00339, -31.48767], @@ -40043,7 +39812,7 @@ "name": "LPI NSW Base Map", "type": "tms", "template": "https://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Base_Map/MapServer/tile/{zoom}/{y}/{x}", - "scaleExtent": [1, 19], + "zoomExtent": [1, 19], "polygon": [ [ [140.99486, -28.95297], @@ -40082,7 +39851,7 @@ "name": "LPI NSW Imagery", "type": "tms", "template": "https://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Imagery/MapServer/tile/{zoom}/{y}/{x}", - "scaleExtent": [1, 21], + "zoomExtent": [1, 21], "polygon": [ [ [140.98687, -28.98878], @@ -40182,7 +39951,7 @@ "type": "wms", "template": "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Imagery_Dates/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=0&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE", "projection": "EPSG:3857", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [140.98687, -28.98878], @@ -40281,7 +40050,7 @@ "name": "LPI NSW Topographic Map", "type": "tms", "template": "https://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Topo_Map/MapServer/tile/{zoom}/{y}/{x}", - "scaleExtent": [1, 16], + "zoomExtent": [1, 16], "polygon": [ [ [140.99884, -28.99924], @@ -40502,7 +40271,7 @@ "type": "wms", "template": "https://services.slip.wa.gov.au/public/services/SLIP_Public_Services/Transport/MapServer/WMSServer?LAYERS=8&TRANSPARENT=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&FORMAT=image%2Fpng&SRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}", "projection": "EPSG:3857", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [129.00009, -31.68764], @@ -40538,7 +40307,7 @@ "type": "wms", "template": "https://gint.mainz.de/gint1-cgi/mapserv?map=/data/mapbender-int/umn-www/client/a62/luftbild.map&FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Luftbild_f_mz_2016&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [17, 22], + "zoomExtent": [17, 22], "polygon": [ [ [8.10355, 49.865], @@ -40575,7 +40344,7 @@ "name": "Mapbox Satellite", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}.jpg?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJjaml5MjVyb3MwMWV0M3hxYmUzdGdwbzE4In0.q548FjhsSJzvXsGlPsFxAQ", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "terms_url": "https://www.mapbox.com/about/maps", "terms_text": "Terms & Feedback", "default": true, @@ -41039,7 +40808,7 @@ "projection": "EPSG:3857", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-76.23413, 37.92037], @@ -41100,7 +40869,7 @@ "type": "wms", "template": "https://geodata.md.gov/imap/services/Imagery/MD_ThreeInchImagery/MapServer/WmsServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=MD_ThreeInchImagery&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-76.46299, 38.9808], @@ -41766,7 +41535,7 @@ "type": "wms", "template": "https://arcgisproxy.miljodirektoratet.no/arcgis/services/vern/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=naturvern_klasser_omrade,naturvern_punkt&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [-10.95725, 71.60964], @@ -41831,7 +41600,7 @@ "type": "wms", "template": "https://arcgisproxy.miljodirektoratet.no/arcgis/services/friluftsliv_statlig_sikra/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=friluftsliv_statlig_sikra&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [31.90425, 70.43681], @@ -41932,7 +41701,7 @@ "name": "MML Background Map", "type": "tms", "template": "http://tiles.kartat.kapsi.fi/taustakartta/{zoom}/{x}/{y}.jpg", - "scaleExtent": [2, 19], + "zoomExtent": [2, 19], "polygon": [ [ [27.96569, 70.0988], @@ -42043,7 +41812,7 @@ "name": "MML Orthophoto", "type": "tms", "template": "http://tiles.kartat.kapsi.fi/ortokuva/{zoom}/{x}/{y}.jpg", - "scaleExtent": [2, 19], + "zoomExtent": [2, 19], "polygon": [ [ [27.96569, 70.0988], @@ -42155,7 +41924,7 @@ "name": "MML Topographic Map", "type": "tms", "template": "http://tiles.kartat.kapsi.fi/peruskartta/{zoom}/{x}/{y}.jpg", - "scaleExtent": [2, 19], + "zoomExtent": [2, 19], "polygon": [ [ [27.96569, 70.0988], @@ -42266,7 +42035,7 @@ "name": "MTBmap.no", "type": "tms", "template": "https://mtbmap.no/tiles/osm/mtbmap/{zoom}/{x}/{y}.jpg", - "scaleExtent": [3, 14], + "zoomExtent": [3, 14], "polygon": [ [ [31.90425, 70.43681], @@ -42310,7 +42079,7 @@ "projection": "EPSG:3857", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [11, 22], + "zoomExtent": [11, 22], "polygon": [ [ [11.48878, 48.053], @@ -42381,7 +42150,7 @@ "template": "https://geoportal.openlabs.cc/mapcache/tms/1.0.0/public-transport@GoogleMapsCompatibleExtended/{zoom}/{x}/{-y}.png", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2017-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [19.73762, 41.30154], @@ -42403,7 +42172,7 @@ "template": "https://geoportal.openlabs.cc/mapcache/tms/1.0.0/tirana@GoogleMapsCompatibleExtended/{zoom}/{x}/{-y}.png", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2017-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [19.70226, 41.1404], @@ -42487,7 +42256,7 @@ "template": "http://{switch:a,b,c,d}.imagery.paulnorman.ca/tiles/niger_oct_2012_321/{zoom}/{x}/{y}.png", "endDate": "2012-10-01T00:00:00.000Z", "startDate": "2012-10-01T00:00:00.000Z", - "scaleExtent": [1, 13], + "zoomExtent": [1, 13], "polygon": [ [ [-6.92922, 13.78574], @@ -42514,7 +42283,7 @@ "projection": "EPSG:3857", "endDate": "2015-05-03T00:00:00.000Z", "startDate": "2015-03-29T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-74.86599, 40.08543], @@ -42611,7 +42380,7 @@ "projection": "EPSG:3857", "endDate": "2015-05-03T00:00:00.000Z", "startDate": "2015-03-29T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-74.86599, 40.08543], @@ -42706,7 +42475,7 @@ "type": "tms", "template": "https://wmts.nlsc.gov.tw/wmts/EMAP5_OPENDATA/default/EPSG:3857/{zoom}/{y}/{x}", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [0, 15], + "zoomExtent": [0, 15], "polygon": [ [ [120.45706, 26.39706], @@ -42857,7 +42626,7 @@ "name": "Nomes de Ruas IBGE Salvador-BA", "type": "tms", "template": "https://api.mapbox.com/styles/v1/wille/cj8lp78dn62wl2rquim47qo0g/tiles/256/{zoom}/{x}/{y}?access_token=pk.eyJ1Ijoid2lsbGUiLCJhIjoicFNVWk5VWSJ9.hluCd0YGvYHNlFi_utWe2g", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-38.48974, -12.81113], @@ -42881,7 +42650,7 @@ "name": "Nordic snowmobile overlay", "type": "tms", "template": "https://tiles.kelkkareitit.fi/kelkkareitit/{zoom}/{x}/{y}.png", - "scaleExtent": [3, 18], + "zoomExtent": [3, 18], "polygon": [ [ [27.53173, 60.21799], @@ -42924,7 +42693,7 @@ "name": "Norway Orthophoto", "type": "tms", "template": "https://waapi.webatlas.no/maptiles/tiles/webatlas-orto-newup/wa_grid/{zoom}/{x}/{y}.jpeg?api_key=b8e36d51-119a-423b-b156-d744d54123d5", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [11.53568, 58.86659], @@ -43090,7 +42859,7 @@ "type": "wms", "template": "http://npdwms.npd.no/NPD_FactMap.asp?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Surface_labels,Surface,Pipelines,Pipelines_labels,Fields_labels,Fields&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [6, 20], + "zoomExtent": [6, 20], "polygon": [ [ [8.51989, 57.6454], @@ -43126,7 +42895,7 @@ "type": "wms", "template": "https://gis3.nve.no/map/services/SkredSnoAktR/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Snoskred-Aktsomhetsomrader&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [5, 13], + "zoomExtent": [5, 13], "polygon": [ [ [31.90425, 70.43681], @@ -43169,7 +42938,7 @@ "type": "wms", "template": "https://gis3.nve.no/map/services/Nettanlegg1/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Sentralnett,Regionalnett,Distribusjonsnett,Sjokabler,Master og stolper,Transformatorstasjoner&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [6, 22], + "zoomExtent": [6, 22], "polygon": [ [ [31.90425, 70.43681], @@ -43212,7 +42981,7 @@ "type": "wms", "template": "https://gis3.nve.no/map/services/Vannkraft1/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Vannkraftverk,Vannvei,Dam&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [6, 22], + "zoomExtent": [6, 22], "polygon": [ [ [31.90425, 70.43681], @@ -43255,7 +43024,7 @@ "type": "wms", "template": "https://gis3.nve.no/map/services/Vindkraft/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Vindkraft_utbygd,Vindkraft_under_bygging,Vindkraftomrade_konsesjonsbehandling,Vindturbin_konsesjonsbehandling&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [6, 22], + "zoomExtent": [6, 22], "polygon": [ [ [31.90425, 70.43681], @@ -43300,7 +43069,7 @@ "projection": "EPSG:3857", "endDate": "2018-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-74.91231, 45.03606], @@ -43660,7 +43429,7 @@ "name": "OpenStreetMap (Belgian Style - Dutch)", "type": "tms", "template": "https://tile.openstreetmap.be/osmbe-nl/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [ [ [5.47007, 49.49196], @@ -44173,7 +43942,7 @@ "name": "OpenStreetMap (Belgian Style - French)", "type": "tms", "template": "https://tile.openstreetmap.be/osmbe-fr/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [ [ [5.47007, 49.49196], @@ -44686,7 +44455,7 @@ "name": "OpenStreetMap (Standard)", "type": "tms", "template": "https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "terms_url": "https://www.openstreetmap.org", "terms_text": "© OpenStreetMap contributors, CC-BY-SA", "default": true, @@ -44698,7 +44467,7 @@ "name": "OpenStreetMap GPS traces", "type": "tms", "template": "https://{switch:a,b,c}.gps-tile.openstreetmap.org/lines/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "terms_url": "https://www.openstreetmap.org/copyright", "terms_text": "© OpenStreetMap contributors", "terms_html": "GPS Direction: © OpenStreetMap contributors.", @@ -44711,7 +44480,7 @@ "name": "openstreetmap.hu orthophotos", "type": "tms", "template": "http://adam.openstreetmap.hu/mapproxy/tiles/1.0.0/openstreetmap.hu.orthophotos/mercator/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [18.8577, 47.44553], @@ -44917,7 +44686,7 @@ "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/ortho_2010/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", "endDate": "2010-07-02T00:00:00.000Z", "startDate": "2010-06-24T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -45159,7 +44928,7 @@ "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/ortho_2013/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", "endDate": "2013-07-20T00:00:00.000Z", "startDate": "2013-07-19T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -45401,7 +45170,7 @@ "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/ortho_2016/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", "endDate": "2016-08-16T00:00:00.000Z", "startDate": "2013-08-30T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -45643,7 +45412,7 @@ "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/ortho_2017/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", "endDate": "2017-06-22T00:00:00.000Z", "startDate": "2017-06-14T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -45934,7 +45703,7 @@ "projection": "EPSG:3857", "endDate": "2006-01-01T00:00:00.000Z", "startDate": "2004-01-01T00:00:00.000Z", - "scaleExtent": [14, 19], + "zoomExtent": [14, 19], "polygon": [ [ [-7.31278, 36.98391], @@ -46006,7 +45775,7 @@ "projection": "EPSG:3857", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [1, 20], + "zoomExtent": [1, 20], "polygon": [ [ [-7.38968, 37.19205], @@ -46099,7 +45868,7 @@ "name": "OS 1:25k historic (OSM)", "type": "tms", "template": "https://ooc.openstreetmap.org/os1/{zoom}/{x}/{y}.jpg", - "scaleExtent": [6, 17], + "zoomExtent": [6, 17], "polygon": [ [ [-6.45854, 49.90441], @@ -46395,7 +46164,7 @@ "name": "OS New Popular Edition historic", "type": "tms", "template": "https://ooc.openstreetmap.org/npe/{zoom}/{x}/{y}.png", - "scaleExtent": [6, 15], + "zoomExtent": [6, 15], "polygon": [ [ [-3.68466, 55.23744], @@ -46496,7 +46265,7 @@ "name": "OS OpenData Locator", "type": "tms", "template": "http://tiles.itoworld.com/os_locator/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [-0.88639, 61.11666], @@ -46531,7 +46300,7 @@ "name": "OS OpenData StreetView", "type": "tms", "template": "https://{switch:a,b,c}.os.openstreetmap.org/sv/{zoom}/{x}/{y}.png", - "scaleExtent": [1, 18], + "zoomExtent": [1, 18], "polygon": [ [ [-5.82929, 50.02297], @@ -46829,7 +46598,7 @@ "name": "OS Scottish Popular historic", "type": "tms", "template": "https://ooc.openstreetmap.org/npescotland/{zoom}/{x}/{y}.jpg", - "scaleExtent": [6, 15], + "zoomExtent": [6, 15], "polygon": [ [ [-6.37546, 57.1045], @@ -47125,7 +46894,7 @@ "name": "OSMIE Baronies", "type": "tms", "template": "https://tile.openstreetmap.ie/barony/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [-6.32987, 52.38838], @@ -47219,7 +46988,7 @@ "name": "OSMIE Civil Parishes", "type": "tms", "template": "https://tile.openstreetmap.ie/civilparish/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [-6.32987, 52.38838], @@ -47313,7 +47082,7 @@ "name": "OSMIE Dail Election Areas", "type": "tms", "template": "https://tile.openstreetmap.ie/dail_ea/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [-6.32987, 52.38838], @@ -47407,7 +47176,7 @@ "name": "OSMIE EDs", "type": "tms", "template": "https://tile.openstreetmap.ie/ed/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [-6.32987, 52.38838], @@ -47501,7 +47270,7 @@ "name": "OSMIE Local Election Areas", "type": "tms", "template": "https://tile.openstreetmap.ie/local_ea/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [-6.32987, 52.38838], @@ -47595,7 +47364,7 @@ "name": "OSMIE T.ie Land not in Counties", "type": "tms", "template": "https://www.townlands.ie/tiles/not_counties/{zoom}/{x}/{y}.png", - "scaleExtent": [2, 19], + "zoomExtent": [2, 19], "polygon": [ [ [-6.32987, 52.38838], @@ -47689,7 +47458,7 @@ "name": "OSMIE Townlands", "type": "tms", "template": "https://tile.openstreetmap.ie/townland/{zoom}/{x}/{y}.png", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [-6.32987, 52.38838], @@ -47920,7 +47689,7 @@ "name": "Pangasinán/Bulacan (Philippines HiRes)", "type": "tms", "template": "https://gravitystorm.dev.openstreetmap.org/imagery/philippines/{zoom}/{x}/{y}.png", - "scaleExtent": [12, 19], + "zoomExtent": [12, 19], "polygon": [ [ [120.33659, 15.98577], @@ -48517,7 +48286,7 @@ "name": "PDOK aerial imagery Beeldmateriaal.nl 25cm latest", "type": "tms", "template": "https://geodata.nationaalgeoregister.nl/luchtfoto/rgb/wmts?FORMAT=image/jpeg&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=Actueel_ortho25&STYLE=&FORMAT=image/jpeg&tileMatrixSet=OGC:1.0:GoogleMapsCompatible&tileMatrix={zoom}&tileRow={y}&tileCol={x}", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [3.14377, 51.35984], @@ -48661,7 +48430,7 @@ "name": "PNOA Spain", "type": "tms", "template": "https://www.ign.es/wmts/pnoa-ma?request=GetTile&service=WMTS&VERSION=1.0.0&Layer=OI.OrthoimageCoverage&Style=default&Format=image/png&TileMatrixSet=GoogleMapsCompatible&TileMatrix={zoom}&TileRow={y}&TileCol={x}", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-17.88463, 28.44606], @@ -49352,7 +49121,7 @@ "type": "wms", "template": "http://wms.epodgik.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=budynki&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [15.9751, 54.37092], @@ -49464,7 +49233,7 @@ "type": "wms", "template": "https://debica.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=budynki,adresy,EBU,EBT,S&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [21.53768, 50.01085], @@ -49497,7 +49266,7 @@ "type": "wms", "template": "https://lancut.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=EBT,budynki,adresy&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [22.17224, 50.10121], @@ -49537,7 +49306,7 @@ "type": "wms", "template": "https://lubaczow.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=S,EBT,adresy,budynki_ewid&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [23.29224, 50.09693], @@ -49612,7 +49381,7 @@ "type": "wms", "template": "https://spropczyce.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=budynki&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [21.60041, 50.21025], @@ -49646,7 +49415,7 @@ "type": "wms", "template": "https://powiatrzeszowski.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=budynki,EBT,EBU&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [22.18219, 50.17339], @@ -49684,7 +49453,7 @@ "type": "wms", "template": "https://powiatrzeszowski.geoportal2.pl/map/wms/wms.php?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=ortofotomapa&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [22.18219, 50.17339], @@ -49722,7 +49491,7 @@ "type": "wms", "template": "https://stalowawola.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=EBT,adresy,budynki,centroidy&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [22.017, 50.35811], @@ -49759,7 +49528,7 @@ "projection": "EPSG:3857", "endDate": "2018-01-01T00:00:00.000Z", "startDate": "2018-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [16.72794, 52.48838], @@ -49799,7 +49568,7 @@ "projection": "EPSG:3857", "endDate": "2014-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [16.72794, 52.48838], @@ -49839,7 +49608,7 @@ "projection": "EPSG:3857", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [16.72794, 52.48838], @@ -49877,7 +49646,7 @@ "type": "wms", "template": "http://giswa1.mag.mepnet.cz/arcgis/services/MAP/letecke_snimky_posledni_snimkovani_cache/MapServer/WmsServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=0&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [1, 20], + "zoomExtent": [1, 20], "polygon": [ [ [14.81232, 49.93089], @@ -49894,7 +49663,7 @@ "type": "wms", "template": "http://giswa1.mag.mepnet.cz/arcgis/services/MAP/mimovegetacni_snimkovani_cache/MapServer/WmsServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=0&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [1, 20], + "zoomExtent": [1, 20], "polygon": [ [ [14.30454, 49.99538], @@ -49945,7 +49714,7 @@ "type": "wms", "template": "http://przemysl.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=adresy,budynki&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [22.72268, 49.76885], @@ -49986,7 +49755,7 @@ "type": "wms", "template": "http://przemysl.geoportal2.pl/map/wms/wms.php?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=ortofotomapa&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [22.72268, 49.76885], @@ -50026,7 +49795,7 @@ "name": "RABA-KGZ: Slovenia built-up areas", "type": "tms", "template": "http://wms.openstreetmap.de/tms/RABA3000/{zoom}/{x}/{y}.png", - "scaleExtent": [8, 19], + "zoomExtent": [8, 19], "polygon": [ [ [15.17101, 45.41273], @@ -50218,7 +49987,7 @@ "name": "RABA-KGZ: Slovenia farmland use", "type": "tms", "template": "http://wms.openstreetmap.de/tms/RABA/{zoom}/{x}/{y}.png", - "scaleExtent": [8, 19], + "zoomExtent": [8, 19], "polygon": [ [ [15.17101, 45.41273], @@ -50411,7 +50180,7 @@ "type": "wms", "template": "https://kart.ra.no/arcgis/services/Distribusjon/Kulturminner/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=3,6&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [13, 22], + "zoomExtent": [13, 22], "polygon": [ [ [-10.95725, 71.60964], @@ -50640,7 +50409,7 @@ "projection": "EPSG:3857", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [0, 13], + "zoomExtent": [0, 13], "polygon": [ [ [-42.30363, -22.43698], @@ -50658,7 +50427,7 @@ "name": "Route 500", "type": "tms", "template": "https://{switch:a,b,c}.tile.openstreetmap.fr/route500/{zoom}/{x}/{y}.png", - "scaleExtent": [12, 20], + "zoomExtent": [12, 20], "polygon": [ [ [-2.7, 43.9], @@ -51238,7 +51007,7 @@ "type": "wms", "template": "https://rudaslaska.geoportal2.pl/map/wmsorto/wms.php?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=ortofotomapa&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [18.84294, 50.32508], @@ -51298,7 +51067,7 @@ "type": "wms", "template": "http://wms.erzeszow.pl/?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=komunikacja,budynki,adresy&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [22.09538, 50.08157], @@ -51346,7 +51115,7 @@ "type": "wms", "template": "http://wms.erzeszow.pl/?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=rastry&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [22.09538, 50.08157], @@ -51395,7 +51164,7 @@ "template": "http://imagery-pr-usace-2013.s3-website-us-east-1.amazonaws.com/tiles/{zoom}/{x}/{y}.jpg", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-66.15007, 18.35002], @@ -52338,7 +52107,7 @@ "name": "SDFE aerial imagery", "type": "tms", "template": "https://osmtools.septima.dk/mapproxy/tiles/1.0.0/kortforsyningen_ortoforaar/EPSG3857/{zoom}/{x}/{y}.jpeg", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [8.37439, 54.95517], @@ -52517,7 +52286,7 @@ "type": "wms", "template": "https://kortforsyningen.kms.dk/cp_inspire?login=OpenStreetMapDK2015&password=Gall4Peters&FORMAT=image/png&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=CP.CadastralParcel&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [8.37439, 54.95517], @@ -52694,7 +52463,7 @@ "type": "wms", "template": "https://kortforsyningen.kms.dk/topo25?FORMAT=image/png&VERSION=1.1.1&login=OpenStreetMapDK2015&password=Gall4Peters&SERVICE=WMS&REQUEST=GetMap&Layers=topo25_klassisk&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [8.37439, 54.95517], @@ -52871,7 +52640,7 @@ "type": "wms", "template": "https://kortforsyningen.kms.dk/topo_skaermkort?FORMAT=image/png&VERSION=1.1.1&login=OpenStreetMapDK2015&password=Gall4Peters&SERVICE=WMS&REQUEST=GetMap&Layers=dtk_skaermkort&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [8.37439, 54.95517], @@ -53048,7 +52817,7 @@ "type": "wms", "template": "https://kortforsyningen.kms.dk/dhm?login=OpenStreetMapDK2015&password=Gall4Peters&FORMAT=image/png&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=dhm_overflade_skyggekort&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [8.37439, 54.95517], @@ -53225,7 +52994,7 @@ "type": "wms", "template": "https://kortforsyningen.kms.dk/dhm?login=OpenStreetMapDK2015&password=Gall4Peters&FORMAT=image/png&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=dhm_terraen_skyggekort&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [8.37439, 54.95517], @@ -53426,7 +53195,7 @@ "type": "wms", "template": "https://siemianowice.geoportal2.pl/map/wms/wms.php?FORMAT=image/png&transparent=true&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=budynki,drogi,adresy,ulice&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [19.03103, 50.33933], @@ -53459,7 +53228,7 @@ "type": "wms", "template": "https://siemianowice.geoportal2.pl/map/wms/wms.php?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=ortofotomapa&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:4326", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [19.03103, 50.33933], @@ -53805,7 +53574,7 @@ "template": "http://osmdata.asitvd.ch/tiles/sigip2012/{zoom}/{x}/{y}.png", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [6.71057, 46.54396], @@ -53833,7 +53602,7 @@ "name": "Slovakia Historic Maps", "type": "tms", "template": "http://tms.freemap.sk/historicke/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 12], + "zoomExtent": [0, 12], "polygon": [ [ [16.81969, 47.49272], @@ -53850,7 +53619,7 @@ "name": "Snowmobile map Sweden", "type": "tms", "template": "https://tiles.skoterleder.org/tiles/{zoom}/{x}/{y}.png", - "scaleExtent": [5, 14], + "zoomExtent": [5, 14], "polygon": [ [ [12.80182, 55.19612], @@ -53888,7 +53657,7 @@ "template": "http://adam.openstreetmap.hu/mapproxy/tiles/1.0.0/Soskut-Tarnok-Pusztazamor-Diosd/mercator/{zoom}/{x}/{y}.png", "endDate": "2017-03-01T00:00:00.000Z", "startDate": "2017-03-01T00:00:00.000Z", - "scaleExtent": [11, 20], + "zoomExtent": [11, 20], "polygon": [ [ [18.79273, 47.37079], @@ -54016,7 +53785,7 @@ "name": "South Africa CD:NGI Aerial", "type": "tms", "template": "http://{switch:a,b,c}.aerial.openstreetmap.org.za/ngi-aerial/{zoom}/{x}/{y}.jpg", - "scaleExtent": [1, 22], + "zoomExtent": [1, 22], "polygon": [ [ [17.83968, -32.79834], @@ -54326,7 +54095,7 @@ "template": "https://geoservices.buergernetz.bz.it/geoserver/gwc/service/wmts/?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=P_BZ_OF_2011_EPSG3857&STYLE=default&TILEMATRIXSET=GoogleMapsCompatible&TILEMATRIX=GoogleMapsCompatible%3A{zoom}&TILEROW={y}&TILECOL={x}&FORMAT=image%2Fjpeg", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2011-01-01T00:00:00.000Z", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [ [ [10.38615, 46.68821], @@ -55295,299 +55064,272 @@ "template": "https://geoservices.buergernetz.bz.it/geoserver/gwc/service/wmts/?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=P_BZ_OF_2014_EPSG3857&STYLE=default&TILEMATRIXSET=GoogleMapsCompatible&TILEMATRIX=GoogleMapsCompatible%3A{zoom}&TILEROW={y}&TILECOL={x}&FORMAT=image%2Fjpeg", "endDate": "2014-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [ [ - [12.23442, 46.84254], - [12.24913, 46.82785], - [12.24913, 46.81945], - [12.25753, 46.81735], - [12.26173, 46.80686], - [12.26594, 46.80686], - [12.26804, 46.79637], - [12.26173, 46.79427], - [12.27014, 46.78587], - [12.27224, 46.76908], - [12.28485, 46.76698], - [12.29745, 46.7439], - [12.30166, 46.7439], - [12.30166, 46.7376], - [12.28695, 46.7376], - [12.23652, 46.72081], - [12.18819, 46.71451], - [12.16298, 46.70192], - [12.10835, 46.69353], - [12.08944, 46.68303], - [12.05792, 46.68093], - [12.04531, 46.67464], - [12.03481, 46.67464], - [12.03061, 46.68303], - [12.0096, 46.68303], - [11.98438, 46.66834], - [11.94866, 46.66414], - [11.93816, 46.65575], - [11.90874, 46.64525], - [11.89193, 46.64525], - [11.8457, 46.62846], - [11.82049, 46.62636], - [11.80998, 46.60748], - [11.79528, 46.60328], - [11.78897, 46.59069], - [11.78057, 46.58859], - [11.76586, 46.55501], - [11.75325, 46.54871], - [11.73855, 46.52982], - [11.73224, 46.52982], - [11.72384, 46.51513], - [11.71543, 46.51513], - [11.71333, 46.50254], - [11.70913, 46.50254], - [11.70913, 46.49624], - [11.69652, 46.48575], - [11.69442, 46.47735], - [11.66711, 46.47525], - [11.66711, 46.44587], - [11.6608, 46.44587], - [11.64399, 46.45846], - [11.63769, 46.45846], - [11.63349, 46.44587], - [11.62508, 46.44797], + [11.71495, 46.51227], + [11.69889, 46.50218], + [11.6672, 46.49647], + [11.64515, 46.49743], + [11.63849, 46.50051], + [11.63495, 46.49486], + [11.64297, 46.49346], + [11.65174, 46.48271], + [11.64536, 46.47189], + [11.64179, 46.47439], + [11.62679, 46.4708], + [11.62987, 46.46377], + [11.61882, 46.44325], + [11.61936, 46.43957], [11.62508, 46.43957], - [11.61878, 46.43957], - [11.61458, 46.42278], - [11.61037, 46.42278], - [11.61037, 46.41649], - [11.62508, 46.41229], - [11.62508, 46.3934], - [11.61458, 46.38291], - [11.60617, 46.38291], - [11.60407, 46.37241], - [11.58306, 46.37241], - [11.58306, 46.34932], - [11.55995, 46.32414], - [11.53894, 46.32834], - [11.53894, 46.34513], - [11.52423, 46.34723], - [11.50111, 46.34723], - [11.49901, 46.32834], - [11.48851, 46.33044], - [11.49271, 46.32204], - [11.41917, 46.32414], - [11.41707, 46.25278], - [11.35613, 46.24858], - [11.34773, 46.25698], - [11.34563, 46.26747], - [11.34143, 46.26747], - [11.33722, 46.25068], - [11.31201, 46.24858], - [11.30991, 46.25278], - [11.3015, 46.25278], - [11.2889, 46.24019], - [11.2889, 46.2234], - [11.25108, 46.2234], - [11.24898, 46.2129], - [11.16283, 46.215], - [11.16283, 46.24858], - [11.12711, 46.24858], - [11.13131, 46.25278], - [11.12291, 46.25488], - [11.12501, 46.29895], - [11.16283, 46.29895], - [11.16283, 46.36821], - [11.16703, 46.36821], - [11.16703, 46.38081], - [11.17543, 46.3871], - [11.17754, 46.3997], - [11.20485, 46.3997], - [11.20695, 46.42068], - [11.19014, 46.42068], - [11.19014, 46.42698], - [11.19435, 46.42698], - [11.19855, 46.44167], - [11.20485, 46.44587], - [11.20695, 46.47315], - [11.19014, 46.47525], - [11.18804, 46.48365], - [11.15232, 46.47525], - [11.08298, 46.47525], - [11.08088, 46.43537], - [11.04096, 46.42068], - [11.01154, 46.42068], - [10.99894, 46.44587], - [10.98843, 46.44587], - [10.98423, 46.45217], - [10.96952, 46.45217], - [10.96112, 46.48365], - [10.95691, 46.48365], - [10.95481, 46.49414], - [10.94851, 46.49624], - [10.94431, 46.50673], - [10.91069, 46.49624], - [10.90018, 46.49624], - [10.89808, 46.51093], - [10.85606, 46.50254], - [10.84135, 46.54241], - [10.86446, 46.54661], - [10.86236, 46.55081], - [10.76571, 46.54031], - [10.72158, 46.55081], - [10.59762, 46.55291], - [10.5619, 46.54871], - [10.53668, 46.55291], - [10.50096, 46.54871], - [10.48415, 46.55501], - [10.46314, 46.55501], - [10.47155, 46.5676], - [10.47365, 46.59069], - [10.47785, 46.59069], - [10.47785, 46.61797], - [10.46944, 46.62636], - [10.44843, 46.63476], - [10.42532, 46.62846], - [10.3938, 46.63056], - [10.3896, 46.64525], - [10.3812, 46.64735], - [10.3833, 46.66414], - [10.3791, 46.66834], - [10.3791, 46.69353], - [10.3854, 46.72081], - [10.3896, 46.72081], - [10.3938, 46.7376], - [10.41692, 46.75229], - [10.41692, 46.78587], - [10.42112, 46.79847], - [10.42532, 46.79847], - [10.42532, 46.80686], - [10.43373, 46.81106], - [10.43793, 46.79007], - [10.44423, 46.78377], - [10.44843, 46.7439], - [10.45264, 46.7439], - [10.45684, 46.72711], - [10.46104, 46.72711], - [10.46524, 46.71032], - [10.46944, 46.71032], - [10.47365, 46.67254], - [10.47995, 46.67044], - [10.48415, 46.65575], - [10.48836, 46.65575], - [10.48415, 46.62846], - [10.48836, 46.62846], - [10.48836, 46.62217], - [10.49886, 46.62007], - [10.52828, 46.62427], - [10.49886, 46.63056], - [10.49886, 46.64525], - [10.50516, 46.64316], - [10.49886, 46.66204], - [10.51777, 46.65785], - [10.58921, 46.66204], - [10.63123, 46.65365], - [10.79302, 46.65575], - [10.79723, 46.65155], - [10.88127, 46.65155], - [10.89178, 46.65785], - [10.9275, 46.66414], - [10.94431, 46.67464], - [10.98003, 46.67464], - [11.00104, 46.68303], - [11.02415, 46.68303], - [11.05147, 46.69772], - [11.12711, 46.70822], - [11.13971, 46.71242], - [11.14392, 46.71871], - [11.17543, 46.72081], - [11.19855, 46.7334], - [11.24898, 46.7355], - [11.25318, 46.72291], - [11.2952, 46.72921], - [11.30571, 46.70822], - [11.31831, 46.70822], - [11.33722, 46.71871], - [11.38555, 46.72081], - [11.43178, 46.7376], - [11.50952, 46.7439], - [11.54734, 46.75649], - [11.56835, 46.75649], - [11.58306, 46.76698], - [11.60617, 46.76908], - [11.61037, 46.77538], - [11.69862, 46.79427], - [11.71753, 46.79427], - [11.72174, 46.79007], - [11.73855, 46.75859], - [11.74905, 46.75019], - [11.76166, 46.70192], - [11.76586, 46.70192], - [11.78687, 46.71661], - [11.8331, 46.72291], - [11.86041, 46.7376], - [11.90874, 46.7439], - [11.92135, 46.75229], - [11.95286, 46.75859], - [11.95917, 46.76488], - [11.99489, 46.77538], - [12.00749, 46.77538], - [12.0096, 46.77958], - [12.03061, 46.78168], - [12.05792, 46.79427], - [12.09995, 46.79847], - [12.13566, 46.81316], - [12.15878, 46.81316], - [12.18609, 46.82785], - [12.21551, 46.83205], - [12.23442, 46.84254] + [11.62508, 46.44797], + [11.63349, 46.44587], + [11.63769, 46.45846], + [11.64399, 46.45846], + [11.6608, 46.44587], + [11.66711, 46.44587], + [11.66711, 46.47525], + [11.69442, 46.47735], + [11.69652, 46.48575], + [11.70913, 46.49624], + [11.70913, 46.50254], + [11.71333, 46.50254], + [11.71495, 46.51227] ], [ - [11.97388, 46.96637], - [11.99489, 46.96007], - [11.99489, 46.95587], - [12.0096, 46.95797], - [12.0117, 46.94748], - [12.0159, 46.94748], - [12.0117, 46.94118], - [12.018, 46.93908], - [12.02851, 46.92229], - [12.00749, 46.9118], - [11.97388, 46.9055], - [11.95707, 46.89501], - [11.91714, 46.88871], - [11.85831, 46.86982], - [11.8394, 46.86982], - [11.80158, 46.85303], - [11.76166, 46.84464], - [11.74905, 46.83415], - [11.68181, 46.81945], - [11.67971, 46.81526], - [11.6545, 46.81106], - [11.64399, 46.80476], - [11.63769, 46.80686], - [11.63139, 46.82155], - [11.62508, 46.82155], - [11.62298, 46.83205], - [11.61037, 46.84464], - [11.61668, 46.84884], - [11.61458, 46.85723], - [11.61037, 46.85723], - [11.61668, 46.86563], - [11.64609, 46.86563], - [11.68602, 46.88242], - [11.69022, 46.88871], - [11.74905, 46.89711], - [11.75325, 46.90341], - [11.78057, 46.9076], - [11.80158, 46.9202], - [11.8457, 46.92649], - [11.87722, 46.94118], - [11.89613, 46.94328], - [11.92135, 46.95797], - [11.96757, 46.96217], - [11.97388, 46.96637] + [11.61435, 46.41535], + [11.60161, 46.39731], + [11.60307, 46.38924], + [11.5932, 46.38265], + [11.56489, 46.38018], + [11.55878, 46.35076], + [11.55249, 46.34418], + [11.54423, 46.34483], + [11.53837, 46.35015], + [11.52445, 46.35502], + [11.47969, 46.36277], + [11.48052, 46.3551], + [11.46322, 46.34922], + [11.45556, 46.33396], + [11.42105, 46.32441], + [11.40517, 46.32387], + [11.39865, 46.31426], + [11.39994, 46.30709], + [11.39569, 46.3083], + [11.38188, 46.30052], + [11.36088, 46.29906], + [11.36078, 46.29682], + [11.38256, 46.29177], + [11.3871, 46.28143], + [11.39609, 46.27423], + [11.39862, 46.264], + [11.38756, 46.26029], + [11.37347, 46.2629], + [11.36836, 46.26135], + [11.35783, 46.26481], + [11.35495, 46.27564], + [11.33912, 46.28306], + [11.33379, 46.29049], + [11.33471, 46.2962], + [11.3129, 46.28256], + [11.31737, 46.27303], + [11.30645, 46.25786], + [11.29124, 46.2604], + [11.24743, 46.22933], + [11.20622, 46.2187], + [11.18267, 46.22496], + [11.17077, 46.23806], + [11.17994, 46.24434], + [11.18351, 46.25269], + [11.18935, 46.25354], + [11.19448, 46.2461], + [11.20029, 46.25566], + [11.16604, 46.26129], + [11.14885, 46.27904], + [11.13725, 46.28336], + [11.14293, 46.28934], + [11.15847, 46.29059], + [11.16439, 46.2986], + [11.1761, 46.30346], + [11.1847, 46.32104], + [11.18894, 46.32151], + [11.18696, 46.32673], + [11.1942, 46.33016], + [11.20204, 46.34212], + [11.19001, 46.35984], + [11.19263, 46.36578], + [11.20393, 46.36765], + [11.19792, 46.37232], + [11.21275, 46.39804], + [11.21345, 46.40675], + [11.20644, 46.4156], + [11.20485, 46.3997], + [11.17754, 46.3997], + [11.17543, 46.3871], + [11.16703, 46.38081], + [11.16703, 46.36821], + [11.16283, 46.36821], + [11.16283, 46.29895], + [11.12501, 46.29895], + [11.12291, 46.25488], + [11.13131, 46.25278], + [11.12711, 46.24858], + [11.16283, 46.24858], + [11.16283, 46.215], + [11.24898, 46.2129], + [11.25108, 46.2234], + [11.2889, 46.2234], + [11.2889, 46.24019], + [11.3015, 46.25278], + [11.30991, 46.25278], + [11.31201, 46.24858], + [11.33722, 46.25068], + [11.34143, 46.26747], + [11.34563, 46.26747], + [11.34773, 46.25698], + [11.35613, 46.24858], + [11.41707, 46.25278], + [11.41917, 46.32414], + [11.49271, 46.32204], + [11.48851, 46.33044], + [11.49901, 46.32834], + [11.50111, 46.34723], + [11.52423, 46.34723], + [11.53894, 46.34513], + [11.53894, 46.32834], + [11.55995, 46.32414], + [11.58306, 46.34932], + [11.58306, 46.37241], + [11.60407, 46.37241], + [11.60617, 46.38291], + [11.61458, 46.38291], + [11.62508, 46.3934], + [11.62508, 46.41229], + [11.61435, 46.41535] + ], + [ + [11.20663, 46.41745], + [11.21026, 46.4206], + [11.20347, 46.42682], + [11.21416, 46.43556], + [11.21634, 46.44255], + [11.20903, 46.45293], + [11.21419, 46.45807], + [11.21736, 46.45731], + [11.21886, 46.46199], + [11.21626, 46.47277], + [11.20939, 46.481], + [11.20876, 46.49346], + [11.19608, 46.50241], + [11.1924, 46.501], + [11.18686, 46.50734], + [11.18002, 46.49823], + [11.17014, 46.49635], + [11.16095, 46.4878], + [11.12934, 46.48058], + [11.1103, 46.49643], + [11.10449, 46.4948], + [11.08812, 46.50128], + [11.08173, 46.53021], + [11.05915, 46.51508], + [11.03795, 46.51357], + [11.05006, 46.50784], + [11.05773, 46.49235], + [11.06278, 46.4894], + [11.06894, 46.46619], + [11.07625, 46.45487], + [11.0778, 46.44569], + [11.07301, 46.44042], + [11.05394, 46.44849], + [11.0414, 46.44569], + [11.02817, 46.46116], + [11.00952, 46.46917], + [11.00462, 46.47607], + [10.98695, 46.48289], + [10.96543, 46.48103], + [10.96285, 46.47718], + [10.96952, 46.45217], + [10.98423, 46.45217], + [10.98843, 46.44587], + [10.99894, 46.44587], + [11.01154, 46.42068], + [11.04096, 46.42068], + [11.08088, 46.43537], + [11.08298, 46.47525], + [11.15232, 46.47525], + [11.18804, 46.48365], + [11.19014, 46.47525], + [11.20695, 46.47315], + [11.20485, 46.44587], + [11.19855, 46.44167], + [11.19435, 46.42698], + [11.19014, 46.42698], + [11.19014, 46.42068], + [11.20695, 46.42068], + [11.20663, 46.41745] + ], + [ + [10.47433, 46.55501], + [10.47617, 46.55749], + [10.47321, 46.56701], + [10.48305, 46.5777], + [10.48575, 46.58921], + [10.48221, 46.59199], + [10.48576, 46.59805], + [10.48291, 46.60512], + [10.49055, 46.61394], + [10.46952, 46.62628], + [10.47785, 46.61797], + [10.47785, 46.59069], + [10.47365, 46.59069], + [10.47155, 46.5676], + [10.46314, 46.55501], + [10.47433, 46.55501] + ], + [ + [10.46925, 46.62643], + [10.44632, 46.63989], + [10.40935, 46.63389], + [10.40011, 46.63648], + [10.39873, 46.6455], + [10.38946, 46.65862], + [10.39057, 46.67089], + [10.3803, 46.68399], + [10.38615, 46.68821], + [10.39201, 46.69016], + [10.40215, 46.70624], + [10.41274, 46.70821], + [10.41622, 46.71479], + [10.4168, 46.71847], + [10.39934, 46.73435], + [10.43464, 46.75356], + [10.44107, 46.75413], + [10.44011, 46.77149], + [10.42123, 46.78861], + [10.42845, 46.79755], + [10.43626, 46.79843], + [10.43373, 46.81106], + [10.42532, 46.80686], + [10.42532, 46.79847], + [10.42112, 46.79847], + [10.41692, 46.78587], + [10.41692, 46.75229], + [10.3938, 46.7376], + [10.3896, 46.72081], + [10.3854, 46.72081], + [10.3791, 46.69353], + [10.3791, 46.66834], + [10.3833, 46.66414], + [10.3812, 46.64735], + [10.3896, 46.64525], + [10.3938, 46.63056], + [10.42532, 46.62846], + [10.44843, 46.63476], + [10.46925, 46.62643] ] ], "terms_url": "https://geoservices.buergernetz.bz.it/geokatalog/", - "terms_text": "© Autonomen Provinz Bozen/Provincia Autonoma di Bolzano CC-BY 3.0", - "best": true + "terms_text": "© Autonomen Provinz Bozen/Provincia Autonoma di Bolzano CC-BY 3.0" }, { "id": "South-Tyrol-Orthofoto-2014-2015", @@ -55596,7 +55338,7 @@ "template": "https://geoservices.buergernetz.bz.it/geoserver/gwc/service/wmts/?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=P_BZ_OF_2014_2015_EPSG3857&STYLE=default&TILEMATRIXSET=GoogleMapsCompatible&TILEMATRIX=GoogleMapsCompatible%3A{zoom}&TILEROW={y}&TILECOL={x}&FORMAT=image%2Fjpeg", "endDate": "2015-11-01T00:00:00.000Z", "startDate": "2014-07-01T00:00:00.000Z", - "scaleExtent": [0, 18], + "zoomExtent": [0, 18], "polygon": [ [ [10.38615, 46.68821], @@ -56079,7 +55821,7 @@ "name": "South Tyrol Topomap", "type": "tms", "template": "https://geoservices.buergernetz.bz.it/geoserver/gwc/service/wmts/?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=P_BZ_BASEMAP_TOPO&STYLE=default&TILEMATRIXSET=GoogleMapsCompatible&TILEMATRIX=GoogleMapsCompatible%3A{zoom}&TILEROW={y}&TILECOL={x}&FORMAT=image%2Fjpeg", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [10.38615, 46.68821], @@ -57519,7 +57261,7 @@ "type": "wms", "template": "https://ogc.ssb.no/wms.ashx?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=layer_193&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [31.90425, 70.43681], @@ -57562,7 +57304,7 @@ "type": "wms", "template": "https://ogc.ssb.no/wms.ashx?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=layer_198&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [31.90425, 70.43681], @@ -57606,7 +57348,7 @@ "template": "https://mapproxy.osm.ch/tiles/bern2016/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2016-01-01T00:00:00.000Z", "startDate": "2016-01-01T00:00:00.000Z", - "scaleExtent": [8, 21], + "zoomExtent": [8, 21], "polygon": [ [ [7.29431, 46.92376], @@ -57709,7 +57451,7 @@ "template": "https://mapproxy.osm.ch/tiles/bern2012/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [14, 19], + "zoomExtent": [14, 19], "polygon": [ [ [7.3807, 47.00952], @@ -57780,7 +57522,7 @@ "template": "https://mapproxy.osm.ch/tiles/uster/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2008-01-01T00:00:00.000Z", "startDate": "2008-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [8.68, 47.32], @@ -57799,7 +57541,7 @@ "template": "https://mapproxy.osm.ch/tiles/zh_luftbild2011/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2011-01-01T00:00:00.000Z", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [8.44624, 47.44143], @@ -57816,7 +57558,7 @@ "name": "Stadtplan Zürich", "type": "tms", "template": "https://mapproxy.osm.ch/tiles/zh_stadtplan/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [8.56681, 47.34713], @@ -58302,7 +58044,7 @@ "name": "Stamen Terrain", "type": "tms", "template": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/terrain-background/{zoom}/{x}/{y}.jpg", - "scaleExtent": [4, 18], + "zoomExtent": [4, 18], "terms_url": "http://maps.stamen.com/#terrain", "terms_text": "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL", "icon": "https://stamen.com/wp-content/uploads/2016/07/stamen_compass_rose_small-01.png" @@ -58314,7 +58056,7 @@ "template": "https://{switch:a,b,c}.surrey.aerial.openstreetmap.org.uk/layer/gb_surrey_aerial/{zoom}/{x}/{y}.png", "endDate": "2009-01-01T00:00:00.000Z", "startDate": "2007-01-01T00:00:00.000Z", - "scaleExtent": [8, 21], + "zoomExtent": [8, 21], "polygon": [ [ [-0.75248, 51.08219], @@ -58447,7 +58189,7 @@ "projection": "EPSG:4326", "endDate": "2008-01-01T00:00:00.000Z", "startDate": "2008-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [18.92818, 50.32151], @@ -58496,7 +58238,7 @@ "projection": "EPSG:4326", "endDate": "2009-01-01T00:00:00.000Z", "startDate": "2009-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [18.92818, 50.32151], @@ -58545,7 +58287,7 @@ "projection": "EPSG:4326", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [18.92818, 50.32151], @@ -58593,7 +58335,7 @@ "template": "http://e.tile.openstreetmap.hu/szeged-2011-10cm/{zoom}/{x}/{y}.png", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2011-01-01T00:00:00.000Z", - "scaleExtent": [10, 22], + "zoomExtent": [10, 22], "polygon": [ [ [20.14599, 46.22811], @@ -58661,7 +58403,7 @@ "name": "Taiwan e-Map Open Data", "type": "tms", "template": "https://wmts.nlsc.gov.tw/wmts/EMAP6_OPENDATA/default/GoogleMapsCompatible/{zoom}/{y}/{x}", - "scaleExtent": [0, 15], + "zoomExtent": [0, 15], "polygon": [ [ [120.45706, 26.39706], @@ -59110,7 +58852,7 @@ "id": "TEClines", "name": "TEC bus lines", "type": "wms", - "template": "https://geodata.tec-wl.be/arcgis/rest/services/Lignes/MapServer/export?f=image&format=png8&transparent=True&SRS={proj}&bboxSR=3857&imageSR=3857&bbox={bbox}&size={width},{height}", + "template": "https://geodata.tec-wl.be/arcgis/services/Lignes/MapServer/WMSServer?FORMAT=image/png&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=0&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", "polygon": [ [ @@ -59169,7 +58911,7 @@ "id": "TECstops", "name": "TEC bus stops", "type": "wms", - "template": "https://geodata.tec-wl.be/arcgis/rest/services/Poteaux/MapServer/export?f=image&format=png8&transparent=True&SRS={proj}&bboxSR=3857&imageSR=3857&bbox={bbox}&size={width},{height}", + "template": "https://geodata.tec-wl.be/arcgis/services/Poteaux/MapServer/WMSServer?FORMAT=image/png&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=0&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", "polygon": [ [ @@ -59277,7 +59019,7 @@ "type": "tms", "template": "https://txgi.tnris.org/login/path/ecology-fiona-poem-romeo/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=texas&STYLE=&FORMAT=image/png&tileMatrixSet=0to20&tileMatrix=0to20:{zoom}&tileRow={y}&tileCol={x}", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-99.99854, 34.56018], @@ -59317,7 +59059,7 @@ "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/styles/v1/openstreetmapus/cj8dftc3q1ecn2tnx9qhwyj0c/tiles/256/{zoom}/{x}/{y}?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcHVzIiwiYSI6ImNpcnF4Ym43dDBoOXZmYW04bWhlNWdrY2EifQ.4SFexuTUuKkZeerO3dgtmw", "endDate": "2017-01-01T00:00:00.000Z", "startDate": "2017-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [-124.76179, 48.41301], @@ -59464,7 +59206,7 @@ "type": "wms", "template": "https://gis.tirol.gv.at/arcgis/services/Service_Public/terrain/MapServer/WmsServer?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Hoehenschichtlinien 20m&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [15, 22], + "zoomExtent": [15, 22], "polygon": [ [ [10.43998, 47.59768], @@ -60682,7 +60424,7 @@ "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/topo/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.png", "endDate": "2010-07-20T00:00:00.000Z", "startDate": "2013-07-19T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [5.96175, 50.17631], @@ -61262,7 +61004,7 @@ "projection": "EPSG:3857", "endDate": "2007-01-01T00:00:00.000Z", "startDate": "2007-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [1.192, 43.63288], @@ -61359,7 +61101,7 @@ "projection": "EPSG:3857", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2011-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [1.11351, 43.68676], @@ -61430,7 +61172,7 @@ "projection": "EPSG:3857", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [1.11351, 43.68676], @@ -61501,7 +61243,7 @@ "projection": "EPSG:3857", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [1.11351, 43.68676], @@ -61571,7 +61313,7 @@ "template": "http://wms.openstreetmap.fr/tms/1.0.0/tours/{zoom}/{x}/{y}", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2008-01-01T00:00:00.000Z", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [0.54575, 47.46526], @@ -61733,7 +61475,7 @@ "template": "http://wms.openstreetmap.fr/tms/1.0.0/tours_2013/{zoom}/{x}/{y}", "endDate": "2013-01-01T00:00:00.000Z", "startDate": "2013-01-01T00:00:00.000Z", - "scaleExtent": [0, 22], + "zoomExtent": [0, 22], "polygon": [ [ [0.77512, 47.32983], @@ -61858,7 +61600,7 @@ "type": "wms", "template": "http://geo-baninfo.trafikverket.se/mapservice/wms.axd/BanInfo?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Spar_Huvud_och_sidospar&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [7, 20], + "zoomExtent": [7, 20], "polygon": [ [ [12.80182, 55.19612], @@ -61894,7 +61636,7 @@ "type": "wms", "template": "https://geo-netinfo.trafikverket.se/mapservice/wms.axd/NetInfo?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Vagtrafiknat,Funkvagklass,Farjeled&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [13, 20], + "zoomExtent": [13, 20], "polygon": [ [ [12.80182, 55.19612], @@ -61930,7 +61672,7 @@ "type": "wms", "template": "https://geo-netinfo.trafikverket.se/mapservice/wms.axd/NetInfo?FORMAT=image/png&TRANSPARENT=TRUE&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Vagnummer,Vaghinder,Rastplats,Rastficka,Hallplats,Farthinder,BroTunnel,ATK_Matplats&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", "projection": "EPSG:3857", - "scaleExtent": [3, 20], + "zoomExtent": [3, 20], "polygon": [ [ [12.80182, 55.19612], @@ -61965,7 +61707,7 @@ "name": "Trafikverket Street Names", "type": "tms", "template": "https://mapproxy.openstreetmap.se/tiles/1.0.0/nvdb_names/EPSG3857/{zoom}/{x}/{y}.png", - "scaleExtent": [15, 19], + "zoomExtent": [15, 19], "polygon": [ [ [12.80182, 55.19612], @@ -62000,7 +61742,7 @@ "name": "U.S. Forest Roads Overlay", "type": "tms", "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/styles/v1/glassman/cjf4qjmps0tgv2qpahj977mvz/tiles/256/{zoom}/{x}/{y}?access_token=pk.eyJ1IjoiZ2xhc3NtYW4iLCJhIjoiRjk3dWdwYyJ9.Tg_fMJWxygeKBgVTrZHmGA", - "scaleExtent": [0, 20], + "zoomExtent": [0, 20], "polygon": [ [ [-124.76179, 48.41301], @@ -62135,7 +61877,7 @@ "name": "U.S. Forest Service roads", "type": "tms", "template": "https://osm.cycle.travel/forest/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-124.76179, 48.41301], @@ -62267,7 +62009,7 @@ "name": "Übersichtsplan Zürich", "type": "tms", "template": "https://mapproxy.osm.ch/tiles/zh_uebersichtsplan/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", - "scaleExtent": [0, 21], + "zoomExtent": [0, 21], "polygon": [ [ [8.45788, 47.44582], @@ -62300,7 +62042,7 @@ "template": "http://map.land.gov.ua/map/ortho_kiev/{zoom}/{x}/{-y}.jpg", "endDate": "2014-01-01T00:00:00.000Z", "startDate": "2014-01-01T00:00:00.000Z", - "scaleExtent": [0, 16], + "zoomExtent": [0, 16], "polygon": [ [ [30.30752, 50.57184], @@ -62474,7 +62216,7 @@ "template": "http://212.26.144.110/tile2/orto_10000/{zoom}/{x}/{-y}.jpg", "endDate": "2012-01-01T00:00:00.000Z", "startDate": "2012-01-01T00:00:00.000Z", - "scaleExtent": [0, 16], + "zoomExtent": [0, 16], "polygon": [ [ [23.6193, 51.65491], @@ -63554,7 +63296,7 @@ "name": "USGS Large Scale Imagery", "type": "tms", "template": "http://{switch:a,b,c}.tile.openstreetmap.us/usgs_large_scale/{zoom}/{x}/{y}.jpg", - "scaleExtent": [12, 20], + "zoomExtent": [12, 20], "polygon": [ [ [-123.25493, 48.7529], @@ -64618,7 +64360,7 @@ "name": "USGS Topographic Maps", "type": "tms", "template": "https://caltopo.s3.amazonaws.com/topo/{zoom}/{x}/{y}.png", - "scaleExtent": [0, 16], + "zoomExtent": [0, 16], "polygon": [ [ [-55.99594, 52.00107], @@ -64788,7 +64530,7 @@ "name": "Vector Streetmap for San Juan County WA", "type": "tms", "template": "https://sjcgis.org/arcgis/rest/services/Basemaps/General_Basemap_WM/MapServer/tile/{zoom}/{y}/{x}", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [-123.27402, 48.69297], @@ -64814,7 +64556,7 @@ "template": "http://wms.openstreetmap.fr/tms/1.0.0/PNRVercors-RHP-1999/{zoom}/{x}/{y}", "endDate": "1999-01-01T00:00:00.000Z", "startDate": "1999-01-01T00:00:00.000Z", - "scaleExtent": [0, 19], + "zoomExtent": [0, 19], "polygon": [ [ [5.43565, 44.99918], @@ -64915,7 +64657,7 @@ "name": "Vienna: Beschriftungen (annotations)", "type": "tms", "template": "https://maps.wien.gv.at/wmts/beschriftung/normal/google3857/{zoom}/{y}/{x}.png", - "scaleExtent": [12, 19], + "zoomExtent": [12, 19], "polygon": [ [ [16.54475, 48.17286], @@ -65014,7 +64756,7 @@ "name": "Vienna: Mehrzweckkarte (general purpose)", "type": "tms", "template": "https://maps.wien.gv.at/wmts/fmzk/pastell/google3857/{zoom}/{y}/{x}.jpeg", - "scaleExtent": [10, 19], + "zoomExtent": [10, 19], "polygon": [ [ [16.54475, 48.17286], @@ -65112,7 +64854,7 @@ "name": "Vienna: Orthofoto (aerial image)", "type": "tms", "template": "https://maps.wien.gv.at/wmts/lb/farbe/google3857/{zoom}/{y}/{x}.jpeg", - "scaleExtent": [10, 19], + "zoomExtent": [10, 19], "polygon": [ [ [16.14995, 48.10832], @@ -65137,7 +64879,7 @@ "template": "http://osmdata.asitvd.ch/tiles/nyon2010/{zoom}/{x}/{y}.png", "endDate": "2010-01-01T00:00:00.000Z", "startDate": "2010-01-01T00:00:00.000Z", - "scaleExtent": [14, 20], + "zoomExtent": [14, 20], "polygon": [ [ [6.24859, 46.38252], @@ -65897,7 +65639,7 @@ "projection": "EPSG:3857", "endDate": "2015-01-01T00:00:00.000Z", "startDate": "2015-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [17.14217, 51.12846], @@ -66009,7 +65751,7 @@ "projection": "EPSG:4326", "endDate": "2011-01-01T00:00:00.000Z", "startDate": "2011-01-01T00:00:00.000Z", - "scaleExtent": [0, 23], + "zoomExtent": [0, 23], "polygon": [ [ [18.74396, 50.2753], diff --git a/data/update_imagery.js b/data/update_imagery.js index ca2e255ca..fbf604950 100644 --- a/data/update_imagery.js +++ b/data/update_imagery.js @@ -116,7 +116,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 ]; diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index 76eba00af..f580a6742 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -48,7 +48,7 @@ export function rendererBackgroundSource(data) { var best = !!source.best; var template = source.template; - source.scaleExtent = data.scaleExtent || [0, 22]; + source.zoomExtent = data.zoomExtent || [0, 22]; source.overzoom = data.overzoom !== false; @@ -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; diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index c30ee2c3e..d7d93bef3 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -271,7 +271,7 @@ export function rendererTileLayer(context) { if (!arguments.length) return _source; _source = _; _cache = {}; - tiler.scaleExtent(_source.scaleExtent); + tiler.zoomExtent(_source.zoomExtent); return background; }; diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index ddf72eabc..b73db8b71 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -28,7 +28,7 @@ var viewerjs = 'mapillary-js/mapillary.min.js'; var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; var maxResults = 1000; var tileZoom = 14; -var tiler = utilTiler().skipNullIsland(true); +var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true); var dispatch = d3_dispatch('loadedImages', 'loadedSigns'); var _mlyFallback = false; var _mlyCache; @@ -54,7 +54,7 @@ function maxPageAtZoom(z) { function loadTiles(which, url, projection) { var currZoom = Math.floor(geoScaleToZoom(projection.scale())); - var tiles = tiler.getTiles(projection, tileZoom); + var tiles = tiler.getTiles(projection); // abort inflight requests that are no longer needed var cache = _mlyCache[which]; diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index 4c0c4cd28..40044e8ca 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -35,7 +35,7 @@ import { var apibase = 'https://openstreetcam.org'; var maxResults = 1000; var tileZoom = 14; -var tiler = utilTiler().skipNullIsland(true); +var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true); var dispatch = d3_dispatch('loadedImages'); var imgZoom = d3_zoom() .extent([[0, 0], [320, 240]]) @@ -63,7 +63,7 @@ function maxPageAtZoom(z) { function loadTiles(which, url, projection) { var currZoom = Math.floor(geoScaleToZoom(projection.scale())); - var tiles = tiler.getTiles(projection, tileZoom); + var tiles = tiler.getTiles(projection); // abort inflight requests that are no longer needed var cache = _oscCache[which]; diff --git a/modules/services/osm.js b/modules/services/osm.js index 4a3c0bc5c..31dd39703 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -769,7 +769,7 @@ export default { var path = '/api/0.6/map?bbox='; // determine the needed tiles to cover the view - var tiles = tiler.getTiles(projection, _tileZoom); + var tiles = tiler.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed var hadRequests = !_isEmpty(_tileCache.inflight); @@ -809,7 +809,7 @@ export default { // Load notes from the API in tiles // GET /api/0.6/notes?bbox= loadNotes: function(projection, noteOptions) { - noteOptions = _extend({ limit: 10000, closed: 7}, noteOptions); + noteOptions = _extend({ limit: 10000, closed: 7 }, noteOptions); if (_off) return; var that = this; @@ -821,7 +821,7 @@ export default { }, 750); // determine the needed tiles to cover the view - var tiles = tiler.getTiles(projection, _noteZoom); + var tiles = tiler.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed abortUnwantedRequests(_noteCache, tiles); diff --git a/modules/services/streetside.js b/modules/services/streetside.js index 39d5f7990..8a0cd1b1e 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -41,7 +41,7 @@ var pannellumViewerCSS = 'pannellum-streetside/pannellum.css'; var pannellumViewerJS = 'pannellum-streetside/pannellum.js'; var maxResults = 2000; var tileZoom = 16.5; -var tiler = utilTiler().skipNullIsland(true); +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 @@ -81,7 +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 tiles = tiler.margin(margin).getTiles(projection, tileZoom); + var tiles = tiler.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed var cache = _ssCache[which]; diff --git a/modules/util/tiler.js b/modules/util/tiler.js index a869a3472..03686c9c9 100644 --- a/modules/util/tiler.js +++ b/modules/util/tiler.js @@ -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,27 +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, tilezoom) { - var dimensions = projection.clipExtent()[1]; - 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; @@ -111,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; }; diff --git a/test/spec/renderer/background_source.js b/test/spec/renderer/background_source.js index 2828bdf0c..2289676a4 100644 --- a/test/spec/renderer/background_source.js +++ b/test/spec/renderer/background_source.js @@ -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; From 52dc6c280fab25c77c1daaa6b373009459c81cf1 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 22 Jul 2018 01:43:56 -0400 Subject: [PATCH 12/16] Preparation for non-256px tileSizes in sources --- modules/renderer/background_source.js | 6 +++--- modules/renderer/tile_layer.js | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index f580a6742..005500a9a 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -48,10 +48,10 @@ export function rendererBackgroundSource(data) { var best = !!source.best; var template = source.template; + 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); } diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index d7d93bef3..03af506d6 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -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,9 @@ export function rendererTileLayer(context) { background.source = function(_) { if (!arguments.length) return _source; _source = _; + _tileSize = _source.tileSize; _cache = {}; - tiler.zoomExtent(_source.zoomExtent); + tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent); return background; }; From ac27cbd67d944d1f2f20ca9422b7392cc8dcafeb Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 22 Jul 2018 02:50:49 -0400 Subject: [PATCH 13/16] Add support for 512px Mapbox Satellite tiles The tiler and projection are still not aware of the 512px size. This results in fetching 512px images but placing them in the 256px slippy map and scalling them down. (This is dumb but at least they look nicer than before) --- data/imagery.json | 104 ++++++++++++++++++++++++++++++++- data/update_imagery.js | 9 ++- modules/renderer/tile_layer.js | 3 +- 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/data/imagery.json b/data/imagery.json index 8716aaeca..b1c9998e8 100644 --- a/data/imagery.json +++ b/data/imagery.json @@ -38932,6 +38932,107 @@ "terms_url": "http://data2.loire-atlantique.fr/licences/", "terms_text": "Département de Loire-Atlantique" }, + { + "id": "Loire_Atlantique-Orthophotos-2016", + "name": "Loire-Atlantique - Orthophotos 2016 - 10 cm", + "type": "wms", + "template": "https://wms-vuduciel2.makina-corpus.net/geoserver/wms?SERVICE=WMS&FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=cg44:ortho44-2016&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}", + "projection": "EPSG:3857", + "endDate": "2016-01-01T00:00:00.000Z", + "startDate": "2016-01-01T00:00:00.000Z", + "zoomExtent": [0, 20], + "polygon": [ + [ + [-1.48638, 46.87691], + [-1.54437, 46.84873], + [-1.73468, 46.87246], + [-1.89276, 46.94234], + [-1.95475, 46.98084], + [-2.07067, 47.08521], + [-2.2678, 47.12656], + [-2.22627, 47.17124], + [-2.19217, 47.16914], + [-2.19341, 47.25546], + [-2.29694, 47.22769], + [-2.41658, 47.25336], + [-2.54862, 47.28575], + [-2.63913, 47.41678], + [-2.50573, 47.50812], + [-2.31063, 47.53021], + [-2.24836, 47.52384], + [-2.23223, 47.51499], + [-2.12109, 47.54674], + [-2.11783, 47.60126], + [-2.1001, 47.61123], + [-2.09981, 47.62005], + [-2.11141, 47.62873], + [-2.10055, 47.65141], + [-2.08121, 47.66578], + [-1.98016, 47.70751], + [-1.83077, 47.72419], + [-1.67455, 47.72544], + [-1.63735, 47.77463], + [-1.49601, 47.81752], + [-1.49911, 47.84166], + [-1.38133, 47.84415], + [-1.346, 47.81086], + [-1.23007, 47.78587], + [-1.21643, 47.75838], + [-1.22635, 47.73628], + [-1.21086, 47.7317], + [-1.18668, 47.73462], + [-1.15196, 47.69332], + [-1.13151, 47.63654], + [-1.09121, 47.6332], + [-0.99265, 47.6027], + [-0.98459, 47.58598], + [-1.0317, 47.55001], + [-1.13585, 47.55628], + [-1.13275, 47.5161], + [-1.0317, 47.51778], + [-0.94863, 47.50103], + [-0.93686, 47.47715], + [-0.93376, 47.43859], + [-0.9009, 47.39874], + [-0.92694, 47.37482], + [-0.97157, 47.35845], + [-1.28339, 47.32736], + [-1.23379, 47.26093], + [-1.20032, 47.26935], + [-1.16498, 47.24957], + [-1.14143, 47.1763], + [-1.1563, 47.15818], + [-1.20652, 47.12402], + [-1.20838, 47.10968], + [-1.15568, 47.10504], + [-1.14081, 47.08056], + [-1.09431, 47.0717], + [-1.09989, 47.03199], + [-1.14453, 47.01636], + [-1.21582, 47.02904], + [-1.26727, 47.06325], + [-1.28524, 47.02185], + [-1.34972, 47.02397], + [-1.33918, 46.969], + [-1.3491, 46.94446], + [-1.45014, 46.91186], + [-1.47504, 46.9176], + [-1.48775, 46.93063], + [-1.49235, 46.98433], + [-1.48644, 46.99943], + [-1.49213, 47.02722], + [-1.52764, 47.00541], + [-1.52961, 46.97252], + [-1.50507, 46.94439], + [-1.50222, 46.92973], + [-1.51142, 46.91371], + [-1.48622, 46.89724], + [-1.48638, 46.87691] + ] + ], + "terms_url": "http://data2.loire-atlantique.fr/licences/", + "terms_text": "© Loire-Atlantique ouverture des données publiques" + }, { "id": "Lombardia-Italy-CTR-DBT", "name": "Lombardia - Italy (CTR DBT)", @@ -40343,7 +40444,8 @@ "id": "Mapbox", "name": "Mapbox Satellite", "type": "tms", - "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}.jpg?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJjaml5MjVyb3MwMWV0M3hxYmUzdGdwbzE4In0.q548FjhsSJzvXsGlPsFxAQ", + "template": "https://{switch:a,b,c,d}.tiles.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}@2x.jpg?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJjaml5MjVyb3MwMWV0M3hxYmUzdGdwbzE4In0.q548FjhsSJzvXsGlPsFxAQ", + "tileSize": 512, "zoomExtent": [0, 22], "terms_url": "https://www.mapbox.com/about/maps", "terms_text": "Terms & Feedback", diff --git a/data/update_imagery.js b/data/update_imagery.js index fbf604950..4375f5d3f 100644 --- a/data/update_imagery.js +++ b/data/update_imagery.js @@ -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; } @@ -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]; } diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 03af506d6..454e71400 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -272,7 +272,8 @@ export function rendererTileLayer(context) { _source = _; _tileSize = _source.tileSize; _cache = {}; - tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent); + // tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent); // not yet + tiler.zoomExtent(_source.zoomExtent); return background; }; From 90bc0b8537b3e36b46921b157c5081b71a983a10 Mon Sep 17 00:00:00 2001 From: Jon D Date: Sun, 22 Jul 2018 19:35:29 +0100 Subject: [PATCH 14/16] Update to prevent detachment of node when either a via or location_hint role in a turn restriction. Update to move any other relation to new node. --- data/core.yaml | 3 +- dist/locales/en.json | 3 +- modules/actions/detach_node.js | 20 +- modules/operations/detach_node.js | 56 ++++- modules/util/index.js | 4 +- test/spec/actions/detach_node.js | 82 +++++++- test/spec/operations/detach_node.js | 303 +++++++++++++++++++++------- 7 files changed, 385 insertions(+), 86 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 20d827514..9f5c8eb3f 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -233,7 +233,8 @@ en: title: Detach key: T description: Detach this node from these lines/areas. - annotation: Detached a node from owning lines/areas. + annotation: Detached a node from owning lines/areas. + via_restriction: "This can't be detached because it would damage a turn restriction." restriction: controls: distance: Distance diff --git a/dist/locales/en.json b/dist/locales/en.json index 15dbd00c9..4aea68312 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -302,7 +302,8 @@ "title": "Detach", "key": "T", "description": "Detach this node from these lines/areas.", - "annotation": "Detached a node from owning lines/areas." + "annotation": "Detached a node from owning lines/areas.", + "via_restriction": "This can't be detached because it would damage a turn restriction." } }, "restriction": { diff --git a/modules/actions/detach_node.js b/modules/actions/detach_node.js index d83fd46ed..7ff5cd8e3 100644 --- a/modules/actions/detach_node.js +++ b/modules/actions/detach_node.js @@ -9,19 +9,31 @@ export function actionDetachNode(nodeId) { // Create a new node to replace the one we will detach var replacementNode = osmNode({ loc: node.loc }); // We need to process each way in turn, updating the graph as we go - return parentWays + var updatedWaysGraph = parentWays .reduce(function (accGraph, parentWay) { // Make a note of where in the way our target node is inside this way var originalIndex = parentWay.nodes.indexOf(nodeId); // Swap out the target node for the replacement - var updatedWay = parentWay - .removeNode(nodeId) // Remove our target node from the parent way + var updatedWay = parentWay.removeNode(nodeId) // Remove our target node from the parent way .addNode(replacementNode.id, originalIndex); // Add in the replacement node in its place - // Update the graph with the updated way + // Update the graph with the updated way and pass into the next cycle of the reduce operation return accGraph.replace(updatedWay); }, // Seed the reduction with the input graph, updated to include the replacementNode so // that is accessible to the ways when we add it in to them graph.replace(replacementNode)); + // Process any relations too + var parentRels = updatedWaysGraph.parentRelations(node); + return parentRels + .reduce(function (accGraph, parentRel) { + // Move the relationship to the new node + var originalMember = parentRel.memberById(nodeId); + var newMember = { id: replacementNode.id, type: 'node', role: originalMember.role }; + // Remove & replace with the new member + var updatedRel = parentRel.removeMembersWithID(nodeId) + .addMember(newMember, originalMember.index); + // Update graph and pass into the next cycle of the reduce operation + return accGraph.replace(updatedRel); + }, updatedWaysGraph); }; } diff --git a/modules/operations/detach_node.js b/modules/operations/detach_node.js index 28ca9b600..bb1f985cf 100644 --- a/modules/operations/detach_node.js +++ b/modules/operations/detach_node.js @@ -2,6 +2,8 @@ import { actionDetachNode } from '../actions/index'; import { behaviorOperation } from '../behavior/index'; import { modeMove } from '../modes/index'; import { t } from '../util/locale'; +import _flatMap from 'lodash-es/flatMap'; +import _uniq from 'lodash-es/uniq'; export function operationDetachNode(selectedIDs, context) { var selectedNode = selectedIDs[0]; @@ -37,7 +39,10 @@ export function operationDetachNode(selectedIDs, context) { return false; }; operation.tooltip = function () { - return t('operations.detachNode.description'); + var disableReason = operation.disabled(); + return disableReason + ? t('operations.detachNode.' + disableReason) + : t('operations.detachNode.description'); }; operation.annotation = function () { return t('operations.detachNode.annotation'); @@ -46,5 +51,54 @@ export function operationDetachNode(selectedIDs, context) { operation.keys = [t('operations.detachNode.key')]; operation.title = t('operations.detachNode.title'); operation.behavior = behaviorOperation(context).which(operation); + + operation.disabled = function () { + // We should prevent the node being detached if it represents a via/location_hint node of a turn restriction + var graph = context.graph(); + // Get nodes for the Ids (although there should only be one, we can handle multiple here) + var nodes = selectedIDs.map(function (i) { return graph.hasEntity(i); }) + .filter(isNotNullOrUndefined); + // Get all via nodes of restrictions involving the target nodes + var restrictionNodeIds = _flatMap(nodes, function (node) { + // Get the relations that this node belongs to + var relationsFromNode = graph.parentRelations(node); + // Check each relation in turn + return _flatMap(relationsFromNode, function (relation) { + // Check to see if this is a restriction relation, if not return null + if (!relation.isValidRestriction()) { + return null; + } + // We have identified that it is a restriction. + // https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that + // from & to roles are only appropriate for Ways + // The via members can be either nodes or ways. Via-Ways do not prevent us removing a node + // from within them, as it is the way itself which is in the relation with the via role, + // and not the consitutent nodes (so if we switch out a constituent node, the way id + // does not change and therefore the relation will not be affected). Therefore we + // only need to examine the standalone nodes + return relation.members.filter(function (m) { + return (m.role === 'via' || m.role === 'location_hint') && m.type === 'node'; + }).map(function (m) { return m.id; }); + }); + }).filter(isNotNullOrUndefined); + + // Get unique list of ids in restrictionNodeIds to simplify checking + var nodeIds = _uniq(restrictionNodeIds); + + // Now we have a list of via/location_hint nodes, we should prevent detachment if the target node is in this list + var anyInhibits = nodes.filter(function (n) { + return nodeIds.indexOf(n.id) !== -1; + }); + if (anyInhibits.length > 0) { + // The node is a via/location_hint, do not permit + return 'via_restriction'; + } + // We are ok to proceed + return false; + }; return operation; } + +function isNotNullOrUndefined(i) { + return i !== undefined && i !== null; +} \ No newline at end of file diff --git a/modules/util/index.js b/modules/util/index.js index f64856de8..d25494503 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -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'; @@ -24,4 +24,4 @@ export { utilStringQs } from './util'; export { utilSuggestNames } from './suggest_names'; export { utilTagText } from './util'; export { utilTriggerEvent } from './trigger_event'; -export { utilWrap } from './util'; +export { utilWrap } from './util'; \ No newline at end of file diff --git a/test/spec/actions/detach_node.js b/test/spec/actions/detach_node.js index 70739b640..16fcc5e3a 100644 --- a/test/spec/actions/detach_node.js +++ b/test/spec/actions/detach_node.js @@ -79,7 +79,7 @@ describe('iD.actionDetachNode', function () { // confirm that a still exists var targetNode = assertionGraph.entity('a'); expect(targetNode).not.to.eql(undefined); - // .., and that the location is correct + // ... 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); @@ -149,7 +149,7 @@ describe('iD.actionDetachNode', function () { // confirm that a still exists var targetNode = assertionGraph.entity('b'); expect(targetNode).not.to.eql(undefined); - // .., and that the location is correct + // ... 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); @@ -240,7 +240,7 @@ describe('iD.actionDetachNode', function () { // confirm that a still exists var targetNode = assertionGraph.entity('a'); expect(targetNode).not.to.eql(undefined); - // .., and that the location is correct + // ... 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); @@ -312,7 +312,7 @@ describe('iD.actionDetachNode', function () { // confirm that a still exists var targetNode = assertionGraph.entity('b'); expect(targetNode).not.to.eql(undefined); - // .., and that the location is correct + // ... 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); @@ -416,7 +416,7 @@ describe('iD.actionDetachNode', function () { // confirm that a still exists var targetNode = assertionGraph.entity('c'); expect(targetNode).not.to.eql(undefined); - // .., and that the location is correct + // ... 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); @@ -529,7 +529,7 @@ describe('iD.actionDetachNode', function () { // confirm that a still exists var targetNode = assertionGraph.entity('c'); expect(targetNode).not.to.eql(undefined); - // .., and that the location is correct + // ... 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); @@ -537,4 +537,74 @@ describe('iD.actionDetachNode', function () { expect(assertionGraph.parentWays(targetNode)).to.eql([]); }); }); + describe('with relation', function () { + var graph; + + beforeEach(function () { + // Set up a simple way + // a-b-c (0,0)-(1,0)-(2,0) + // Node b represents the target + // With a relationship for the way including b + graph = iD.Graph([ + iD.Node({ id: 'a', loc: [0, 0] }), + iD.Node({ id: 'b', loc: [1, 0], tags: tags }), + iD.Node({ id: 'c', loc: [2, 0] }), + iD.Way({ id: 'w', nodes: ['a', 'b', 'c'] }), + iD.Relation({ + 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('w'); + 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('w'); + 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' }); + }); + + }); }); \ No newline at end of file diff --git a/test/spec/operations/detach_node.js b/test/spec/operations/detach_node.js index 6604ce051..357d390fa 100644 --- a/test/spec/operations/detach_node.js +++ b/test/spec/operations/detach_node.js @@ -1,85 +1,246 @@ describe('iD.operationDetachNode', function () { var fakeContext; var graph; + + // Some common setup functions + // Set up the fake context + fakeContext = {}; + fakeContext.graph = function () { + return graph; + }; var fakeTags = { 'name': 'fake' }; - beforeEach(function () { - // Set up graph - var createFakeNode = function (id, hasTags) { - return hasTags - ? { id: id, type: 'node', tags: fakeTags } - : { id: id, type: 'node' }; - }; - // 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.Graph([ - iD.Node(createFakeNode('a', true)), - iD.Node(createFakeNode('b', true)), - iD.Node(createFakeNode('c', false)), - iD.Node(createFakeNode('d', false)), - iD.Node(createFakeNode('e', true)), - iD.Node(createFakeNode('f', false)), - iD.Way({ id: 'x', nodes: ['a', 'b', 'c', 'd'] }), - iD.Way({ id: 'y', nodes: ['b', 'd'] }) - ]); + // Set up graph + var createFakeNode = function (id, hasTags) { + return hasTags + ? { id: id, type: 'node', tags: fakeTags } + : { id: id, type: 'node' }; + }; - // Set up the fake context - fakeContext = {}; - fakeContext.graph = function () { - return graph; - }; + 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.Graph([ + iD.Node(createFakeNode('a', true)), + iD.Node(createFakeNode('b', true)), + iD.Node(createFakeNode('c', false)), + iD.Node(createFakeNode('d', false)), + iD.Node(createFakeNode('e', true)), + iD.Node(createFakeNode('f', false)), + iD.Way({ id: 'x', nodes: ['a', 'b', 'c', 'd'] }), + iD.Way({ id: 'y', nodes: ['b', 'd'] }) + ]); + }); + + it('is not available for no selected ids', function () { + var result = iD.operationDetachNode([], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for two selected ids', function () { + var result = iD.operationDetachNode(['a', 'b'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for unkown selected id', function () { + var result = iD.operationDetachNode(['z'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected way', function () { + var result = iD.operationDetachNode(['x'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with tags, no parent way', function () { + var result = iD.operationDetachNode(['e'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with no tags, no parent way', function () { + var result = iD.operationDetachNode(['f'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with no tags, parent way', function () { + var result = iD.operationDetachNode(['c'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is not available for selected node with no tags, two parent ways', function () { + var result = iD.operationDetachNode(['d'], fakeContext).available(); + expect(result).to.eql(false); + }); + + it('is available for selected node with tags, parent way', function () { + var result = iD.operationDetachNode(['a'], fakeContext).available(); + expect(result).to.eql(true); + }); + + it('is available for selected node with tags, two parent ways', function () { + var result = iD.operationDetachNode(['b'], fakeContext).available(); + expect(result).to.eql(true); + }); }); - it('is not available for no selected ids', function () { - var result = iD.operationDetachNode([], fakeContext).available(); - expect(result).to.eql(false); - }); + describe('disabled', function () { + it('returns enabled for non-related node', function () { + graph = iD.Graph([ + iD.Node(createFakeNode('a', false)), + iD.Node(createFakeNode('b', true)), + iD.Node(createFakeNode('c', false)), + iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }) + ]); + var result = iD.operationDetachNode(['b'], fakeContext).disabled(); + expect(result).to.eql(false); + }); - it('is not available for two selected ids', function () { - var result = iD.operationDetachNode(['a', 'b'], fakeContext).available(); - expect(result).to.eql(false); - }); + it('returns enabled for non-restriction related node', function () { + graph = iD.Graph([ + iD.Node(createFakeNode('a', false)), + iD.Node(createFakeNode('b', true)), + iD.Node(createFakeNode('c', false)), + iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }), + iD.Relation({ id: 'r', members: [{ id: 'b', role: 'label' }] }) + ]); + var result = iD.operationDetachNode(['b'], fakeContext).disabled(); + expect(result).to.eql(false); + }); - it('is not available for unkown selected id', function () { - var result = iD.operationDetachNode(['z'], fakeContext).available(); - expect(result).to.eql(false); - }); + 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.Graph([ + iD.Node(createFakeNode('a', false)), + iD.Node(createFakeNode('b', false)), + iD.Node(createFakeNode('c', false)), + iD.Node(createFakeNode('d', true)), + iD.Node(createFakeNode('e', false)), + iD.Node(createFakeNode('f', false)), + iD.Node(createFakeNode('g', false)), + iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }), + iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), + iD.Relation({ + 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).not.to.eql(false); + }); - it('is not available for selected way', function () { - var result = iD.operationDetachNode(['x'], fakeContext).available(); - expect(result).to.eql(false); - }); + it('returns not-enabled for via node in restriction and other non-restriction relation', function () { + graph = iD.Graph([ + iD.Node(createFakeNode('a', false)), + iD.Node(createFakeNode('b', false)), + iD.Node(createFakeNode('c', false)), + iD.Node(createFakeNode('d', true)), + iD.Node(createFakeNode('e', false)), + iD.Node(createFakeNode('f', false)), + iD.Node(createFakeNode('g', false)), + iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }), + iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), + iD.Relation({ + 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' } + ] + }), + iD.Relation({ + id: 's', + members: [ + { id: 'x', type: 'way' }, + { id: 'd', type: 'node' }, + ] + }) + ]); + var result = iD.operationDetachNode(['d'], fakeContext).disabled(); + expect(result).not.to.eql(false); + }); - it('is not available for selected node with tags, no parent way', function () { - var result = iD.operationDetachNode(['e'], fakeContext).available(); - expect(result).to.eql(false); - }); + 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.Graph([ + iD.Node(createFakeNode('a', false)), + iD.Node(createFakeNode('b', false)), + iD.Node(createFakeNode('c', false)), + iD.Node(createFakeNode('d', true)), + iD.Node(createFakeNode('e', false)), + iD.Node(createFakeNode('f', false)), + iD.Node(createFakeNode('g', false)), + iD.Way({ id: 'x', nodes: ['a', 'b'] }), + iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), + iD.Relation({ + 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).not.to.eql(false); + }); - it('is not available for selected node with no tags, no parent way', function () { - var result = iD.operationDetachNode(['f'], fakeContext).available(); - expect(result).to.eql(false); - }); - - it('is not available for selected node with no tags, parent way', function () { - var result = iD.operationDetachNode(['c'], fakeContext).available(); - expect(result).to.eql(false); - }); - - it('is not available for selected node with no tags, two parent ways', function () { - var result = iD.operationDetachNode(['d'], fakeContext).available(); - expect(result).to.eql(false); - }); - - it('is available for selected node with tags, parent way', function () { - var result = iD.operationDetachNode(['a'], fakeContext).available(); - expect(result).to.eql(true); - }); - - it('is available for selected node with tags, two parent ways', function () { - var result = iD.operationDetachNode(['b'], fakeContext).available(); - expect(result).to.eql(true); + it('returns not-enabled for location_hint node in restriction and other non-restriction relation', function () { + graph = iD.Graph([ + iD.Node(createFakeNode('a', false)), + iD.Node(createFakeNode('b', false)), + iD.Node(createFakeNode('c', false)), + iD.Node(createFakeNode('d', true)), + iD.Node(createFakeNode('e', false)), + iD.Node(createFakeNode('f', false)), + iD.Node(createFakeNode('g', false)), + iD.Way({ id: 'x', nodes: ['a', 'b'] }), + iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), + iD.Relation({ + 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' } + ] + }), + iD.Relation({ + id: 's', + members: [ + { id: 'x', type: 'way' }, + { id: 'd', type: 'node' }, + ] + }) + ]); + var result = iD.operationDetachNode(['d'], fakeContext).disabled(); + expect(result).not.to.eql(false); + }); }); }); From 119792fd22b9f72b6b29a96742435a5b9e59f3dc Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 23 Jul 2018 13:36:34 -0400 Subject: [PATCH 15/16] Cleanups to Detach Node feature This commit includes a bunch of minor things: - change keyboard shortcut to 'E' to not conflict with anything - move `disabled` check from operation into action and simplify - use `actionMoveNode` to place the detached node at the mouse cursor - disable the operation if the node is connected to hidden features - lots of code simplification - make the icon more centered --- data/core.yaml | 10 +- data/shortcuts.json | 100 +++----- dist/locales/en.json | 10 +- modules/actions/detach_node.js | 75 +++--- modules/core/graph.js | 47 ++-- modules/operations/detach_node.js | 147 +++++------ modules/osm/relation.js | 3 +- modules/osm/way.js | 37 ++- .../operations/operation-detach-node.svg | 6 + .../operations/operation-detachNode.svg | 9 - test/spec/actions/detach_node.js | 232 ++++++++++-------- test/spec/operations/detach_node.js | 204 +++++---------- 12 files changed, 382 insertions(+), 498 deletions(-) create mode 100644 svg/iD-sprite/operations/operation-detach-node.svg delete mode 100644 svg/iD-sprite/operations/operation-detachNode.svg diff --git a/data/core.yaml b/data/core.yaml index f06a69983..828e9715b 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -229,12 +229,13 @@ en: annotation: create: Added a turn restriction delete: Deleted a turn restriction - detachNode: + detach_node: title: Detach - key: T + key: E description: Detach this node from these lines/areas. - annotation: Detached a node from owning lines/areas. - via_restriction: "This can't be detached because it would damage a turn restriction." + 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 @@ -1140,6 +1141,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" diff --git a/data/shortcuts.json b/data/shortcuts.json index ee0c9cdc9..0da3dfdce 100644 --- a/data/shortcuts.json +++ b/data/shortcuts.json @@ -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" } ] diff --git a/dist/locales/en.json b/dist/locales/en.json index 8363a101f..47a1f0100 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -298,12 +298,13 @@ "delete": "Deleted a turn restriction" } }, - "detachNode": { + "detach_node": { "title": "Detach", - "key": "T", + "key": "E", "description": "Detach this node from these lines/areas.", - "annotation": "Detached a node from owning lines/areas.", - "via_restriction": "This can't be detached because it would damage a turn restriction." + "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": { @@ -1315,6 +1316,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", diff --git a/modules/actions/detach_node.js b/modules/actions/detach_node.js index 7ff5cd8e3..39bcd59fa 100644 --- a/modules/actions/detach_node.js +++ b/modules/actions/detach_node.js @@ -1,39 +1,48 @@ import { osmNode } from '../osm'; -export function actionDetachNode(nodeId) { - return function (graph) { - // Get the point in question - var node = graph.entity(nodeId); - // Get all of the ways it's currently attached to - var parentWays = graph.parentWays(node); + +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 replacementNode = osmNode({ loc: node.loc }); - // We need to process each way in turn, updating the graph as we go - var updatedWaysGraph = parentWays - .reduce(function (accGraph, parentWay) { - // Make a note of where in the way our target node is inside this way - var originalIndex = parentWay.nodes.indexOf(nodeId); - // Swap out the target node for the replacement - var updatedWay = parentWay.removeNode(nodeId) // Remove our target node from the parent way - .addNode(replacementNode.id, originalIndex); // Add in the replacement node in its place - // Update the graph with the updated way and pass into the next cycle of the reduce operation - return accGraph.replace(updatedWay); - }, - // Seed the reduction with the input graph, updated to include the replacementNode so - // that is accessible to the ways when we add it in to them - graph.replace(replacementNode)); + 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 - var parentRels = updatedWaysGraph.parentRelations(node); - return parentRels - .reduce(function (accGraph, parentRel) { - // Move the relationship to the new node - var originalMember = parentRel.memberById(nodeId); - var newMember = { id: replacementNode.id, type: 'node', role: originalMember.role }; - // Remove & replace with the new member - var updatedRel = parentRel.removeMembersWithID(nodeId) - .addMember(newMember, originalMember.index); - // Update graph and pass into the next cycle of the reduce operation - return accGraph.replace(updatedRel); - }, updatedWaysGraph); + 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; } diff --git a/modules/core/graph.js b/modules/core/graph.js index bfe2c1cf2..31c06c410 100644 --- a/modules/core/graph.js +++ b/modules/core/graph.js @@ -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); } diff --git a/modules/operations/detach_node.js b/modules/operations/detach_node.js index bb1f985cf..268b8554e 100644 --- a/modules/operations/detach_node.js +++ b/modules/operations/detach_node.js @@ -1,104 +1,85 @@ -import { actionDetachNode } from '../actions/index'; -import { behaviorOperation } from '../behavior/index'; -import { modeMove } from '../modes/index'; +import _some from 'lodash-es/some'; + +import { actionDetachNode, actionMoveNode } from '../actions'; +import { behaviorOperation } from '../behavior'; +import { modeMove } from '../modes'; import { t } from '../util/locale'; -import _flatMap from 'lodash-es/flatMap'; -import _uniq from 'lodash-es/uniq'; + export function operationDetachNode(selectedIDs, context) { - var selectedNode = selectedIDs[0]; + var nodeID = selectedIDs.length && selectedIDs[0]; + var action = actionDetachNode(nodeID); + var operation = function () { - context.perform(actionDetachNode(selectedNode)); - context.enter(modeMove(context, [selectedNode], context.graph)); - }; - var hasTags = function (entity) { - return Object.keys(entity.tags).length > 0; + 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 () { - // Check multiple items aren't selected - if (selectedIDs.length !== 1) { - return false; - } - // Get the entity itself + if (selectedIDs.length !== 1) return false; + var graph = context.graph(); - var entity = graph.hasEntity(selectedNode); - if (!entity) { - // This probably isn't possible - return false; - } - // Confirm entity is a node with tags - if (entity.type === 'node' && hasTags(entity)) { - // Confirm that the node is owned by at least 1 parent way - var parentWays = graph.parentWays(entity); - return parentWays && parentWays.length > 0; - } - // Not appropriate - return false; + var entity = graph.hasEntity(nodeID); + if (!entity) return false; + + return entity.type === 'node' && + entity.hasInterestingTags() && + graph.parentWays(entity).length > 0; }; + + operation.disabled = function () { - return false; + var reason; + if (_some(selectedIDs, context.hasHiddenConnections)) { + reason = 'connected_to_hidden'; + } + return action.disabled(context.graph()) || reason; }; + + operation.tooltip = function () { var disableReason = operation.disabled(); - return disableReason - ? t('operations.detachNode.' + disableReason) - : t('operations.detachNode.description'); + 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.detachNode.annotation'); + return t('operations.detach_node.annotation'); }; - operation.id = 'detachNode'; - operation.keys = [t('operations.detachNode.key')]; - operation.title = t('operations.detachNode.title'); + + + operation.id = 'detach-node'; + operation.keys = [t('operations.detach_node.key')]; + operation.title = t('operations.detach_node.title'); operation.behavior = behaviorOperation(context).which(operation); - operation.disabled = function () { - // We should prevent the node being detached if it represents a via/location_hint node of a turn restriction - var graph = context.graph(); - // Get nodes for the Ids (although there should only be one, we can handle multiple here) - var nodes = selectedIDs.map(function (i) { return graph.hasEntity(i); }) - .filter(isNotNullOrUndefined); - // Get all via nodes of restrictions involving the target nodes - var restrictionNodeIds = _flatMap(nodes, function (node) { - // Get the relations that this node belongs to - var relationsFromNode = graph.parentRelations(node); - // Check each relation in turn - return _flatMap(relationsFromNode, function (relation) { - // Check to see if this is a restriction relation, if not return null - if (!relation.isValidRestriction()) { - return null; - } - // We have identified that it is a restriction. - // https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that - // from & to roles are only appropriate for Ways - // The via members can be either nodes or ways. Via-Ways do not prevent us removing a node - // from within them, as it is the way itself which is in the relation with the via role, - // and not the consitutent nodes (so if we switch out a constituent node, the way id - // does not change and therefore the relation will not be affected). Therefore we - // only need to examine the standalone nodes - return relation.members.filter(function (m) { - return (m.role === 'via' || m.role === 'location_hint') && m.type === 'node'; - }).map(function (m) { return m.id; }); - }); - }).filter(isNotNullOrUndefined); - // Get unique list of ids in restrictionNodeIds to simplify checking - var nodeIds = _uniq(restrictionNodeIds); - - // Now we have a list of via/location_hint nodes, we should prevent detachment if the target node is in this list - var anyInhibits = nodes.filter(function (n) { - return nodeIds.indexOf(n.id) !== -1; - }); - if (anyInhibits.length > 0) { - // The node is a via/location_hint, do not permit - return 'via_restriction'; - } - // We are ok to proceed - return false; - }; return operation; } -function isNotNullOrUndefined(i) { - return i !== undefined && i !== null; -} \ No newline at end of file diff --git a/modules/osm/relation.js b/modules/osm/relation.js index 8532990ed..1ba283df4 100644 --- a/modules/osm/relation.js +++ b/modules/osm/relation.js @@ -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); diff --git a/modules/osm/way.js b/modules/osm/way.js index af1b3d476..c4da27c25 100644 --- a/modules/osm/way.js +++ b/modules/osm/way.js @@ -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; }) diff --git a/svg/iD-sprite/operations/operation-detach-node.svg b/svg/iD-sprite/operations/operation-detach-node.svg new file mode 100644 index 000000000..a10edfc66 --- /dev/null +++ b/svg/iD-sprite/operations/operation-detach-node.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/svg/iD-sprite/operations/operation-detachNode.svg b/svg/iD-sprite/operations/operation-detachNode.svg deleted file mode 100644 index 504acc2cb..000000000 --- a/svg/iD-sprite/operations/operation-detachNode.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/test/spec/actions/detach_node.js b/test/spec/actions/detach_node.js index 16fcc5e3a..1bac58e44 100644 --- a/test/spec/actions/detach_node.js +++ b/test/spec/actions/detach_node.js @@ -1,20 +1,22 @@ describe('iD.actionDetachNode', function () { var tags = { 'name': 'test' }; + function createTargetNode(id, lonlat) { - return iD.Node({ id: id, loc: lonlat, tags: tags }); + return iD.osmNode({ id: id, loc: lonlat, tags: tags }); } - describe('simple way', function () { + + describe('linear way', function () { var graph; beforeEach(function () { - // Set up a simple way - // a-b-c-d - // (0,0)-(1,0)-(2,0)-(3,0) - graph = iD.Graph([ - iD.Node({ id: 'a', loc: [0, 0] }), - iD.Node({ id: 'b', loc: [1, 0] }), - iD.Node({ id: 'c', loc: [2, 0] }), - iD.Node({ id: 'd', loc: [3, 0] }), - iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd'] }) + // + // 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'] }) ]); }); @@ -30,7 +32,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('a')(graph); // Confirm that the way still has 4 nodes - var target = assertionGraph.entity('w'); + var target = assertionGraph.entity('-'); expect(target.nodes.length).to.eql(4); }); @@ -39,11 +41,11 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('a')(graph); // Confirm that the way is ordered correctly - var target = assertionGraph.entity('w'); + 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 + // 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'); @@ -54,7 +56,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('a')(graph); // Confirm that the nodes have not moved, including the replacement node - var nodes = assertionGraph.entity('w').nodes; + 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]); @@ -65,7 +67,7 @@ describe('iD.actionDetachNode', function () { // Act var assertionGraph = iD.actionDetachNode('a')(graph); - var nodes = assertionGraph.entity('w').nodes; + 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 @@ -100,7 +102,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Confirm that the way still has 4 nodes - var target = assertionGraph.entity('w'); + var target = assertionGraph.entity('-'); expect(target.nodes.length).to.eql(4); }); @@ -109,11 +111,11 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Confirm that the way is ordered correctly - var target = assertionGraph.entity('w'); + 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 + // 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'); @@ -124,7 +126,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Confirm that the nodes have not moved, including the replacement node - var nodes = assertionGraph.entity('w').nodes; + 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]); @@ -135,7 +137,7 @@ describe('iD.actionDetachNode', function () { // Act var assertionGraph = iD.actionDetachNode('b')(graph); - var nodes = assertionGraph.entity('w').nodes; + 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 @@ -158,19 +160,22 @@ describe('iD.actionDetachNode', function () { }); }); }); + + describe('closed way', function () { var graph; beforeEach(function () { - // Set up a closed way - // a-b (0,0)-(1,0) - // | | - // d-c (0,1)-(1,1) - graph = iD.Graph([ - iD.Node({ id: 'a', loc: [0, 0] }), - iD.Node({ id: 'b', loc: [1, 0] }), - iD.Node({ id: 'c', loc: [1, 1] }), - iD.Node({ id: 'd', loc: [0, 1] }), - iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd', 'a'] }) + // + // 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'] }) ]); }); @@ -186,7 +191,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('a')(graph); // Confirm that the way still has 5 nodes - var target = assertionGraph.entity('w'); + var target = assertionGraph.entity('-'); expect(target.nodes.length).to.eql(5); }); @@ -195,11 +200,11 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('a')(graph); // Confirm that the way is ordered correctly - var target = assertionGraph.entity('w'); + 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 + // 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'); @@ -212,7 +217,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('a')(graph); // Confirm that the nodes have not moved, including the replacement node - var nodes = assertionGraph.entity('w').nodes; + 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]); @@ -224,7 +229,7 @@ describe('iD.actionDetachNode', function () { // Act var assertionGraph = iD.actionDetachNode('a')(graph); - var nodes = assertionGraph.entity('w').nodes; + 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 @@ -261,7 +266,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Confirm that the way still has 5 nodes - var target = assertionGraph.entity('w'); + var target = assertionGraph.entity('-'); expect(target.nodes.length).to.eql(5); }); @@ -270,11 +275,11 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Confirm that the way is ordered correctly - var target = assertionGraph.entity('w'); + 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 + // 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'); @@ -286,7 +291,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Confirm that the nodes have not moved, including the replacement node - var nodes = assertionGraph.entity('w').nodes; + 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]); @@ -298,7 +303,7 @@ describe('iD.actionDetachNode', function () { // Act var assertionGraph = iD.actionDetachNode('b')(graph); - var nodes = assertionGraph.entity('w').nodes; + 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 @@ -321,23 +326,29 @@ describe('iD.actionDetachNode', function () { }); }); }); - describe('intersecting simple ways', function () { + + + describe('intersecting linear ways', function () { var graph; beforeEach(function () { - // Set up two simple ways - // a-b-c-d (0,0)-(1,0)-(2,0)-(3,0) - // e (2,1) - // f (2,2) + // + // f + // ‖ + // e + // ‖ + // a -- b -- c -- d + // // Node c represents the target - graph = iD.Graph([ - iD.Node({ id: 'a', loc: [0, 0] }), - iD.Node({ id: 'b', loc: [1, 0] }), - iD.Node({ id: 'c', loc: [2, 0], tags: tags }), - iD.Node({ id: 'd', loc: [3, 0] }), - iD.Node({ id: 'e', loc: [2, 1] }), - iD.Node({ id: 'f', loc: [2, 2] }), - iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd'] }), - iD.Way({ id: 'x', nodes: ['c', 'e', 'f'] }) + // + 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'] }) ]); }); @@ -346,10 +357,10 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('c')(graph); // Confirm that the way still has 4 nodes - var target = assertionGraph.entity('w'); + var target = assertionGraph.entity('-'); expect(target.nodes.length).to.eql(4); // .. and second way has 3 - target = assertionGraph.entity('x'); + target = assertionGraph.entity('='); expect(target.nodes.length).to.eql(3); }); @@ -358,16 +369,16 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('c')(graph); // Confirm that the way is ordered correctly - var target = assertionGraph.entity('w'); + 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 + // 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('x'); + target = assertionGraph.entity('='); expect(target.nodes[1]).to.eql('e'); expect(target.nodes[2]).to.eql('f'); }); @@ -377,13 +388,13 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('c')(graph); // Confirm that the nodes have not moved, including the replacement node - var nodes = assertionGraph.entity('w').nodes; + 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('x').nodes; + 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]); @@ -393,20 +404,20 @@ describe('iD.actionDetachNode', function () { // Act var assertionGraph = iD.actionDetachNode('c')(graph); // Confirm both ways have the same replacement node - expect(assertionGraph.entity('w').nodes[2]).to.eql(assertionGraph.entity('x').nodes[0]); + 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('w').nodes; + 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('x').nodes[0]).to.eql(nodes[2]); + expect(assertionGraph.entity('=').nodes[0]).to.eql(nodes[2]); }); it('does detach target node', function () { @@ -424,26 +435,30 @@ describe('iD.actionDetachNode', function () { expect(assertionGraph.parentWays(targetNode)).to.eql([]); }); }); + + describe('intersecting closed way', function () { var graph; beforeEach(function () { - // Set up two intersecting closed ways - // a-b (0,0)-(1,0) - // | | - // d-c-e (0,1)-(1,1)-(2,1) - // | | - // g f (0,2) - (1,2) - // C is the target node - graph = iD.Graph([ - iD.Node({ id: 'a', loc: [0, 0] }), - iD.Node({ id: 'b', loc: [1, 0] }), - iD.Node({ id: 'c', loc: [1, 1], tags: tags }), - iD.Node({ id: 'd', loc: [0, 1] }), - iD.Node({ id: 'e', loc: [2, 1] }), - iD.Node({ id: 'f', loc: [1, 2] }), - iD.Node({ id: 'g', loc: [0, 2] }), - iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd', 'a'] }), - iD.Way({ id: 'x', nodes: ['c', 'e', 'f', 'g', 'c'] }) + // + // 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'] }) ]); }); @@ -452,10 +467,10 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('c')(graph); // Confirm that the way still has 5 nodes - var target = assertionGraph.entity('w'); + var target = assertionGraph.entity('-'); expect(target.nodes.length).to.eql(5); // and the second - target = assertionGraph.entity('x'); + target = assertionGraph.entity('='); expect(target.nodes.length).to.eql(5); }); @@ -464,18 +479,18 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('c')(graph); // Confirm that the way is ordered correctly - var target = assertionGraph.entity('w'); + 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 + // 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('x'); + target = assertionGraph.entity('='); expect(target.nodes[1]).to.eql('e'); expect(target.nodes[2]).to.eql('f'); expect(target.nodes[3]).to.eql('g'); @@ -487,32 +502,32 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('c')(graph); // Confirm that the nodes have not moved, including the replacement node - var nodes = assertionGraph.entity('w').nodes; + 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('x').nodes; + 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([1, 2]); - expect(assertionGraph.entity(nodes[3]).loc).to.eql([0, 2]); + 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('w').nodes[2]).to.eql(assertionGraph.entity('x').nodes[0]); + 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('w').nodes; + 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 @@ -537,25 +552,24 @@ describe('iD.actionDetachNode', function () { expect(assertionGraph.parentWays(targetNode)).to.eql([]); }); }); + + describe('with relation', function () { var graph; beforeEach(function () { - // Set up a simple way - // a-b-c (0,0)-(1,0)-(2,0) + // + // a -- b -- c + // // Node b represents the target // With a relationship for the way including b - graph = iD.Graph([ - iD.Node({ id: 'a', loc: [0, 0] }), - iD.Node({ id: 'b', loc: [1, 0], tags: tags }), - iD.Node({ id: 'c', loc: [2, 0] }), - iD.Way({ id: 'w', nodes: ['a', 'b', 'c'] }), - iD.Relation({ - id: 'r', - tags: { - type: 'route', - route: 'foot' - }, + // + 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' }, @@ -577,7 +591,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Find the new node - var targetWay = assertionGraph.entity('w'); + var targetWay = assertionGraph.entity('-'); var newNodeId = targetWay.nodes.filter(function (m) { return m !== 'a' && m !== 'b' && m !== 'c'; })[0]; @@ -592,7 +606,7 @@ describe('iD.actionDetachNode', function () { var assertionGraph = iD.actionDetachNode('b')(graph); // Find the new node - var targetWay = assertionGraph.entity('w'); + var targetWay = assertionGraph.entity('-'); var newNodeId = targetWay.nodes.filter(function (m) { return m !== 'a' && m !== 'b' && m !== 'c'; })[0]; @@ -607,4 +621,4 @@ describe('iD.actionDetachNode', function () { }); }); -}); \ No newline at end of file +}); diff --git a/test/spec/operations/detach_node.js b/test/spec/operations/detach_node.js index 357d390fa..e79bf8349 100644 --- a/test/spec/operations/detach_node.js +++ b/test/spec/operations/detach_node.js @@ -2,13 +2,13 @@ describe('iD.operationDetachNode', function () { var fakeContext; var graph; - // Some common setup functions // Set up the fake context fakeContext = {}; - fakeContext.graph = function () { - return graph; - }; + fakeContext.graph = function () { return graph; }; + fakeContext.hasHiddenConnections = function () { return false; }; + var fakeTags = { 'name': 'fake' }; + // Set up graph var createFakeNode = function (id, hasTags) { return hasTags @@ -24,112 +24,108 @@ describe('iD.operationDetachNode', function () { // 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.Graph([ - iD.Node(createFakeNode('a', true)), - iD.Node(createFakeNode('b', true)), - iD.Node(createFakeNode('c', false)), - iD.Node(createFakeNode('d', false)), - iD.Node(createFakeNode('e', true)), - iD.Node(createFakeNode('f', false)), - iD.Way({ id: 'x', nodes: ['a', 'b', 'c', 'd'] }), - iD.Way({ id: 'y', nodes: ['b', 'd'] }) + 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.eql(false); + 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.eql(false); + expect(result).to.be.not.ok; }); - it('is not available for unkown selected id', function () { + it('is not available for unknown selected id', function () { var result = iD.operationDetachNode(['z'], fakeContext).available(); - expect(result).to.eql(false); + expect(result).to.be.not.ok; }); it('is not available for selected way', function () { var result = iD.operationDetachNode(['x'], fakeContext).available(); - expect(result).to.eql(false); + 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.eql(false); + 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.eql(false); + 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.eql(false); + 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.eql(false); + 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.eql(true); + 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.eql(true); + expect(result).to.be.ok; }); }); + describe('disabled', function () { it('returns enabled for non-related node', function () { - graph = iD.Graph([ - iD.Node(createFakeNode('a', false)), - iD.Node(createFakeNode('b', true)), - iD.Node(createFakeNode('c', false)), - iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }) + 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.eql(false); + expect(result).to.be.not.ok; }); it('returns enabled for non-restriction related node', function () { - graph = iD.Graph([ - iD.Node(createFakeNode('a', false)), - iD.Node(createFakeNode('b', true)), - iD.Node(createFakeNode('c', false)), - iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }), - iD.Relation({ id: 'r', members: [{ id: 'b', role: 'label' }] }) + 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.eql(false); + 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.Graph([ - iD.Node(createFakeNode('a', false)), - iD.Node(createFakeNode('b', false)), - iD.Node(createFakeNode('c', false)), - iD.Node(createFakeNode('d', true)), - iD.Node(createFakeNode('e', false)), - iD.Node(createFakeNode('f', false)), - iD.Node(createFakeNode('g', false)), - iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }), - iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), - iD.Relation({ - id: 'r', - tags: { - type: 'restriction', - restriction: 'no_right_turn' - }, + 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' }, @@ -138,63 +134,23 @@ describe('iD.operationDetachNode', function () { }) ]); var result = iD.operationDetachNode(['d'], fakeContext).disabled(); - expect(result).not.to.eql(false); - }); - - it('returns not-enabled for via node in restriction and other non-restriction relation', function () { - graph = iD.Graph([ - iD.Node(createFakeNode('a', false)), - iD.Node(createFakeNode('b', false)), - iD.Node(createFakeNode('c', false)), - iD.Node(createFakeNode('d', true)), - iD.Node(createFakeNode('e', false)), - iD.Node(createFakeNode('f', false)), - iD.Node(createFakeNode('g', false)), - iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }), - iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), - iD.Relation({ - 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' } - ] - }), - iD.Relation({ - id: 's', - members: [ - { id: 'x', type: 'way' }, - { id: 'd', type: 'node' }, - ] - }) - ]); - var result = iD.operationDetachNode(['d'], fakeContext).disabled(); - expect(result).not.to.eql(false); + 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.Graph([ - iD.Node(createFakeNode('a', false)), - iD.Node(createFakeNode('b', false)), - iD.Node(createFakeNode('c', false)), - iD.Node(createFakeNode('d', true)), - iD.Node(createFakeNode('e', false)), - iD.Node(createFakeNode('f', false)), - iD.Node(createFakeNode('g', false)), - iD.Way({ id: 'x', nodes: ['a', 'b'] }), - iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), - iD.Relation({ - id: 'r', - tags: { - type: 'restriction', - restriction: 'no_right_turn' - }, + 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' }, @@ -204,43 +160,7 @@ describe('iD.operationDetachNode', function () { }) ]); var result = iD.operationDetachNode(['d'], fakeContext).disabled(); - expect(result).not.to.eql(false); - }); - - it('returns not-enabled for location_hint node in restriction and other non-restriction relation', function () { - graph = iD.Graph([ - iD.Node(createFakeNode('a', false)), - iD.Node(createFakeNode('b', false)), - iD.Node(createFakeNode('c', false)), - iD.Node(createFakeNode('d', true)), - iD.Node(createFakeNode('e', false)), - iD.Node(createFakeNode('f', false)), - iD.Node(createFakeNode('g', false)), - iD.Way({ id: 'x', nodes: ['a', 'b'] }), - iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }), - iD.Relation({ - 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' } - ] - }), - iD.Relation({ - id: 's', - members: [ - { id: 'x', type: 'way' }, - { id: 'd', type: 'node' }, - ] - }) - ]); - var result = iD.operationDetachNode(['d'], fakeContext).disabled(); - expect(result).not.to.eql(false); + expect(result).to.eql('restriction'); }); }); }); From 31c39287bf09b01b3f9480c5b3a33df0484577ad Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 23 Jul 2018 14:44:33 -0400 Subject: [PATCH 16/16] Restore the viewfields --- css/60_photos.css | 7 ------- 1 file changed, 7 deletions(-) diff --git a/css/60_photos.css b/css/60_photos.css index c1e4a0d9e..cc853900c 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -177,13 +177,6 @@ .layer-mapillary-images .viewfield-group * { fill: #55ff22; } -.layer-mapillary-images .viewfield-group .viewfield { - display: none; -} -.layer-mapillary-images .viewfield-group.selected .viewfield, -.layer-mapillary-images .viewfield-group .viewfield.pano { - display: inline; -} .layer-mapillary-images .sequence { stroke: #55ff22; }