From aa1130611c76a28492e282b409a15db4c3fe34d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 26 Mar 2016 16:12:31 +0100 Subject: [PATCH 01/39] indoor: ui-control attempts --- index.html | 7 ++ js/id/id.js | 19 ++++- js/id/renderer/features.js | 6 ++ js/id/ui.js | 5 ++ js/id/ui/indoor_mode.js | 157 +++++++++++++++++++++++++++++++++++++ 5 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 js/id/ui/indoor_mode.js diff --git a/index.html b/index.html index 80d184a2fe..368280ae66 100644 --- a/index.html +++ b/index.html @@ -94,6 +94,7 @@ + @@ -275,6 +276,12 @@ } ])); + id.connection().switch({ + "url": "http://localhost:3000", + "oauth_consumer_key": "y0SQSVjXlQRXgEefMNmJ6zSwFwI4kwXo7oUGI9eM", + "oauth_secret": "kg67RISYNj9rJ7GuUdGbPwBpRzouJvLulC7DOItB" + }); + }); diff --git a/js/id/id.js b/js/id/id.js index 0e8c416c64..e5e145afc1 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -2,7 +2,7 @@ window.iD = function () { window.locale.en = iD.data.en; window.locale.current('en'); - var dispatch = d3.dispatch('enter', 'exit'), + var dispatch = d3.dispatch('enter', 'exit', 'indoorMode'), context = {}; // https://github.com/openstreetmap/iD/issues/772 @@ -321,6 +321,23 @@ window.iD = function () { }; + /* Indoor mode */ + var indoorMode = false, indoorLevel; + + context.indoorMode = function () { + return indoorMode; + }; + + context.indoorLevel = function () { + return indoorLevel; + }; + + context.enterIndoorMode = function () { + console.log("context.enterIndoorMode called"); + indoorMode = true; + indoorLevel = prompt("Enter level", "1"); + }; + /* Init */ context.projection = iD.geo.RawMercator(); diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 98eed9ac58..3e0bde0636 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -358,6 +358,7 @@ iD.Features = function(context) { return false; }; + // entity, iD.Graph, enum(vertex,...) features.isHiddenChild = function(entity, resolver, geometry) { if (!_hidden.length) return false; if (!entity.version || geometry === 'point') return false; @@ -403,6 +404,11 @@ iD.Features = function(context) { return fn(entity, resolver, geometry); }; + /** + * @param d array of all entities + * @param resolver iD.Graph + * @returns {Array} filtered array + */ features.filter = function(d, resolver) { if (!_hidden.length) return d; diff --git a/js/id/ui.js b/js/id/ui.js index f06f3a8451..ec4fb1faad 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -54,8 +54,13 @@ iD.ui = function(context) { limiter.append('div') .attr('class', 'button-wrap col1') + .attr('style', 'margin-right: 60px') .call(iD.ui.Save(context)); + limiter.append('div') + .attr('class', 'button-wrap col1 ar') + .call(iD.ui.IndoorMode(context)); + bar.append('div') .attr('class', 'full-screen') .call(iD.ui.FullScreen(context)); diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js new file mode 100644 index 0000000000..d6b3947915 --- /dev/null +++ b/js/id/ui/indoor_mode.js @@ -0,0 +1,157 @@ +iD.ui.IndoorMode = function (context) { + var redrawButton = function (selection, enableButton) { + + //buttons.each(function(d) { + // d3.select(this) + // .call(iD.svg.Icon('#icon-' + d.id)); + //}); + + //update selection + buttons.select('span.label') + .text('Good'); + + if (context.indoorMode()) { + commands[0].title = "Level " + context.indoorLevel(); + //selection.selectAll('button').data(commands) + } + + buttons + .property('disabled', !enableButton) + .classed('hide', !enableButton) + //.each(function() { + // var selection = d3.select(this); + // if (selection.property('tooltipVisible')) { + // selection.call(tooltip.show); + // } + //}); + }; + + var initButtons = function (selection) { + var enterButtonTooltip = bootstrap.tooltip() + .placement('bottom') + .html(true) + .title(iD.ui.tooltipHtml('Enter indoor editing mode', iD.ui.cmd('⌘I'))); + + var exitButtonTooltip = bootstrap.tooltip() + .placement('bottom') + .html(true) + .title(iD.ui.tooltipHtml('Exit indoor editing mode', iD.ui.cmd('⌘I'))); + + + var enterButton = selection.append('button') + .attr('class', 'col6') + .on('click', function () { + context.enterIndoorMode(); + }) + .call(enterButtonTooltip); + + + var buttons = selection.selectAll('button'); + + //enter selection + buttons + .enter().append('button') + + + //update selection + buttons.append('span') + .attr('class', 'label') + .text(function (mode) { + return mode.title; + }); + + + buttons + .property('disabled', !true) + .classed('hide', !true); + + }; + + + return function (selection) { + + // draw both controls hidden + initButtons(selection); + + // enable one of them and update labels + redrawButton(selection, false); + + + var keybinding = d3.keybinding('indoor') + .on(commands[0].cmd, function () { + d3.event.preventDefault(); + commands[0].action(); + }) + //.on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); }); + + d3.select(document) + .call(keybinding); + + //context.history() + // .on('change.undo_redo', update); + + context + .on('enter.indoor_mode', update) + //.on('indoorModeChanged.indoor_mode', update); + + function update() { + var enableButton = context.indoorMode(); + + if (!enableButton) { + var graph = context.graph(); + var ids = context.selectedIDs(); + var entities = ids.map(function (id) { + return graph.entity(id); + }); + var hasIndoorRelatedTag = function (e) { + return e.tags.level || e.tags.indoor || e.tags.building; + }; + enableButton = entities.some(hasIndoorRelatedTag); + } + + console.log("context.on(enter) called"); + console.log('enableIndoor', enableButton) + redrawButton(selection, enableButton); + } + }; +}; + +/* + + init + - vždy : vytvořit button, nastavit .hide + - vždy : vytvořit select+buton, .hide + + + update + - indoorMode : nastavit správný level + -vždy unhide to správné + + - přidat selectbox, přepsat text na X + + + + + + + */ + + + + + + + + + + + + + + + + + + + + From 24cfd5ed49e0dcef336de33410095f1c5db3c376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Thu, 7 Apr 2016 23:29:56 +0200 Subject: [PATCH 02/39] indoor: ui-control works, needs refactor --- css/app.css | 6 ++ js/id/id.js | 27 ++++++-- js/id/renderer/map.js | 45 +++++++++++++ js/id/ui.js | 2 +- js/id/ui/indoor_mode.js | 139 +++++++++++++++++++++------------------- 5 files changed, 145 insertions(+), 74 deletions(-) diff --git a/css/app.css b/css/app.css index 22f85f441e..f1824e069d 100644 --- a/css/app.css +++ b/css/app.css @@ -485,6 +485,12 @@ button.save.has-count .count::before { border-right: 6px solid rgba(255,255,255,.5); } +.indoormode-level-combo .combobox-input, +.indoormode-level-combo .combobox-caret { + height: 40px; + border-radius: 4px 0 0 4px; +} + /* Icons */ .icon { diff --git a/js/id/id.js b/js/id/id.js index e5e145afc1..170cd39858 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -2,7 +2,7 @@ window.iD = function () { window.locale.en = iD.data.en; window.locale.current('en'); - var dispatch = d3.dispatch('enter', 'exit', 'indoorMode'), + var dispatch = d3.dispatch('enter', 'exit', 'indoor'), context = {}; // https://github.com/openstreetmap/iD/issues/772 @@ -322,20 +322,33 @@ window.iD = function () { /* Indoor mode */ - var indoorMode = false, indoorLevel; + var indoorMode = false, indoorLevel = '0'; + // return d3.rebind(indoor, dispatch, 'on'); + context.indoorMode = function () { return indoorMode; }; - context.indoorLevel = function () { + context.indoorLevel = function (newLevel) { + if (newLevel && newLevel !== indoorLevel) { + indoorLevel = newLevel; + map.redraw(); //TODO event? + + } return indoorLevel; }; - context.enterIndoorMode = function () { - console.log("context.enterIndoorMode called"); - indoorMode = true; - indoorLevel = prompt("Enter level", "1"); + context.toggleIndoorMode = function () { + console.log("context.toggleIndoorMode called"); + indoorMode = !indoorMode; + dispatch.indoor(); //update combo + + map.redraw(); //TODO event? + }; + + context.indoorLevels = function () { + return [-1, 0, 1, 2] }; /* Init */ diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 63164fca26..221084f292 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -140,8 +140,52 @@ iD.Map = function(context) { } } + //filtering data + if (context.indoorMode()) { + var levelRange = /(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?/; // alowing untrimed string (not sure..) + + var inRange = function (value, rangeText) { + var range = rangeText && levelRange.exec(rangeText); + + if (!range) { //blank text OR not matched + return false; + } + + if (range[2] === undefined && range[4] == undefined) { //exact match + if (range[1] === value) { + return true; + } + } + else if (range[2] === '-') { // range from - to + if (range[1] <= value && range[3] >= value) { + return true; + } + } + else { // range list + if (range[0].split(';').indexOf(value) !== -1) { + return true; + } + } + + return false; + }; + + data = data.filter(function (entity) { + var current = context.indoorLevel(); + + if (inRange(current, entity.tags.level)) { + return true; + } + if (inRange(current, entity.tags.repeat_on)) { + return true; + } + return false; + }); + } + data = features.filter(data, graph); + //surface = d3 selection of "'s surface for entities" surface .call(drawVertices, graph, data, filter, map.extent(), map.zoom()) .call(drawLines, graph, data, filter) @@ -202,6 +246,7 @@ iD.Map = function(context) { return true; } + map.redraw = redraw; function redraw(difference, extent) { if (!surface || !redrawEnabled) return; diff --git a/js/id/ui.js b/js/id/ui.js index ec4fb1faad..3ef93b11dc 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -58,7 +58,7 @@ iD.ui = function(context) { .call(iD.ui.Save(context)); limiter.append('div') - .attr('class', 'button-wrap col1 ar') + .attr('class', 'button-wrap col1') .call(iD.ui.IndoorMode(context)); bar.append('div') diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index d6b3947915..6a8693037d 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -1,98 +1,97 @@ iD.ui.IndoorMode = function (context) { - var redrawButton = function (selection, enableButton) { + var updateControls = function (selection, enableButton) { - //buttons.each(function(d) { - // d3.select(this) - // .call(iD.svg.Icon('#icon-' + d.id)); - //}); - //update selection - buttons.select('span.label') - .text('Good'); + var enterButton = selection.select('.indoormode-enter-button'); + var indoorControl = selection.select('.indoormode-control'); if (context.indoorMode()) { - commands[0].title = "Level " + context.indoorLevel(); - //selection.selectAll('button').data(commands) - } + console.log("updateControls indoor=true"); + enterButton.classed('hide', true); + indoorControl.classed('hide', false); + indoorControl.select('.combobox-input') + .attr('placeholder', context.indoorLevel()) + .value('') + .call(d3.combobox().data(context.indoorLevels().map(comboValues))); - buttons - .property('disabled', !enableButton) - .classed('hide', !enableButton) - //.each(function() { - // var selection = d3.select(this); - // if (selection.property('tooltipVisible')) { - // selection.call(tooltip.show); - // } - //}); + } + else { + enterButton.classed('hide', false).property('disabled', !enableButton); + indoorControl.classed('hide', true); + } }; - var initButtons = function (selection) { - var enterButtonTooltip = bootstrap.tooltip() - .placement('bottom') - .html(true) - .title(iD.ui.tooltipHtml('Enter indoor editing mode', iD.ui.cmd('⌘I'))); + var toggleIndoor = function () { + d3.event.preventDefault(); + context.toggleIndoorMode(); - var exitButtonTooltip = bootstrap.tooltip() + }; + + var buttonTooltip = function (description) { + return bootstrap.tooltip() .placement('bottom') .html(true) - .title(iD.ui.tooltipHtml('Exit indoor editing mode', iD.ui.cmd('⌘I'))); - - - var enterButton = selection.append('button') - .attr('class', 'col6') - .on('click', function () { - context.enterIndoorMode(); - }) - .call(enterButtonTooltip); - - - var buttons = selection.selectAll('button'); - - //enter selection - buttons - .enter().append('button') + .title(iD.ui.tooltipHtml(description, iD.ui.cmd('⌘⇧I'))); + }; + var setLevel = function () { + var input = d3.select(this); + var data = input.value(); //string! + if (data === '') return; //blank value + console.log('setLevel', data); - //update selection - buttons.append('span') - .attr('class', 'label') - .text(function (mode) { - return mode.title; - }); + input + .attr('placeholder', data) + .value(''); + context.indoorLevel(data); - buttons - .property('disabled', !true) - .classed('hide', !true); + }; + var createControls = function (selection) { + var enterButton = selection.append('button') + .attr('class', 'indoormode-enter-button col12 ') + .on('click', toggleIndoor) + .call(buttonTooltip('Enter indoor editing mode')) + .append('span').attr('class', 'label').text('Indoor'); + + + var indoorControl = selection.append('div') + .attr('class', 'indoormode-control joined '); + + indoorControl.append('div') + .attr('class', 'col8 indoormode-level-combo') + .append('input') + .attr('type', 'text') + .call(d3.combobox().data([0, 1, 2, 3, 4, 5, 6].map(comboValues))) + .on('blur', setLevel) + .on('change', setLevel) + + indoorControl.append('button') + .attr('class', 'col4') + .on('click', toggleIndoor) + .call(buttonTooltip('Exit indoor editing mode')) + .call(iD.svg.Icon('#icon-close')); + + enterButton.classed('hide', false).property('disabled', true); + indoorControl.classed('hide', true); }; return function (selection) { // draw both controls hidden - initButtons(selection); - - // enable one of them and update labels - redrawButton(selection, false); - + createControls(selection); var keybinding = d3.keybinding('indoor') - .on(commands[0].cmd, function () { - d3.event.preventDefault(); - commands[0].action(); - }) - //.on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); }); + .on(iD.ui.cmd('⌘I'), toggleIndoor) d3.select(document) .call(keybinding); - //context.history() - // .on('change.undo_redo', update); - context .on('enter.indoor_mode', update) - //.on('indoorModeChanged.indoor_mode', update); + .on('indoor.indoor_mode', update); function update() { var enableButton = context.indoorMode(); @@ -111,9 +110,17 @@ iD.ui.IndoorMode = function (context) { console.log("context.on(enter) called"); console.log('enableIndoor', enableButton) - redrawButton(selection, enableButton); + updateControls(selection, enableButton); } }; + + + function comboValues(d) { + return { + value: d.toString(), + title: d.toString() + }; + } }; /* From 57bead6fa75e38189f833171d0546379e93799d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 15:21:54 +0200 Subject: [PATCH 03/39] indoor: ui-control refactoring --- js/id/ui/indoor_mode.js | 147 ++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 96 deletions(-) diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index 6a8693037d..bff191c07a 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -1,62 +1,17 @@ iD.ui.IndoorMode = function (context) { - var updateControls = function (selection, enableButton) { + var enterButton, indoorControl; - var enterButton = selection.select('.indoormode-enter-button'); - var indoorControl = selection.select('.indoormode-control'); - - if (context.indoorMode()) { - console.log("updateControls indoor=true"); - enterButton.classed('hide', true); - indoorControl.classed('hide', false); - indoorControl.select('.combobox-input') - .attr('placeholder', context.indoorLevel()) - .value('') - .call(d3.combobox().data(context.indoorLevels().map(comboValues))); - - } - else { - enterButton.classed('hide', false).property('disabled', !enableButton); - indoorControl.classed('hide', true); - } - }; - - var toggleIndoor = function () { - d3.event.preventDefault(); - context.toggleIndoorMode(); - - }; - - var buttonTooltip = function (description) { - return bootstrap.tooltip() - .placement('bottom') - .html(true) - .title(iD.ui.tooltipHtml(description, iD.ui.cmd('⌘⇧I'))); - }; - - var setLevel = function () { - var input = d3.select(this); - var data = input.value(); //string! - if (data === '') return; //blank value - console.log('setLevel', data); - - input - .attr('placeholder', data) - .value(''); - - context.indoorLevel(data); - - }; - - var createControls = function (selection) { - var enterButton = selection.append('button') + function createControls(selection) { + enterButton = selection.append('button') .attr('class', 'indoormode-enter-button col12 ') .on('click', toggleIndoor) - .call(buttonTooltip('Enter indoor editing mode')) + .call(buttonTooltip('Enter indoor editing mode')); + enterButton .append('span').attr('class', 'label').text('Indoor'); - var indoorControl = selection.append('div') + indoorControl = selection.append('div') .attr('class', 'indoormode-control joined '); indoorControl.append('div') @@ -65,7 +20,7 @@ iD.ui.IndoorMode = function (context) { .attr('type', 'text') .call(d3.combobox().data([0, 1, 2, 3, 4, 5, 6].map(comboValues))) .on('blur', setLevel) - .on('change', setLevel) + .on('change', setLevel); indoorControl.append('button') .attr('class', 'col4') @@ -75,16 +30,14 @@ iD.ui.IndoorMode = function (context) { enterButton.classed('hide', false).property('disabled', true); indoorControl.classed('hide', true); - }; + } return function (selection) { - - // draw both controls hidden createControls(selection); var keybinding = d3.keybinding('indoor') - .on(iD.ui.cmd('⌘I'), toggleIndoor) + .on(iD.ui.cmd('⌘I'), toggleIndoor); d3.select(document) .call(keybinding); @@ -109,56 +62,58 @@ iD.ui.IndoorMode = function (context) { } console.log("context.on(enter) called"); - console.log('enableIndoor', enableButton) + console.log('enableIndoor', enableButton); updateControls(selection, enableButton); } }; + function updateControls(selection, enableButton) { + if (context.indoorMode()) { + console.log("updateControls indoor=true"); + enterButton.classed('hide', true); + indoorControl.classed('hide', false); + indoorControl.select('.combobox-input') + .attr('placeholder', context.indoorLevel()) + .value('') + .call(d3.combobox().data(context.indoorLevels().map(comboValues))); - function comboValues(d) { - return { - value: d.toString(), - title: d.toString() - }; + } + else { + enterButton.classed('hide', false).property('disabled', !enableButton); + indoorControl.classed('hide', true); + } } -}; - -/* - - init - - vždy : vytvořit button, nastavit .hide - - vždy : vytvořit select+buton, .hide - - - update - - indoorMode : nastavit správný level - -vždy unhide to správné - - - přidat selectbox, přepsat text na X - - - - - - - */ - - - - - - - - - - - - - + function toggleIndoor() { + d3.event.preventDefault(); + context.toggleIndoorMode(); + } + function buttonTooltip(description) { + return bootstrap.tooltip() + .placement('bottom') + .html(true) + .title(iD.ui.tooltipHtml(description, iD.ui.cmd('⌘⇧I'))); + } + function setLevel() { + var input = d3.select(this); + var data = input.value(); //string! + if (data === '') return; //blank value + console.log('setLevel', data); + input + .attr('placeholder', data) + .value(''); + context.indoorLevel(data); + } + function comboValues(d) { + return { + value: d.toString(), + title: d.toString() + }; + } +}; From 26c8a54cb76e22b4e2e582634651b8b65e90ad0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 16:34:53 +0200 Subject: [PATCH 04/39] indoor: basic rendering --- css/map.css | 21 +++++++++++++++++++++ data/presets.yaml | 7 +++++++ data/presets/fields.json | 5 +++++ data/presets/fields/repeat_on.json | 5 +++++ data/presets/presets.json | 17 +++++++++++++++++ data/presets/presets/indoor.json | 17 +++++++++++++++++ data/taginfo.json | 3 +++ dist/locales/en.json | 7 +++++++ index.html | 10 +++++----- js/id/id.js | 2 ++ js/id/renderer/map.js | 10 +++------- js/id/svg/tag_classes.js | 2 +- js/id/ui/indoor_mode.js | 6 +++--- 13 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 data/presets/fields/repeat_on.json create mode 100644 data/presets/presets/indoor.json diff --git a/css/map.css b/css/map.css index c7a166e438..e27dcb5798 100644 --- a/css/map.css +++ b/css/map.css @@ -1306,6 +1306,27 @@ path.fill.tag-amenity-shelter { background-color: rgba(224, 110, 95, 0.3); } +.indoor-mode path.fill.tag-building { + clip-path: none !important; + stroke-width: 120px; +} + + +/* Indoor features */ +path.fill.tag-indoor { + display: none; +} + +.indoor-mode path.fill.tag-indoor { + display: block; + fill: rgba(169, 169, 169, 0.3); + stroke-width: 30px; +} +.indoor-mode path.stroke.tag-indoor { + stroke: rgb(169, 169, 169); +} + + /* Labels / Markers */ diff --git a/data/presets.yaml b/data/presets.yaml index d42ade6eed..3281e44113 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -770,6 +770,9 @@ en: religion: # 'religion=*' label: Religion + repeat_on: + # 'repeat_on=*' + label: Repeat on restriction: # 'restriction=*' label: Type @@ -2312,6 +2315,10 @@ en: # historic=wayside_shrine name: Wayside Shrine terms: "" + indoor: + # 'indoor=*' + name: Indoor object + terms: "" junction: # junction=yes name: Junction diff --git a/data/presets/fields.json b/data/presets/fields.json index ee2a6a2f15..f6f509daa3 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -1015,6 +1015,11 @@ "type": "combo", "label": "Religion" }, + "repeat_on": { + "key": "repeat_on", + "type": "combo", + "label": "Repeat on" + }, "restriction": { "key": "restriction", "type": "combo", diff --git a/data/presets/fields/repeat_on.json b/data/presets/fields/repeat_on.json new file mode 100644 index 0000000000..42fce3ff00 --- /dev/null +++ b/data/presets/fields/repeat_on.json @@ -0,0 +1,5 @@ +{ + "key": "repeat_on", + "type": "combo", + "label": "Repeat on" +} diff --git a/data/presets/presets.json b/data/presets/presets.json index f693661762..c967d99d8b 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -5111,6 +5111,23 @@ }, "name": "Wayside Shrine" }, + "indoor": { + "fields": [ + "name", + "level", + "repeat_on" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "indoor": "*" + }, + "matchScore": 0.4, + "terms": [], + "name": "Indoor object" + }, "junction": { "geometry": [ "vertex", diff --git a/data/presets/presets/indoor.json b/data/presets/presets/indoor.json new file mode 100644 index 0000000000..95ffa3a0ea --- /dev/null +++ b/data/presets/presets/indoor.json @@ -0,0 +1,17 @@ +{ + "fields": [ + "name", + "level", + "repeat_on" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "indoor": "*" + }, + "matchScore": 0.4, + "terms": [], + "name": "Indoor object" +} diff --git a/data/taginfo.json b/data/taginfo.json index 0bf0bba64e..7977fbf77e 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -1141,6 +1141,9 @@ "key": "historic", "value": "wayside_shrine" }, + { + "key": "indoor" + }, { "key": "junction", "value": "yes" diff --git a/dist/locales/en.json b/dist/locales/en.json index bb3ee333e3..bbc44f59e4 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1278,6 +1278,9 @@ "religion": { "label": "Religion" }, + "repeat_on": { + "label": "Repeat on" + }, "restriction": { "label": "Type" }, @@ -2645,6 +2648,10 @@ "name": "Wayside Shrine", "terms": "" }, + "indoor": { + "name": "Indoor object", + "terms": "" + }, "junction": { "name": "Junction", "terms": "" diff --git a/index.html b/index.html index 368280ae66..f39ad0406b 100644 --- a/index.html +++ b/index.html @@ -276,11 +276,11 @@ } ])); - id.connection().switch({ - "url": "http://localhost:3000", - "oauth_consumer_key": "y0SQSVjXlQRXgEefMNmJ6zSwFwI4kwXo7oUGI9eM", - "oauth_secret": "kg67RISYNj9rJ7GuUdGbPwBpRzouJvLulC7DOItB" - }); +// id.connection().switch({ +// "url": "http://localhost:3000", +// "oauth_consumer_key": "y0SQSVjXlQRXgEefMNmJ6zSwFwI4kwXo7oUGI9eM", +// "oauth_secret": "kg67RISYNj9rJ7GuUdGbPwBpRzouJvLulC7DOItB" +// }); }); diff --git a/js/id/id.js b/js/id/id.js index 170cd39858..89aa8e2ec9 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -344,6 +344,8 @@ window.iD = function () { indoorMode = !indoorMode; dispatch.indoor(); //update combo + context.surface().classed('indoor-mode', indoorMode); + map.redraw(); //TODO event? }; diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 221084f292..d29cc4812f 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -173,13 +173,9 @@ iD.Map = function(context) { data = data.filter(function (entity) { var current = context.indoorLevel(); - if (inRange(current, entity.tags.level)) { - return true; - } - if (inRange(current, entity.tags.repeat_on)) { - return true; - } - return false; + return entity.tags.building + || inRange(current, entity.tags.level) + || inRange(current, entity.tags.repeat_on); }); } diff --git a/js/id/svg/tag_classes.js b/js/id/svg/tag_classes.js index 63774ec49e..771f065b53 100644 --- a/js/id/svg/tag_classes.js +++ b/js/id/svg/tag_classes.js @@ -2,7 +2,7 @@ iD.svg.TagClasses = function() { var primaries = [ 'building', 'highway', 'railway', 'waterway', 'aeroway', 'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse', - 'leisure', 'place' + 'leisure', 'place', 'indoor' ], statuses = [ 'proposed', 'construction', 'disused', 'abandoned', 'dismantled', diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index bff191c07a..34b266a1d7 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -28,7 +28,7 @@ iD.ui.IndoorMode = function (context) { .call(buttonTooltip('Exit indoor editing mode')) .call(iD.svg.Icon('#icon-close')); - enterButton.classed('hide', false).property('disabled', true); + enterButton.classed('hide', true); //.property('disabled', true); indoorControl.classed('hide', true); } @@ -56,7 +56,7 @@ iD.ui.IndoorMode = function (context) { return graph.entity(id); }); var hasIndoorRelatedTag = function (e) { - return e.tags.level || e.tags.indoor || e.tags.building; + return e.tags.level || e.tags.repeat_on || e.tags.indoor || e.tags.building; }; enableButton = entities.some(hasIndoorRelatedTag); } @@ -79,7 +79,7 @@ iD.ui.IndoorMode = function (context) { } else { - enterButton.classed('hide', false).property('disabled', !enableButton); + enterButton.classed('hide', !enableButton); //.property('disabled', !enableButton); indoorControl.classed('hide', true); } } From d67fafa22e2002b4fb166da9778b0e7808748ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 16:44:18 +0200 Subject: [PATCH 05/39] indoor: choose level of selected entitry --- js/id/id.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/js/id/id.js b/js/id/id.js index 89aa8e2ec9..df7f6ac1d6 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -342,11 +342,20 @@ window.iD = function () { context.toggleIndoorMode = function () { console.log("context.toggleIndoorMode called"); indoorMode = !indoorMode; - dispatch.indoor(); //update combo context.surface().classed('indoor-mode', indoorMode); + var selected = context.selectedIDs(); + if (indoorMode && selected.length) { + var entity = context.graph().entity(selected[0]); + if (entity.tags.level) + indoorLevel = entity.tags.level.replace(/[-;].+/, ''); + else if (entity.tags.repeat_on) + indoorLevel = entity.tags.repeat_on.replace(/[-;].+/, ''); + } + map.redraw(); //TODO event? + dispatch.indoor(); //update combo }; context.indoorLevels = function () { From c9bf632d8779898720759438bd42706a90f8d3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 16:45:19 +0200 Subject: [PATCH 06/39] indoor: refactor event name --- js/id/id.js | 4 ++-- js/id/ui/indoor_mode.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/id/id.js b/js/id/id.js index df7f6ac1d6..2a18c531d6 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -2,7 +2,7 @@ window.iD = function () { window.locale.en = iD.data.en; window.locale.current('en'); - var dispatch = d3.dispatch('enter', 'exit', 'indoor'), + var dispatch = d3.dispatch('enter', 'exit', 'indoorLevelChanged'), context = {}; // https://github.com/openstreetmap/iD/issues/772 @@ -355,7 +355,7 @@ window.iD = function () { } map.redraw(); //TODO event? - dispatch.indoor(); //update combo + dispatch.indoorLevelChanged(); //update combo }; context.indoorLevels = function () { diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index 34b266a1d7..b08ad99f0e 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -44,7 +44,7 @@ iD.ui.IndoorMode = function (context) { context .on('enter.indoor_mode', update) - .on('indoor.indoor_mode', update); + .on('indoorLevelChanged.indoor_mode', update); function update() { var enableButton = context.indoorMode(); From 5fde1b6fee578cb5f848a600e51de3eb9614721f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 17:00:52 +0200 Subject: [PATCH 07/39] indoor: hide building label-icons (added classes) --- css/map.css | 7 +++++++ js/id/svg/labels.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/css/map.css b/css/map.css index e27dcb5798..6ce08ef96c 100644 --- a/css/map.css +++ b/css/map.css @@ -1325,7 +1325,14 @@ path.fill.tag-indoor { .indoor-mode path.stroke.tag-indoor { stroke: rgb(169, 169, 169); } +.indoor-mode text { + display: none; +} +.indoor-mode use.icon.areaicon.tag-building, +.indoor-mode text.arealabel.tag-building { + display: none; +} /* Labels / Markers */ diff --git a/js/id/svg/labels.js b/js/id/svg/labels.js index 926414b31e..2aaf96f42e 100644 --- a/js/id/svg/labels.js +++ b/js/id/svg/labels.js @@ -166,7 +166,7 @@ iD.svg.Labels = function(projection, context) { icons.enter() .append('use') - .attr('class', 'icon areaicon') + .attr('class', function(d, i) { return 'icon areaicon ' + labels[i].classes; }) .attr('width', '18px') .attr('height', '18px'); From 86a25e74a217bb77c55f2f1106b4cdd152694c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 17:20:45 +0200 Subject: [PATCH 08/39] indoor: new objects with correct level --- js/id/id.js | 4 ++-- js/id/modes/add_area.js | 4 ++++ js/id/modes/add_line.js | 4 ++++ js/id/modes/add_point.js | 4 ++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/js/id/id.js b/js/id/id.js index 2a18c531d6..c5f410c3b5 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -349,9 +349,9 @@ window.iD = function () { if (indoorMode && selected.length) { var entity = context.graph().entity(selected[0]); if (entity.tags.level) - indoorLevel = entity.tags.level.replace(/[-;].+/, ''); + indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).+/, '$1'); else if (entity.tags.repeat_on) - indoorLevel = entity.tags.repeat_on.replace(/[-;].+/, ''); + indoorLevel = entity.tags.repeat_on.replace(/(-?\d+(\.\d+)?).+/, '$1'); } map.redraw(); //TODO event? diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js index 2f529b9bf9..486039aa48 100644 --- a/js/id/modes/add_area.js +++ b/js/id/modes/add_area.js @@ -19,6 +19,10 @@ iD.modes.AddArea = function(context) { node = iD.Node({loc: loc}), way = iD.Way({tags: defaultTags}); + if (context.indoorMode()) { + way.tags.level = context.indoorLevel(); + } + context.perform( iD.actions.AddEntity(node), iD.actions.AddEntity(way), diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 3dc014c728..97a00c3d7c 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -18,6 +18,10 @@ iD.modes.AddLine = function(context) { node = iD.Node({loc: loc}), way = iD.Way(); + if (context.indoorMode()) { + way.tags.level = context.indoorLevel(); + } + context.perform( iD.actions.AddEntity(node), iD.actions.AddEntity(way), diff --git a/js/id/modes/add_point.js b/js/id/modes/add_point.js index e7519e3b83..1addf242c3 100644 --- a/js/id/modes/add_point.js +++ b/js/id/modes/add_point.js @@ -18,6 +18,10 @@ iD.modes.AddPoint = function(context) { function add(loc) { var node = iD.Node({loc: loc}); + if (context.indoorMode()) { + node.tags.level = context.indoorLevel(); + } + context.perform( iD.actions.AddEntity(node), t('operations.add.annotation.point')); From 493147d3dcc13836b79bfbb1bb63dbd4002606c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 17:57:42 +0200 Subject: [PATCH 09/39] indoor: moved filter to features --- js/id/renderer/features.js | 44 ++++++++++++++++++++++++++++++++++++++ js/id/renderer/map.js | 40 ---------------------------------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 3e0bde0636..c63173bcd9 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -410,6 +410,11 @@ iD.Features = function(context) { * @returns {Array} filtered array */ features.filter = function(d, resolver) { + + if (context.indoorMode()) { + d = filterByLevel(d); + } + if (!_hidden.length) return d; var result = []; @@ -422,5 +427,44 @@ iD.Features = function(context) { return result; }; + function filterByLevel(data) { + var levelRange = /(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?/; // alowing untrimed string (not sure..) + + return data.filter(function (entity) { + var current = context.indoorLevel(); + + return entity.tags.building + || inRange(current, entity.tags.level) + || inRange(current, entity.tags.repeat_on); + }); + + function inRange(value, rangeText) { + var range = rangeText && levelRange.exec(rangeText); + + if (!range) { //blank text OR not matched + return false; + } + + if (range[2] === undefined && range[4] == undefined) { //exact match + if (range[1] === value) { + return true; + } + } + else if (range[2] === '-') { // range from - to + if (range[1] <= value && range[3] >= value) { + return true; + } + } + else { // range list + if (range[0].split(';').indexOf(value) !== -1) { + return true; + } + } + + return false; + } + } + + return d3.rebind(features, dispatch, 'on'); }; diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index d29cc4812f..4f2f5be369 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -140,48 +140,8 @@ iD.Map = function(context) { } } - //filtering data - if (context.indoorMode()) { - var levelRange = /(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?/; // alowing untrimed string (not sure..) - - var inRange = function (value, rangeText) { - var range = rangeText && levelRange.exec(rangeText); - - if (!range) { //blank text OR not matched - return false; - } - - if (range[2] === undefined && range[4] == undefined) { //exact match - if (range[1] === value) { - return true; - } - } - else if (range[2] === '-') { // range from - to - if (range[1] <= value && range[3] >= value) { - return true; - } - } - else { // range list - if (range[0].split(';').indexOf(value) !== -1) { - return true; - } - } - - return false; - }; - - data = data.filter(function (entity) { - var current = context.indoorLevel(); - - return entity.tags.building - || inRange(current, entity.tags.level) - || inRange(current, entity.tags.repeat_on); - }); - } - data = features.filter(data, graph); - //surface = d3 selection of "'s surface for entities" surface .call(drawVertices, graph, data, filter, map.extent(), map.zoom()) .call(drawLines, graph, data, filter) From 757306a394bef2e174d25ea1e14bef6d6ab85b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sat, 16 Apr 2016 17:04:32 +0200 Subject: [PATCH 10/39] indoor: fix build --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 9752cec700..75f0f0a2d1 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,7 @@ dist/iD.js: \ js/id/ui/full_screen.js \ js/id/ui/geolocate.js \ js/id/ui/help.js \ + js/id/ui/indoor_mode.js \ js/id/ui/info.js \ js/id/ui/inspector.js \ js/id/ui/intro.js \ From c2a67fa09217046934429e435a8c4fd6b6876ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Sun, 17 Apr 2016 19:14:13 +0200 Subject: [PATCH 11/39] indoor: fix hiding building label --- css/map.css | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/css/map.css b/css/map.css index 6ce08ef96c..fdf5f51ac7 100644 --- a/css/map.css +++ b/css/map.css @@ -1306,17 +1306,15 @@ path.fill.tag-amenity-shelter { background-color: rgba(224, 110, 95, 0.3); } -.indoor-mode path.fill.tag-building { - clip-path: none !important; - stroke-width: 120px; -} - /* Indoor features */ path.fill.tag-indoor { display: none; } - +.indoor-mode path.fill.tag-building { + clip-path: none !important; + stroke-width: 30px; +} .indoor-mode path.fill.tag-indoor { display: block; fill: rgba(169, 169, 169, 0.3); @@ -1325,12 +1323,9 @@ path.fill.tag-indoor { .indoor-mode path.stroke.tag-indoor { stroke: rgb(169, 169, 169); } -.indoor-mode text { - display: none; -} - .indoor-mode use.icon.areaicon.tag-building, -.indoor-mode text.arealabel.tag-building { +.indoor-mode text.arealabel.tag-building, +.indoor-mode text.arealabel-halo.tag-building { display: none; } From c1be9e26392b49c0167f79129f8333daf4c3103c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 18 Apr 2016 11:39:33 +0200 Subject: [PATCH 12/39] indoor: better indoor-mode control with +- --- css/app.css | 16 ++++++++++++++-- js/id/ui.js | 2 +- js/id/ui/indoor_mode.js | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/css/app.css b/css/app.css index f1824e069d..01de10137e 100644 --- a/css/app.css +++ b/css/app.css @@ -485,11 +485,23 @@ button.save.has-count .count::before { border-right: 6px solid rgba(255,255,255,.5); } -.indoormode-level-combo .combobox-input, -.indoormode-level-combo .combobox-caret { +.indoormode-level-combo .combobox-input { height: 40px; border-radius: 4px 0 0 4px; } +.indoormode-level-combo .combobox-caret { + display: none; +} +.indoormode-control .spin-control { + height: 40px; + width: 41.6666%; /* force .col5 */ + border-right: 1px solid rgba(0,0,0,.5); + margin-left: 0; + background: white; +} +.indoormode-control .spin-control button { + border-right: 0; +} /* Icons */ diff --git a/js/id/ui.js b/js/id/ui.js index 3ef93b11dc..83232f6b3d 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -58,7 +58,7 @@ iD.ui = function(context) { .call(iD.ui.Save(context)); limiter.append('div') - .attr('class', 'button-wrap col1') + .attr('class', 'button-wrap col2') .call(iD.ui.IndoorMode(context)); bar.append('div') diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index b08ad99f0e..cde03ab755 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -3,10 +3,11 @@ iD.ui.IndoorMode = function (context) { var enterButton, indoorControl; function createControls(selection) { + enterButton = selection.append('button') .attr('class', 'indoormode-enter-button col12 ') .on('click', toggleIndoor) - .call(buttonTooltip('Enter indoor editing mode')); + .call(buttonTooltip('Indoor mode helps you creating indoor objects by filtering only the selected level. Even for currently added features.', '⌘⇧I')); enterButton .append('span').attr('class', 'label').text('Indoor'); @@ -14,20 +15,32 @@ iD.ui.IndoorMode = function (context) { indoorControl = selection.append('div') .attr('class', 'indoormode-control joined '); - indoorControl.append('div') - .attr('class', 'col8 indoormode-level-combo') + indoorControl.append('div') //combo + .attr('class', 'col4 indoormode-level-combo') .append('input') .attr('type', 'text') .call(d3.combobox().data([0, 1, 2, 3, 4, 5, 6].map(comboValues))) .on('blur', setLevel) .on('change', setLevel); - + spinControl = indoorControl.append('div'); indoorControl.append('button') - .attr('class', 'col4') + .attr('class', 'col3') .on('click', toggleIndoor) - .call(buttonTooltip('Exit indoor editing mode')) + .call(buttonTooltip('Exit indoor editing mode', '⌘⇧I')) .call(iD.svg.Icon('#icon-close')); + var spinControl; + spinControl + .attr('class', 'spin-control col5'); + spinControl.append('button') + .attr('class', 'increment') + .on('click', levelChangeFunc(+1)) + .call(buttonTooltip('Level +1')); + spinControl.append('button') + .attr('class', 'decrement') + .on('click', levelChangeFunc(-1)) + .call(buttonTooltip('Level -1')); + enterButton.classed('hide', true); //.property('disabled', true); indoorControl.classed('hide', true); } @@ -37,7 +50,7 @@ iD.ui.IndoorMode = function (context) { createControls(selection); var keybinding = d3.keybinding('indoor') - .on(iD.ui.cmd('⌘I'), toggleIndoor); + .on(iD.ui.cmd('⌘⇧I'), toggleIndoor); d3.select(document) .call(keybinding); @@ -87,14 +100,21 @@ iD.ui.IndoorMode = function (context) { function toggleIndoor() { d3.event.preventDefault(); context.toggleIndoorMode(); + } + function levelChangeFunc(dif) { + return function () { + d3.event.preventDefault(); + context.indoorLevel(parseFloat(context.indoorLevel()) + dif + ""); + indoorControl.select('input').attr('placeholder', context.indoorLevel()); + } } - function buttonTooltip(description) { + function buttonTooltip(description, cmd) { return bootstrap.tooltip() .placement('bottom') .html(true) - .title(iD.ui.tooltipHtml(description, iD.ui.cmd('⌘⇧I'))); + .title(iD.ui.tooltipHtml(description, cmd ? iD.ui.cmd(cmd) : undefined)); } function setLevel() { From ca7ed1991cb4b982d0cfbafecc4d1da0f1ac987b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 18 Apr 2016 14:06:41 +0200 Subject: [PATCH 13/39] indoor: refactored filtering using features.indoor --- data/core.yaml | 6 ++++ dist/locales/en.json | 8 +++++ js/id/id.js | 24 +++++++++---- js/id/renderer/features.js | 73 ++++++++++++++++++-------------------- 4 files changed, 65 insertions(+), 46 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 97c852e033..0ddb121000 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -325,6 +325,12 @@ en: past_future: description: Past/Future tooltip: "Proposed, Construction, Abandoned, Demolished, etc." + indoor_other_levels: + description: Indoor other levels + tooltip: "When in indoor mode, it hides all levels except the selected one." + indoor: + description: Indoor + tooltip: "All features with filled level or repeat_on." others: description: Others tooltip: "Everything Else" diff --git a/dist/locales/en.json b/dist/locales/en.json index bbc44f59e4..b8afb471db 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -395,6 +395,14 @@ "description": "Past/Future", "tooltip": "Proposed, Construction, Abandoned, Demolished, etc." }, + "indoor_other_levels": { + "description": "Indoor other levels", + "tooltip": "When in indoor mode, it hides all levels except the selected one." + }, + "indoor": { + "description": "Indoor", + "tooltip": "All features with filled level or repeat_on." + }, "others": { "description": "Others", "tooltip": "Everything Else" diff --git a/js/id/id.js b/js/id/id.js index c5f410c3b5..e822c8b151 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -322,7 +322,7 @@ window.iD = function () { /* Indoor mode */ - var indoorMode = false, indoorLevel = '0'; + var indoorMode = false, indoorLevel = '0', enabledFeaturesBeforeIndoor; // return d3.rebind(indoor, dispatch, 'on'); @@ -333,27 +333,37 @@ window.iD = function () { context.indoorLevel = function (newLevel) { if (newLevel && newLevel !== indoorLevel) { indoorLevel = newLevel; + features.reset(); map.redraw(); //TODO event? - } return indoorLevel; }; context.toggleIndoorMode = function () { - console.log("context.toggleIndoorMode called"); indoorMode = !indoorMode; - context.surface().classed('indoor-mode', indoorMode); - var selected = context.selectedIDs(); - if (indoorMode && selected.length) { - var entity = context.graph().entity(selected[0]); + var selectedFeature = context.selectedIDs(); + if (indoorMode && selectedFeature.length) { + var entity = context.graph().entity(selectedFeature[0]); if (entity.tags.level) indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).+/, '$1'); else if (entity.tags.repeat_on) indoorLevel = entity.tags.repeat_on.replace(/(-?\d+(\.\d+)?).+/, '$1'); } + if (indoorMode) { + enabledFeaturesBeforeIndoor = features.enabled(); + features.enable('indoor'); + features.enable('buildings'); + _.each(_.without(features.keys(), 'indoor', 'buildings'), features.disable); //without indoor to prevent selection loss + } + else { + _.each(features.keys(), features.disable); + _.each(enabledFeaturesBeforeIndoor, features.enable); + } + + features.reset(); map.redraw(); //TODO event? dispatch.indoorLevelChanged(); //update combo }; diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index c63173bcd9..0068bec3f5 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -100,10 +100,22 @@ iD.Features = function(context) { ); }, 250); + defineFeature('indoor_other_levels', function isIndoorOtherLevel(entity) { //other then current level + var current = context.indoorLevel(); + + return (!!entity.tags.level && !inRange(current, entity.tags.level)) + || (!!entity.tags.repeat_on && !inRange(current, entity.tags.repeat_on)); + }); + + defineFeature('indoor', function isIndoorOther(entity) { + return !!entity.tags.level || !!entity.tags.repeat_on; + }); + defineFeature('landuse', function isLanduse(entity, resolver, geometry) { return geometry === 'area' && !_features.buildings.filter(entity) && - !_features.water.filter(entity); + !_features.water.filter(entity) && + !_features.indoor.filter(entity); }); defineFeature('boundaries', function isBoundary(entity) { @@ -348,7 +360,7 @@ iD.Features = function(context) { features.isHiddenFeature = function(entity, resolver, geometry) { if (!_hidden.length) return false; - if (!entity.version) return false; + if (!context.indoorMode() && !entity.version) return false; var matches = features.getMatches(entity, resolver, geometry); @@ -358,10 +370,9 @@ iD.Features = function(context) { return false; }; - // entity, iD.Graph, enum(vertex,...) features.isHiddenChild = function(entity, resolver, geometry) { if (!_hidden.length) return false; - if (!entity.version || geometry === 'point') return false; + if ((!context.indoorMode() && !entity.version) || geometry === 'point') return false; var parents = features.getParents(entity, resolver, geometry); if (!parents.length) return false; @@ -398,7 +409,7 @@ iD.Features = function(context) { features.isHidden = function(entity, resolver, geometry) { if (!_hidden.length) return false; - if (!entity.version) return false; + if (!context.indoorMode() && !entity.version) return false; var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature); return fn(entity, resolver, geometry); @@ -410,11 +421,6 @@ iD.Features = function(context) { * @returns {Array} filtered array */ features.filter = function(d, resolver) { - - if (context.indoorMode()) { - d = filterByLevel(d); - } - if (!_hidden.length) return d; var result = []; @@ -427,42 +433,31 @@ iD.Features = function(context) { return result; }; - function filterByLevel(data) { - var levelRange = /(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?/; // alowing untrimed string (not sure..) - - return data.filter(function (entity) { - var current = context.indoorLevel(); - return entity.tags.building - || inRange(current, entity.tags.level) - || inRange(current, entity.tags.repeat_on); - }); + var levelRangeRegExp = /(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?/; // alowing untrimed string (not sure..) + function inRange(level, rangeText) { + var range = rangeText && levelRangeRegExp.exec(rangeText); - function inRange(value, rangeText) { - var range = rangeText && levelRange.exec(rangeText); - - if (!range) { //blank text OR not matched - return false; - } + if (!range) { //blank text OR not matched + return false; + } - if (range[2] === undefined && range[4] == undefined) { //exact match - if (range[1] === value) { - return true; - } + if (range[2] === undefined && range[4] == undefined) { //exact match + if (range[1] === level) { + return true; } - else if (range[2] === '-') { // range from - to - if (range[1] <= value && range[3] >= value) { - return true; - } + } + else if (range[2] === '-') { // range from - to, only numeric comparison + if (parseFloat(range[1]) <= level && parseFloat(range[3]) >= level) { + return true; } - else { // range list - if (range[0].split(';').indexOf(value) !== -1) { - return true; - } + } + else { // range list + if (range[0].split(';').indexOf(level) !== -1) { + return true; } - - return false; } + return false; } From 439753c3cf13969d42f8567f3b22032646c329d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 18 Apr 2016 14:41:05 +0200 Subject: [PATCH 14/39] indoor: fix showing relevant points --- js/id/id.js | 2 +- js/id/renderer/features.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/js/id/id.js b/js/id/id.js index e822c8b151..cfff5879b0 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -356,7 +356,7 @@ window.iD = function () { enabledFeaturesBeforeIndoor = features.enabled(); features.enable('indoor'); features.enable('buildings'); - _.each(_.without(features.keys(), 'indoor', 'buildings'), features.disable); //without indoor to prevent selection loss + _.each(_.without(features.keys(), 'indoor', 'buildings', 'points'), features.disable); } else { _.each(features.keys(), features.disable); diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 0068bec3f5..ed25febcb9 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -100,11 +100,12 @@ iD.Features = function(context) { ); }, 250); - defineFeature('indoor_other_levels', function isIndoorOtherLevel(entity) { //other then current level + defineFeature('indoor_other_levels', function isIndoorOtherLevel(entity, resolver, geometry) { //other then current level, and also non-indoor-points var current = context.indoorLevel(); return (!!entity.tags.level && !inRange(current, entity.tags.level)) - || (!!entity.tags.repeat_on && !inRange(current, entity.tags.repeat_on)); + || (!!entity.tags.repeat_on && !inRange(current, entity.tags.repeat_on)) + || (geometry === 'point' && !entity.tags.level && !entity.tags.repeat_on); }); defineFeature('indoor', function isIndoorOther(entity) { From 7f53f2b0378cdd2d040d91621d571ac4c0271642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 18 Apr 2016 14:41:38 +0200 Subject: [PATCH 15/39] indoor: fix default tags on new entities --- js/id/modes/add_area.js | 21 ++++++++++++--------- js/id/modes/add_line.js | 14 +++++++++++--- js/id/modes/add_point.js | 12 ++++++++---- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js index 486039aa48..9d45b5e638 100644 --- a/js/id/modes/add_area.js +++ b/js/id/modes/add_area.js @@ -11,17 +11,20 @@ iD.modes.AddArea = function(context) { .tail(t('modes.add_area.tail')) .on('start', start) .on('startFromWay', startFromWay) - .on('startFromNode', startFromNode), - defaultTags = {area: 'yes'}; + .on('startFromNode', startFromNode); + + function defaultTags() { + var tags = {area: 'yes'}; + if (context.indoorMode()) { + tags.level = context.indoorLevel(); + } + return tags; + } function start(loc) { var graph = context.graph(), node = iD.Node({loc: loc}), - way = iD.Way({tags: defaultTags}); - - if (context.indoorMode()) { - way.tags.level = context.indoorLevel(); - } + way = iD.Way({tags: defaultTags()}); context.perform( iD.actions.AddEntity(node), @@ -35,7 +38,7 @@ iD.modes.AddArea = function(context) { function startFromWay(loc, edge) { var graph = context.graph(), node = iD.Node({loc: loc}), - way = iD.Way({tags: defaultTags}); + way = iD.Way({tags: defaultTags()}); context.perform( iD.actions.AddEntity(node), @@ -49,7 +52,7 @@ iD.modes.AddArea = function(context) { function startFromNode(node) { var graph = context.graph(), - way = iD.Way({tags: defaultTags}); + way = iD.Way({tags: defaultTags()}); context.perform( iD.actions.AddEntity(way), diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 97a00c3d7c..a34b87d519 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -13,10 +13,18 @@ iD.modes.AddLine = function(context) { .on('startFromWay', startFromWay) .on('startFromNode', startFromNode); + function defaultTags() { + var tags = {}; + if (context.indoorMode()) { + tags.level = context.indoorLevel(); + } + return tags; + } + function start(loc) { var baseGraph = context.graph(), node = iD.Node({loc: loc}), - way = iD.Way(); + way = iD.Way({tags: defaultTags()}); if (context.indoorMode()) { way.tags.level = context.indoorLevel(); @@ -33,7 +41,7 @@ iD.modes.AddLine = function(context) { function startFromWay(loc, edge) { var baseGraph = context.graph(), node = iD.Node({loc: loc}), - way = iD.Way(); + way = iD.Way({tags: defaultTags()}); context.perform( iD.actions.AddEntity(node), @@ -46,7 +54,7 @@ iD.modes.AddLine = function(context) { function startFromNode(node) { var baseGraph = context.graph(), - way = iD.Way(); + way = iD.Way({tags: defaultTags()}); context.perform( iD.actions.AddEntity(way), diff --git a/js/id/modes/add_point.js b/js/id/modes/add_point.js index 1addf242c3..b862b85ced 100644 --- a/js/id/modes/add_point.js +++ b/js/id/modes/add_point.js @@ -15,12 +15,16 @@ iD.modes.AddPoint = function(context) { .on('cancel', cancel) .on('finish', cancel); - function add(loc) { - var node = iD.Node({loc: loc}); - + function defaultTags() { + var tags = {}; if (context.indoorMode()) { - node.tags.level = context.indoorLevel(); + tags.level = context.indoorLevel(); } + return tags; + } + + function add(loc) { + var node = iD.Node({loc: loc, tags: defaultTags()}); context.perform( iD.actions.AddEntity(node), From bd8d92f30e28002358f3ba790204d317bc752f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 18 Apr 2016 14:48:27 +0200 Subject: [PATCH 16/39] indoor: level=* without whitespace --- js/id/renderer/features.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index ed25febcb9..a541fa7bb8 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -416,11 +416,6 @@ iD.Features = function(context) { return fn(entity, resolver, geometry); }; - /** - * @param d array of all entities - * @param resolver iD.Graph - * @returns {Array} filtered array - */ features.filter = function(d, resolver) { if (!_hidden.length) return d; @@ -435,7 +430,7 @@ iD.Features = function(context) { }; - var levelRangeRegExp = /(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?/; // alowing untrimed string (not sure..) + var levelRangeRegExp = /^(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?$/; function inRange(level, rangeText) { var range = rangeText && levelRangeRegExp.exec(rangeText); From 5f96d625e8fb8165902ee2d018fc866650967ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 18 Apr 2016 15:28:28 +0200 Subject: [PATCH 17/39] indoor: hidden lower buildings then "current level" --- data/core.yaml | 2 +- dist/locales/en.json | 2 +- js/id/renderer/features.js | 22 ++++++++++++++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 0ddb121000..cbb9d88fb0 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -325,7 +325,7 @@ en: past_future: description: Past/Future tooltip: "Proposed, Construction, Abandoned, Demolished, etc." - indoor_other_levels: + indoor_other_then_current_level: description: Indoor other levels tooltip: "When in indoor mode, it hides all levels except the selected one." indoor: diff --git a/dist/locales/en.json b/dist/locales/en.json index b8afb471db..ea2f586a99 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -395,7 +395,7 @@ "description": "Past/Future", "tooltip": "Proposed, Construction, Abandoned, Demolished, etc." }, - "indoor_other_levels": { + "indoor_other_then_current_level": { "description": "Indoor other levels", "tooltip": "When in indoor mode, it hides all levels except the selected one." }, diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index a541fa7bb8..c187c8ce8d 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -100,12 +100,26 @@ iD.Features = function(context) { ); }, 250); - defineFeature('indoor_other_levels', function isIndoorOtherLevel(entity, resolver, geometry) { //other then current level, and also non-indoor-points + defineFeature('indoor_other_then_current_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels var current = context.indoorLevel(); - return (!!entity.tags.level && !inRange(current, entity.tags.level)) - || (!!entity.tags.repeat_on && !inRange(current, entity.tags.repeat_on)) - || (geometry === 'point' && !entity.tags.level && !entity.tags.repeat_on); + if (entity.tags.level && !inRange(current, entity.tags.level)) + return true; + + if (entity.tags.repeat_on && !inRange(current, entity.tags.repeat_on)) + return true; + + if (geometry === 'point' && !entity.tags.level && !entity.tags.repeat_on) + return true; + + if (entity.tags.max_level && entity.tags.min_level) { + if (parseFloat(entity.tags.max_level) < current || parseFloat(entity.tags.min_level) > current) + return true; + } + else if (entity.tags['building'] || entity.tags['building:part']) { + if (parseFloat(entity.tags['building:levels'] || 0) < current) + return true; + } }); defineFeature('indoor', function isIndoorOther(entity) { From 3cd9ef36df708df7716f7c242c4816e013018988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Tue, 19 Apr 2016 13:29:56 +0200 Subject: [PATCH 18/39] indoor: hide surroundings for level < 0 --- data/core.yaml | 6 +++--- dist/locales/en.json | 6 +++--- js/id/renderer/features.js | 7 ++++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index cbb9d88fb0..d91b4f4011 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -325,9 +325,9 @@ en: past_future: description: Past/Future tooltip: "Proposed, Construction, Abandoned, Demolished, etc." - indoor_other_then_current_level: - description: Indoor other levels - tooltip: "When in indoor mode, it hides all levels except the selected one." + indoor_different_level: + description: Indoor - different levels + tooltip: "When in indoor mode, it hides all levels except the selected one. Also lower buildings." indoor: description: Indoor tooltip: "All features with filled level or repeat_on." diff --git a/dist/locales/en.json b/dist/locales/en.json index ea2f586a99..17dd27114d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -395,9 +395,9 @@ "description": "Past/Future", "tooltip": "Proposed, Construction, Abandoned, Demolished, etc." }, - "indoor_other_then_current_level": { - "description": "Indoor other levels", - "tooltip": "When in indoor mode, it hides all levels except the selected one." + "indoor_different_level": { + "description": "Indoor - different levels", + "tooltip": "When in indoor mode, it hides all levels except the selected one. Also lower buildings." }, "indoor": { "description": "Indoor", diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index c187c8ce8d..96d987f396 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -100,7 +100,7 @@ iD.Features = function(context) { ); }, 250); - defineFeature('indoor_other_then_current_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels + defineFeature('indoor_different_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels var current = context.indoorLevel(); if (entity.tags.level && !inRange(current, entity.tags.level)) @@ -120,6 +120,11 @@ iD.Features = function(context) { if (parseFloat(entity.tags['building:levels'] || 0) < current) return true; } + + if (current < 0) { + if (!entity.tags.level && !entity.tags.repeat_on) + return true; + } }); defineFeature('indoor', function isIndoorOther(entity) { From 04f19a12ea30b513833ebc1d5983bb0f8b226527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Tue, 19 Apr 2016 13:39:40 +0200 Subject: [PATCH 19/39] indoor: supress losing selection when exiting indoor-mode --- js/id/id.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/id/id.js b/js/id/id.js index cfff5879b0..15d96685ab 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -359,7 +359,7 @@ window.iD = function () { _.each(_.without(features.keys(), 'indoor', 'buildings', 'points'), features.disable); } else { - _.each(features.keys(), features.disable); + _.each(_.difference(features.keys(), enabledFeaturesBeforeIndoor), features.disable); _.each(enabledFeaturesBeforeIndoor, features.enable); } From e4d5f5878db5d06e4d6267c0a0209efec32bbf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Tue, 19 Apr 2016 15:46:08 +0200 Subject: [PATCH 20/39] indoor: level parameter in url --- js/id/behavior/hash.js | 9 +++++++++ js/id/id.js | 25 ++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/js/id/behavior/hash.js b/js/id/behavior/hash.js index c861ca5758..1dd237c0a0 100644 --- a/js/id/behavior/hash.js +++ b/js/id/behavior/hash.js @@ -36,6 +36,11 @@ iD.behavior.Hash = function(context) { '/' + center[0].toFixed(precision) + '/' + center[1].toFixed(precision); + if (context.indoorMode()) + q.level = context.indoorLevel(); + else + delete q.level; + return '#' + iD.util.qsString(_.assign(q, newParams), true); }; @@ -61,6 +66,9 @@ iD.behavior.Hash = function(context) { context .on('enter.hash', throttledUpdate); + context + .on('indoorLevelChanged.hash', throttledUpdate); + d3.select(window) .on('hashchange.hash', hashchange); @@ -68,6 +76,7 @@ iD.behavior.Hash = function(context) { var q = iD.util.stringQs(location.hash.substring(1)); if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map); if (q.comment) context.storage('comment', q.comment); + if (q.level) context.indoorLevel(q.level); hashchange(); if (q.map) hash.hadHash = true; } diff --git a/js/id/id.js b/js/id/id.js index 15d96685ab..2607620633 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -331,18 +331,30 @@ window.iD = function () { }; context.indoorLevel = function (newLevel) { - if (newLevel && newLevel !== indoorLevel) { + if (newLevel) { //setter indoorLevel = newLevel; - features.reset(); - map.redraw(); //TODO event? + if (indoorMode) { + features.reset(); + map.redraw(); //TODO event? + dispatch.indoorLevelChanged(); //update hash & combo + } + else { + context.toggleIndoorMode(); + } } return indoorLevel; }; context.toggleIndoorMode = function () { + if (!context.surface()) { //hash too early + setTimeout(context.toggleIndoorMode, 200); //TODO better? + return false; + } + indoorMode = !indoorMode; context.surface().classed('indoor-mode', indoorMode); + var selectedFeature = context.selectedIDs(); if (indoorMode && selectedFeature.length) { var entity = context.graph().entity(selectedFeature[0]); @@ -354,9 +366,8 @@ window.iD = function () { if (indoorMode) { enabledFeaturesBeforeIndoor = features.enabled(); - features.enable('indoor'); - features.enable('buildings'); - _.each(_.without(features.keys(), 'indoor', 'buildings', 'points'), features.disable); + _.each(_.without(features.keys(), 'indoor', 'buildings', 'points'), features.disable); //preserve selection on those 'withouts' + _.each(['indoor', 'buildings', 'points'], features.enable); } else { _.each(_.difference(features.keys(), enabledFeaturesBeforeIndoor), features.disable); @@ -365,7 +376,7 @@ window.iD = function () { features.reset(); map.redraw(); //TODO event? - dispatch.indoorLevelChanged(); //update combo + dispatch.indoorLevelChanged(); //update hash & combo }; context.indoorLevels = function () { From 1fbad7f448c0e809ad2f11eba53f789e311a6833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Tue, 19 Apr 2016 15:57:45 +0200 Subject: [PATCH 21/39] indoor: hide "building" icon for amenity --- css/map.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/css/map.css b/css/map.css index fdf5f51ac7..faf181cb8f 100644 --- a/css/map.css +++ b/css/map.css @@ -1324,8 +1324,11 @@ path.fill.tag-indoor { stroke: rgb(169, 169, 169); } .indoor-mode use.icon.areaicon.tag-building, +.indoor-mode use.icon.areaicon.tag-amenity, .indoor-mode text.arealabel.tag-building, -.indoor-mode text.arealabel-halo.tag-building { +.indoor-mode text.arealabel.tag-amenity, +.indoor-mode text.arealabel-halo.tag-building, +.indoor-mode text.arealabel-halo.tag-amenity { display: none; } From aced1a4ad91296448586fe383a119b9193a4d6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Thu, 21 Apr 2016 11:52:15 +0200 Subject: [PATCH 22/39] indoor: fix - correct hiding of vertexes --- js/id/renderer/features.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 96d987f396..7f38950d1d 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -394,6 +394,8 @@ iD.Features = function(context) { if (!_hidden.length) return false; if ((!context.indoorMode() && !entity.version) || geometry === 'point') return false; + if (context.indoorMode() && geometry === 'vertex' && _features.indoor_different_level.filter(entity, resolver, geometry)) return true; + var parents = features.getParents(entity, resolver, geometry); if (!parents.length) return false; From f2c9118888ca55da508f3416a0bd9f5ce3be481b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Fri, 22 Apr 2016 11:58:04 +0200 Subject: [PATCH 23/39] indoor: mandatory level, optional repeat_on - new tagging as of http://wiki.openstreetmap.org/wiki/Simple_Indoor_Tagging --- js/id/renderer/features.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 7f38950d1d..f93e1b65b2 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -103,10 +103,7 @@ iD.Features = function(context) { defineFeature('indoor_different_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels var current = context.indoorLevel(); - if (entity.tags.level && !inRange(current, entity.tags.level)) - return true; - - if (entity.tags.repeat_on && !inRange(current, entity.tags.repeat_on)) + if (entity.tags.level && !inRange(current, entity.tags.level) && !inRange(current, entity.tags.repeat_on)) return true; if (geometry === 'point' && !entity.tags.level && !entity.tags.repeat_on) From dbee34771ee7c6109759a9ef840a1e55fe05792a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Fri, 22 Apr 2016 12:00:42 +0200 Subject: [PATCH 24/39] indoor: hide other buildings underground --- js/id/renderer/features.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index f93e1b65b2..433e5a279f 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -114,10 +114,9 @@ iD.Features = function(context) { return true; } else if (entity.tags['building'] || entity.tags['building:part']) { - if (parseFloat(entity.tags['building:levels'] || 0) < current) + if (current < 0 || current >= parseFloat(entity.tags['building:levels'] || 0)) //one level <=> level=0 return true; } - if (current < 0) { if (!entity.tags.level && !entity.tags.repeat_on) return true; From 1719fc2087b3bd18f84ce7989afbd20977ca3335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Fri, 22 Apr 2016 12:01:08 +0200 Subject: [PATCH 25/39] indoor: translations --- data/core.yaml | 4 ++++ dist/locales/en.json | 5 +++++ js/id/ui/indoor_mode.js | 12 ++++-------- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index d91b4f4011..483a863a60 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -373,6 +373,10 @@ en: Another user changed some of the same map features you changed. Click on each item below for more details about the conflict, and choose whether to keep your changes or the other user's changes. + indoor_mode: + title: Indoor + help: Indoor mode helps you creating indoor objects by filtering only the selected level. + exit: Exit indoor editing mode. merge_remote_changes: conflict: deleted: 'This object has been deleted by {user}.' diff --git a/dist/locales/en.json b/dist/locales/en.json index 17dd27114d..fbd11c8307 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -451,6 +451,11 @@ "help": "Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n" } }, + "indoor_mode": { + "title": "Indoor", + "help": "Indoor mode helps you creating indoor objects by filtering only the selected level.", + "exit": "Exit indoor editing mode." + }, "merge_remote_changes": { "conflict": { "deleted": "This object has been deleted by {user}.", diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index cde03ab755..cf3235c766 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -7,9 +7,9 @@ iD.ui.IndoorMode = function (context) { enterButton = selection.append('button') .attr('class', 'indoormode-enter-button col12 ') .on('click', toggleIndoor) - .call(buttonTooltip('Indoor mode helps you creating indoor objects by filtering only the selected level. Even for currently added features.', '⌘⇧I')); + .call(buttonTooltip(t('indoor_mode.help'), '⌘⇧I')); enterButton - .append('span').attr('class', 'label').text('Indoor'); + .append('span').attr('class', 'label').text(t('indoor_mode.title')); indoorControl = selection.append('div') @@ -26,7 +26,7 @@ iD.ui.IndoorMode = function (context) { indoorControl.append('button') .attr('class', 'col3') .on('click', toggleIndoor) - .call(buttonTooltip('Exit indoor editing mode', '⌘⇧I')) + .call(buttonTooltip(t('indoor_mode.exit'), '⌘⇧I')) .call(iD.svg.Icon('#icon-close')); var spinControl; @@ -74,15 +74,12 @@ iD.ui.IndoorMode = function (context) { enableButton = entities.some(hasIndoorRelatedTag); } - console.log("context.on(enter) called"); - console.log('enableIndoor', enableButton); updateControls(selection, enableButton); } }; function updateControls(selection, enableButton) { if (context.indoorMode()) { - console.log("updateControls indoor=true"); enterButton.classed('hide', true); indoorControl.classed('hide', false); indoorControl.select('.combobox-input') @@ -92,7 +89,7 @@ iD.ui.IndoorMode = function (context) { } else { - enterButton.classed('hide', !enableButton); //.property('disabled', !enableButton); + enterButton.classed('hide', !enableButton); indoorControl.classed('hide', true); } } @@ -121,7 +118,6 @@ iD.ui.IndoorMode = function (context) { var input = d3.select(this); var data = input.value(); //string! if (data === '') return; //blank value - console.log('setLevel', data); input .attr('placeholder', data) From ddbd92a408e58a5019c4908492048337d2cde02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Fri, 22 Apr 2016 12:02:14 +0200 Subject: [PATCH 26/39] indoor: styles for underground view --- css/app.css | 5 ++++ css/map.css | 5 +++- js/id/id.js | 54 ++++++++++++++++++++++++++------------ js/id/renderer/features.js | 4 --- js/id/svg/tag_classes.js | 2 +- 5 files changed, 47 insertions(+), 23 deletions(-) diff --git a/css/app.css b/css/app.css index 01de10137e..a40ec894ae 100644 --- a/css/app.css +++ b/css/app.css @@ -488,6 +488,11 @@ button.save.has-count .count::before { .indoormode-level-combo .combobox-input { height: 40px; border-radius: 4px 0 0 4px; + font-weight: bold; + text-align: center; +} +.indoormode-level-combo .combobox-input::-webkit-input-placeholder { + color: #000; } .indoormode-level-combo .combobox-caret { display: none; diff --git a/css/map.css b/css/map.css index faf181cb8f..53432f5aa3 100644 --- a/css/map.css +++ b/css/map.css @@ -1311,7 +1311,7 @@ path.fill.tag-amenity-shelter { path.fill.tag-indoor { display: none; } -.indoor-mode path.fill.tag-building { +.indoor-mode path.fill.tag-building.tag-max_level { clip-path: none !important; stroke-width: 30px; } @@ -1331,6 +1331,9 @@ path.fill.tag-indoor { .indoor-mode text.arealabel-halo.tag-amenity { display: none; } +.indoor-mode.indoor-underground .way:not(.tag-level):not(.tag-building) { + opacity: 0.1; +} /* Labels / Markers */ diff --git a/js/id/id.js b/js/id/id.js index 2607620633..80868021c2 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -333,14 +333,10 @@ window.iD = function () { context.indoorLevel = function (newLevel) { if (newLevel) { //setter indoorLevel = newLevel; - if (indoorMode) { - features.reset(); - map.redraw(); //TODO event? - dispatch.indoorLevelChanged(); //update hash & combo - } - else { + if (indoorMode) + context.indoorRedraw(); + else context.toggleIndoorMode(); - } } return indoorLevel; }; @@ -351,36 +347,60 @@ window.iD = function () { return false; } - indoorMode = !indoorMode; - context.surface().classed('indoor-mode', indoorMode); - - - var selectedFeature = context.selectedIDs(); - if (indoorMode && selectedFeature.length) { - var entity = context.graph().entity(selectedFeature[0]); + var selectedFeatures = context.selectedIDs(); + if (!indoorMode && selectedFeatures.length) { + var entity = context.graph().entity(selectedFeatures[0]); if (entity.tags.level) indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).+/, '$1'); else if (entity.tags.repeat_on) indoorLevel = entity.tags.repeat_on.replace(/(-?\d+(\.\d+)?).+/, '$1'); + else + indoorLevel = '0'; + + console.log(indoorLevel); } + indoorMode = !indoorMode; + context.surface().classed('indoor-mode', indoorMode); + if (indoorMode) { enabledFeaturesBeforeIndoor = features.enabled(); - _.each(_.without(features.keys(), 'indoor', 'buildings', 'points'), features.disable); //preserve selection on those 'withouts' - _.each(['indoor', 'buildings', 'points'], features.enable); + _.each(features.keys(), features.disable); + _.each(['indoor', 'buildings', 'points', 'paths', 'traffic_roads', 'service_roads'], features.enable); } else { _.each(_.difference(features.keys(), enabledFeaturesBeforeIndoor), features.disable); _.each(enabledFeaturesBeforeIndoor, features.enable); } + context.indoorRedraw(); + + selectedFeatures.length && context.enter(iD.modes.Select(context, selectedFeatures)); + + }; + + context.indoorRedraw = function () { + context.surface().classed('indoor-underground', indoorLevel < 0); + context.surface().classed('indoor-aboveground', indoorLevel > 0); + setBackgroundOpacity(indoorLevel < 0 && 0.1); + features.reset(); map.redraw(); //TODO event? dispatch.indoorLevelChanged(); //update hash & combo + + function setBackgroundOpacity(d) { + var bg = context.container().selectAll('.layer-background'); + if (d === false) + d = bg.attr('data-opacity'); + bg.transition().style('opacity', d); + if (!iD.detect().opera) { + iD.util.setTransform(bg, 0, 0); + } + } }; context.indoorLevels = function () { - return [-1, 0, 1, 2] + return [-1, 0, 1, 2, 3, 0.5, 1.5]; }; /* Init */ diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 433e5a279f..c5926e4e4e 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -117,10 +117,6 @@ iD.Features = function(context) { if (current < 0 || current >= parseFloat(entity.tags['building:levels'] || 0)) //one level <=> level=0 return true; } - if (current < 0) { - if (!entity.tags.level && !entity.tags.repeat_on) - return true; - } }); defineFeature('indoor', function isIndoorOther(entity) { diff --git a/js/id/svg/tag_classes.js b/js/id/svg/tag_classes.js index 771f065b53..9afee95f1e 100644 --- a/js/id/svg/tag_classes.js +++ b/js/id/svg/tag_classes.js @@ -10,7 +10,7 @@ iD.svg.TagClasses = function() { ], secondaries = [ 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', - 'surface', 'tracktype', 'crossing' + 'surface', 'tracktype', 'crossing', 'level', 'max_level' ], tagClassRe = /^tag-/, tags = function(entity) { return entity.tags; }; From 1309b18abd28b59bbefd197eb55fa67b7d35abab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Fri, 22 Apr 2016 13:57:25 +0200 Subject: [PATCH 27/39] indoor: inspector - indoor_levels field + ui.preset.range --- Makefile | 1 + css/app.css | 3 ++ data/presets.yaml | 5 +++ data/presets/fields.json | 13 ++++++ data/presets/fields/indoor_levels.json | 13 ++++++ data/presets/presets.json | 19 ++++++--- data/presets/presets/building/college.json | 3 +- data/presets/presets/building/commercial.json | 1 + data/presets/presets/building/hospital.json | 3 +- data/presets/presets/building/hotel.json | 1 + data/presets/presets/building/public.json | 1 + data/presets/presets/building/retail.json | 1 + data/presets/presets/building/school.json | 3 +- .../presets/building/train_station.json | 3 +- data/presets/presets/building/university.json | 3 +- data/presets/schema/field.json | 3 +- dist/locales/en.json | 5 +++ index.html | 1 + js/id/ui/preset/range.js | 40 +++++++++++++++++++ 19 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 data/presets/fields/indoor_levels.json create mode 100644 js/id/ui/preset/range.js diff --git a/Makefile b/Makefile index 75f0f0a2d1..bfde7ec669 100644 --- a/Makefile +++ b/Makefile @@ -238,6 +238,7 @@ dist/iD.js: \ js/id/ui/preset/localized.js \ js/id/ui/preset/maxspeed.js \ js/id/ui/preset/radio.js \ + js/id/ui/preset/range.js \ js/id/ui/preset/restrictions.js \ js/id/ui/preset/textarea.js \ js/id/ui/preset/wikipedia.js \ diff --git a/css/app.css b/css/app.css index a40ec894ae..0cde19a054 100644 --- a/css/app.css +++ b/css/app.css @@ -1071,6 +1071,9 @@ button.save.has-count .count::before { border-radius: 0 0 4px 4px; overflow: hidden; } +.form-field > input:last-child { + border-left: 0; +} .form-field textarea { height: 65px; diff --git a/data/presets.yaml b/data/presets.yaml index 3281e44113..e215a114d4 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -444,6 +444,11 @@ en: down: Down # incline=up up: Up + indoor_levels: + # 'min_level=*, max_level=*' + label: Indoor levels (min - max) + placeholder-0: '-1, 0, 1, 3...' + placeholder-1: '3, 5, 10...' information: # 'information=*' label: Type diff --git a/data/presets/fields.json b/data/presets/fields.json index f6f509daa3..179142c614 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -616,6 +616,19 @@ } } }, + "indoor_levels": { + "keys": [ + "min_level", + "max_level" + ], + "type": "range", + "label": "Indoor levels (min - max)", + "universal": true, + "strings": { + "placeholder-0": "-1, 0, 1, 3...", + "placeholder-1": "3, 5, 10..." + } + }, "information": { "key": "information", "type": "typeCombo", diff --git a/data/presets/fields/indoor_levels.json b/data/presets/fields/indoor_levels.json new file mode 100644 index 0000000000..6c0d798bd1 --- /dev/null +++ b/data/presets/fields/indoor_levels.json @@ -0,0 +1,13 @@ +{ + "keys": [ + "min_level", + "max_level" + ], + "type": "range", + "label": "Indoor levels (min - max)", + "universal": true, + "strings": { + "placeholder-0": "-1, 0, 1, 3...", + "placeholder-1": "3, 5, 10..." + } +} \ No newline at end of file diff --git a/data/presets/presets.json b/data/presets/presets.json index c967d99d8b..71c72a1b79 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -2563,7 +2563,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" @@ -2642,6 +2643,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ @@ -2749,7 +2751,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" @@ -2780,6 +2783,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ @@ -2861,6 +2865,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ @@ -2876,7 +2881,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" @@ -2892,6 +2898,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ @@ -3011,7 +3018,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" @@ -3034,7 +3042,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "point", diff --git a/data/presets/presets/building/college.json b/data/presets/presets/building/college.json index 2e6f021f27..352a4c1094 100644 --- a/data/presets/presets/building/college.json +++ b/data/presets/presets/building/college.json @@ -2,7 +2,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" diff --git a/data/presets/presets/building/commercial.json b/data/presets/presets/building/commercial.json index 76067d2713..b2cf5a731a 100644 --- a/data/presets/presets/building/commercial.json +++ b/data/presets/presets/building/commercial.json @@ -3,6 +3,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ diff --git a/data/presets/presets/building/hospital.json b/data/presets/presets/building/hospital.json index 0f7dadb649..1e3c281914 100644 --- a/data/presets/presets/building/hospital.json +++ b/data/presets/presets/building/hospital.json @@ -2,7 +2,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" diff --git a/data/presets/presets/building/hotel.json b/data/presets/presets/building/hotel.json index eb76a47ca9..725a10c6ca 100644 --- a/data/presets/presets/building/hotel.json +++ b/data/presets/presets/building/hotel.json @@ -3,6 +3,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ diff --git a/data/presets/presets/building/public.json b/data/presets/presets/building/public.json index 40ba7959e4..ac58420c92 100644 --- a/data/presets/presets/building/public.json +++ b/data/presets/presets/building/public.json @@ -3,6 +3,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ diff --git a/data/presets/presets/building/retail.json b/data/presets/presets/building/retail.json index 4924c99c21..f457a4c853 100644 --- a/data/presets/presets/building/retail.json +++ b/data/presets/presets/building/retail.json @@ -3,6 +3,7 @@ "fields": [ "address", "levels", + "indoor_levels", "smoking" ], "geometry": [ diff --git a/data/presets/presets/building/school.json b/data/presets/presets/building/school.json index c8427df725..c5c71a8206 100644 --- a/data/presets/presets/building/school.json +++ b/data/presets/presets/building/school.json @@ -2,7 +2,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" diff --git a/data/presets/presets/building/train_station.json b/data/presets/presets/building/train_station.json index 6a4d04a180..72dc6b4d23 100644 --- a/data/presets/presets/building/train_station.json +++ b/data/presets/presets/building/train_station.json @@ -2,7 +2,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "point", diff --git a/data/presets/presets/building/university.json b/data/presets/presets/building/university.json index e34385f1df..0c730773c5 100644 --- a/data/presets/presets/building/university.json +++ b/data/presets/presets/building/university.json @@ -2,7 +2,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" diff --git a/data/presets/schema/field.json b/data/presets/schema/field.json index 7b73733ee4..37f202c31c 100644 --- a/data/presets/schema/field.json +++ b/data/presets/schema/field.json @@ -66,7 +66,8 @@ "localized", "wikipedia", "typeCombo", - "restrictions" + "restrictions", + "range" ], "required": true }, diff --git a/dist/locales/en.json b/dist/locales/en.json index fbd11c8307..af67ff3f58 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1035,6 +1035,11 @@ "down": "Down" } }, + "indoor_levels": { + "label": "Indoor levels (min - max)", + "placeholder-0": "-1, 0, 1, 3...", + "placeholder-1": "3, 5, 10..." + }, "information": { "label": "Type" }, diff --git a/index.html b/index.html index f39ad0406b..ea756ae54a 100644 --- a/index.html +++ b/index.html @@ -136,6 +136,7 @@ + diff --git a/js/id/ui/preset/range.js b/js/id/ui/preset/range.js new file mode 100644 index 0000000000..1e0ba7774b --- /dev/null +++ b/js/id/ui/preset/range.js @@ -0,0 +1,40 @@ +iD.ui.preset.range = function (field) { + + var dispatch = d3.dispatch('change'), + inputs; + + function i(selection) { + inputs = selection.selectAll('input') + .data([0, 1]); + + inputs.enter().append('input') + .attr('type', 'number') + .attr('id', function (d) { return 'preset-input-' + field.id + '-' + d; }) + .style('width', '50%') + .style('border-radius', function (d) { return d === 0 ? '0 0 0 4px' : '0 0 4px 0'; }) + .attr('placeholder', function (d) { return field.t('placeholder-' + d); }) + + inputs + .on('input', change(true)) + .on('blur', change()) + .on('change', change()); + } + + function change(onInput) { + return function (d) { + var t = {}; + t[field.keys[d]] = this.value || undefined; + dispatch.change(t, onInput); + }; + } + + i.tags = function (tags) { + inputs.value(function(d){ return tags[field.keys[d]] || ''; }); + }; + + i.focus = function () { + inputs.node().focus(); + }; + + return d3.rebind(i, dispatch, 'on'); +}; From 507878d6a86d5dad9e03a5ec9db970209fc7323c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 25 Apr 2016 13:22:19 +0200 Subject: [PATCH 28/39] indoor: refactoring context.indoor() to separate file --- Makefile | 1 + css/app.css | 2 +- index.html | 1 + js/id/behavior/hash.js | 10 ++-- js/id/id.js | 93 ++++---------------------------------- js/id/modes/add_area.js | 4 +- js/id/modes/add_line.js | 8 +--- js/id/modes/add_point.js | 4 +- js/id/renderer/features.js | 19 +++++--- js/id/renderer/indoor.js | 89 ++++++++++++++++++++++++++++++++++++ js/id/ui/indoor_mode.js | 46 +++++++++++-------- 11 files changed, 151 insertions(+), 126 deletions(-) create mode 100644 js/id/renderer/indoor.js diff --git a/Makefile b/Makefile index bfde7ec669..a406fbd083 100644 --- a/Makefile +++ b/Makefile @@ -161,6 +161,7 @@ dist/iD.js: \ js/id/renderer/background.js \ js/id/renderer/background_source.js \ js/id/renderer/features.js \ + js/id/renderer/indoor.js \ js/id/renderer/map.js \ js/id/renderer/tile_layer.js \ js/id/svg.js \ diff --git a/css/app.css b/css/app.css index 0cde19a054..c05ac34d7f 100644 --- a/css/app.css +++ b/css/app.css @@ -1071,7 +1071,7 @@ button.save.has-count .count::before { border-radius: 0 0 4px 4px; overflow: hidden; } -.form-field > input:last-child { +.form-field > input:nth-child(3) { border-left: 0; } diff --git a/index.html b/index.html index ea756ae54a..2b8cf9cee3 100644 --- a/index.html +++ b/index.html @@ -56,6 +56,7 @@ + diff --git a/js/id/behavior/hash.js b/js/id/behavior/hash.js index 1dd237c0a0..cb39d965bb 100644 --- a/js/id/behavior/hash.js +++ b/js/id/behavior/hash.js @@ -36,8 +36,8 @@ iD.behavior.Hash = function(context) { '/' + center[0].toFixed(precision) + '/' + center[1].toFixed(precision); - if (context.indoorMode()) - q.level = context.indoorLevel(); + if (context.indoor().enabled()) + q.level = context.indoor().level(); else delete q.level; @@ -66,8 +66,8 @@ iD.behavior.Hash = function(context) { context .on('enter.hash', throttledUpdate); - context - .on('indoorLevelChanged.hash', throttledUpdate); + context.indoor() + .on('levelChanged.hash', throttledUpdate); d3.select(window) .on('hashchange.hash', hashchange); @@ -76,7 +76,7 @@ iD.behavior.Hash = function(context) { var q = iD.util.stringQs(location.hash.substring(1)); if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map); if (q.comment) context.storage('comment', q.comment); - if (q.level) context.indoorLevel(q.level); + if (q.level) context.indoor().level(q.level); hashchange(); if (q.map) hash.hadHash = true; } diff --git a/js/id/id.js b/js/id/id.js index 80868021c2..30a8895341 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -2,7 +2,7 @@ window.iD = function () { window.locale.en = iD.data.en; window.locale.current('en'); - var dispatch = d3.dispatch('enter', 'exit', 'indoorLevelChanged'), + var dispatch = d3.dispatch('enter', 'exit'), context = {}; // https://github.com/openstreetmap/iD/issues/772 @@ -215,6 +215,13 @@ window.iD = function () { }; + /* Indoor mode */ + var indoor; + context.indoor = function () { + return indoor; + }; + + /* Debug */ var debugTile = false, debugCollision = false; context.debugTile = function(_) { @@ -321,88 +328,6 @@ window.iD = function () { }; - /* Indoor mode */ - var indoorMode = false, indoorLevel = '0', enabledFeaturesBeforeIndoor; - // return d3.rebind(indoor, dispatch, 'on'); - - - context.indoorMode = function () { - return indoorMode; - }; - - context.indoorLevel = function (newLevel) { - if (newLevel) { //setter - indoorLevel = newLevel; - if (indoorMode) - context.indoorRedraw(); - else - context.toggleIndoorMode(); - } - return indoorLevel; - }; - - context.toggleIndoorMode = function () { - if (!context.surface()) { //hash too early - setTimeout(context.toggleIndoorMode, 200); //TODO better? - return false; - } - - var selectedFeatures = context.selectedIDs(); - if (!indoorMode && selectedFeatures.length) { - var entity = context.graph().entity(selectedFeatures[0]); - if (entity.tags.level) - indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).+/, '$1'); - else if (entity.tags.repeat_on) - indoorLevel = entity.tags.repeat_on.replace(/(-?\d+(\.\d+)?).+/, '$1'); - else - indoorLevel = '0'; - - console.log(indoorLevel); - } - - indoorMode = !indoorMode; - context.surface().classed('indoor-mode', indoorMode); - - if (indoorMode) { - enabledFeaturesBeforeIndoor = features.enabled(); - _.each(features.keys(), features.disable); - _.each(['indoor', 'buildings', 'points', 'paths', 'traffic_roads', 'service_roads'], features.enable); - } - else { - _.each(_.difference(features.keys(), enabledFeaturesBeforeIndoor), features.disable); - _.each(enabledFeaturesBeforeIndoor, features.enable); - } - - context.indoorRedraw(); - - selectedFeatures.length && context.enter(iD.modes.Select(context, selectedFeatures)); - - }; - - context.indoorRedraw = function () { - context.surface().classed('indoor-underground', indoorLevel < 0); - context.surface().classed('indoor-aboveground', indoorLevel > 0); - setBackgroundOpacity(indoorLevel < 0 && 0.1); - - features.reset(); - map.redraw(); //TODO event? - dispatch.indoorLevelChanged(); //update hash & combo - - function setBackgroundOpacity(d) { - var bg = context.container().selectAll('.layer-background'); - if (d === false) - d = bg.attr('data-opacity'); - bg.transition().style('opacity', d); - if (!iD.detect().opera) { - iD.util.setTransform(bg, 0, 0); - } - } - }; - - context.indoorLevels = function () { - return [-1, 0, 1, 2, 3, 0.5, 1.5]; - }; - /* Init */ context.projection = iD.geo.RawMercator(); @@ -453,6 +378,8 @@ window.iD = function () { context.zoomOutFurther = map.zoomOutFurther; context.redrawEnable = map.redrawEnable; + indoor = iD.Indoor(context); + presets = iD.presets(); return d3.rebind(context, dispatch, 'on'); diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js index 9d45b5e638..91813c48c2 100644 --- a/js/id/modes/add_area.js +++ b/js/id/modes/add_area.js @@ -15,8 +15,8 @@ iD.modes.AddArea = function(context) { function defaultTags() { var tags = {area: 'yes'}; - if (context.indoorMode()) { - tags.level = context.indoorLevel(); + if (context.indoor().enabled()) { + tags.level = context.indoor().level(); } return tags; } diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index a34b87d519..606d3851f6 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -15,8 +15,8 @@ iD.modes.AddLine = function(context) { function defaultTags() { var tags = {}; - if (context.indoorMode()) { - tags.level = context.indoorLevel(); + if (context.indoor().enabled()) { + tags.level = context.indoor().level(); } return tags; } @@ -26,10 +26,6 @@ iD.modes.AddLine = function(context) { node = iD.Node({loc: loc}), way = iD.Way({tags: defaultTags()}); - if (context.indoorMode()) { - way.tags.level = context.indoorLevel(); - } - context.perform( iD.actions.AddEntity(node), iD.actions.AddEntity(way), diff --git a/js/id/modes/add_point.js b/js/id/modes/add_point.js index b862b85ced..78be7f8fb7 100644 --- a/js/id/modes/add_point.js +++ b/js/id/modes/add_point.js @@ -17,8 +17,8 @@ iD.modes.AddPoint = function(context) { function defaultTags() { var tags = {}; - if (context.indoorMode()) { - tags.level = context.indoorLevel(); + if (context.indoor().enabled()) { + tags.level = context.indoor().level(); } return tags; } diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index c5926e4e4e..a68d561db6 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -101,7 +101,7 @@ iD.Features = function(context) { }, 250); defineFeature('indoor_different_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels - var current = context.indoorLevel(); + var current = context.indoor().level(); if (entity.tags.level && !inRange(current, entity.tags.level) && !inRange(current, entity.tags.repeat_on)) return true; @@ -117,6 +117,10 @@ iD.Features = function(context) { if (current < 0 || current >= parseFloat(entity.tags['building:levels'] || 0)) //one level <=> level=0 return true; } + + if (current > 0 && entity.tags.highway && !entity.tags.level) { + return true; + } }); defineFeature('indoor', function isIndoorOther(entity) { @@ -372,7 +376,7 @@ iD.Features = function(context) { features.isHiddenFeature = function(entity, resolver, geometry) { if (!_hidden.length) return false; - if (!context.indoorMode() && !entity.version) return false; + if (!context.indoor().enabled() && entity.isNew()) return false; var matches = features.getMatches(entity, resolver, geometry); @@ -384,9 +388,10 @@ iD.Features = function(context) { features.isHiddenChild = function(entity, resolver, geometry) { if (!_hidden.length) return false; - if ((!context.indoorMode() && !entity.version) || geometry === 'point') return false; + if ((!context.indoor().enabled() && entity.isNew()) || geometry === 'point') return false; - if (context.indoorMode() && geometry === 'vertex' && _features.indoor_different_level.filter(entity, resolver, geometry)) return true; + //hide vertices with different level + if (geometry === 'vertex' && context.indoor().enabled() && _features.indoor_different_level.filter(entity, resolver, geometry)) return true; var parents = features.getParents(entity, resolver, geometry); if (!parents.length) return false; @@ -423,7 +428,7 @@ iD.Features = function(context) { features.isHidden = function(entity, resolver, geometry) { if (!_hidden.length) return false; - if (!context.indoorMode() && !entity.version) return false; + if (!context.indoor().enabled() && !entity.version) return false; var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature); return fn(entity, resolver, geometry); @@ -443,9 +448,9 @@ iD.Features = function(context) { }; - var levelRangeRegExp = /^(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?$/; + var rangeRegExp = /^(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?$/; function inRange(level, rangeText) { - var range = rangeText && levelRangeRegExp.exec(rangeText); + var range = rangeText && rangeRegExp.exec(rangeText); if (!range) { //blank text OR not matched return false; diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js new file mode 100644 index 0000000000..404c868a8c --- /dev/null +++ b/js/id/renderer/indoor.js @@ -0,0 +1,89 @@ +iD.Indoor = function (context) { + var dispatch = d3.dispatch('levelChanged'), + indoorMode = false, + indoorLevel = '0', + enabledFeaturesBeforeIndoor, + features = context.features(); + + + function indoor() {} + + indoor.enabled = function () { + return indoorMode; + }; + + indoor.level = function (newLevel) { + if (newLevel) { //setter + indoorLevel = newLevel; + if (indoorMode) + indoor.redraw(); + else + indoor.toggle(); + } + return indoorLevel; + }; + + indoor.toggle = function () { + if (!context.surface()) { //hash calls before surface is initialized + setTimeout(indoor.toggle, 200); //TODO better? + return false; + } + + indoorMode = !indoorMode; + context.surface().classed('indoor-mode', indoorMode); + + var selectedFeatures = context.selectedIDs(); + if (indoorMode && selectedFeatures.length) { + var entity = context.graph().entity(selectedFeatures[0]); + if (entity.tags.level) + indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).+/, '$1'); + } + + if (indoorMode) { + enabledFeaturesBeforeIndoor = features.enabled(); + _.each(features.keys(), features.disable); + _.each(['indoor', 'buildings', 'points', 'paths', 'traffic_roads', 'service_roads'], features.enable); + } + else { + _.each(_.difference(features.keys(), enabledFeaturesBeforeIndoor), features.disable); + _.each(enabledFeaturesBeforeIndoor, features.enable); + + indoorLevel = '0'; + } + + indoor.redraw(); + + if (selectedFeatures.length) { + context.enter(iD.modes.Select(context, selectedFeatures)); + } + }; + + indoor.redraw = function () { + context.surface().classed('indoor-underground', indoorLevel < 0); + context.surface().classed('indoor-aboveground', indoorLevel > 0); + setBackgroundOpacity(indoorLevel < 0 ? 0.1 : 'revert'); + + features.reset(); + context.map().redraw(); //TODO event? + dispatch.levelChanged(); //update hash & combo + }; + + indoor.levels = function () { + return [-1, 0, 1, 1.5]; + }; + + + + function setBackgroundOpacity(d) { + var bg = context.container().selectAll('.layer-background'); + if (d === 'revert') + d = bg.attr('data-opacity'); + bg.transition().style('opacity', d); + if (!iD.detect().opera) { + iD.util.setTransform(bg, 0, 0); + } + } + + + return d3.rebind(indoor, dispatch, 'on'); +}; diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index cf3235c766..cf77592358 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -56,36 +56,42 @@ iD.ui.IndoorMode = function (context) { .call(keybinding); context - .on('enter.indoor_mode', update) - .on('indoorLevelChanged.indoor_mode', update); + .on('enter.indoor_mode', update); + + context.indoor() + .on('levelChanged.indoor_mode', update); + + context.map() + .on('move.indoor_mode', _.debounce(update, 400)) + .on('drawn.indoor_mode', update); + function update() { - var enableButton = context.indoorMode(); + var graph = context.graph(); + var showButton = context.indoor().enabled(); - if (!enableButton) { - var graph = context.graph(); - var ids = context.selectedIDs(); - var entities = ids.map(function (id) { + if (!showButton) { + var entities = context.selectedIDs().map(function (id) { return graph.entity(id); }); - var hasIndoorRelatedTag = function (e) { - return e.tags.level || e.tags.repeat_on || e.tags.indoor || e.tags.building; - }; - enableButton = entities.some(hasIndoorRelatedTag); + + showButton = entities.some(function isBuilding(e) { + return e.tags.building; + }); } - updateControls(selection, enableButton); + updateControls(selection, showButton); } }; function updateControls(selection, enableButton) { - if (context.indoorMode()) { + if (context.indoor().enabled()) { enterButton.classed('hide', true); indoorControl.classed('hide', false); indoorControl.select('.combobox-input') - .attr('placeholder', context.indoorLevel()) + .attr('placeholder', context.indoor().level()) .value('') - .call(d3.combobox().data(context.indoorLevels().map(comboValues))); + .call(d3.combobox().data(context.indoor().levels().map(comboValues))); } else { @@ -96,14 +102,14 @@ iD.ui.IndoorMode = function (context) { function toggleIndoor() { d3.event.preventDefault(); - context.toggleIndoorMode(); + context.indoor().toggle(); } function levelChangeFunc(dif) { return function () { d3.event.preventDefault(); - context.indoorLevel(parseFloat(context.indoorLevel()) + dif + ""); - indoorControl.select('input').attr('placeholder', context.indoorLevel()); + context.indoor().level(parseFloat(context.indoor().level()) + dif + ""); + indoorControl.select('input').attr('placeholder', context.indoor().level()); } } @@ -111,7 +117,7 @@ iD.ui.IndoorMode = function (context) { return bootstrap.tooltip() .placement('bottom') .html(true) - .title(iD.ui.tooltipHtml(description, cmd ? iD.ui.cmd(cmd) : undefined)); + .title(iD.ui.tooltipHtml(description, (cmd ? iD.ui.cmd(cmd) : undefined))); } function setLevel() { @@ -123,7 +129,7 @@ iD.ui.IndoorMode = function (context) { .attr('placeholder', data) .value(''); - context.indoorLevel(data); + context.indoor().level(data); } function comboValues(d) { From 50d4d8d5e3c5fa6e8e5e065eab6d153cfe16bd5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 25 Apr 2016 13:22:53 +0200 Subject: [PATCH 29/39] indoor: show button when level=* feature in viewport --- js/id/ui/indoor_mode.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index cf77592358..ed02d56636 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -80,6 +80,14 @@ iD.ui.IndoorMode = function (context) { }); } + if (!showButton) { + var entities = context.intersects(context.map().extent()); + + showButton = entities.some(function hasIndoorRelatedTag(e) { + return e.tags.level || e.tags.indoor; + }); + } + updateControls(selection, showButton); } }; From b5309d3de01eb5ff3c23f876601ea1bb397c8e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 25 Apr 2016 15:13:45 +0200 Subject: [PATCH 30/39] indoor: map redraw on event --- js/id/renderer/indoor.js | 3 +-- js/id/renderer/map.js | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js index 404c868a8c..c5dc77fcd2 100644 --- a/js/id/renderer/indoor.js +++ b/js/id/renderer/indoor.js @@ -64,8 +64,7 @@ iD.Indoor = function (context) { setBackgroundOpacity(indoorLevel < 0 ? 0.1 : 'revert'); features.reset(); - context.map().redraw(); //TODO event? - dispatch.levelChanged(); //update hash & combo + dispatch.levelChanged(); //update hash & combo, redraw map }; indoor.levels = function () { diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 4f2f5be369..6e32db9723 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -32,6 +32,8 @@ iD.Map = function(context) { .on('change.map', redraw); context.features() .on('redraw.map', redraw); + context.indoor() + .on('levelChanged.map', redraw); drawLayers .on('change.map', function() { context.background().updateImagery(); @@ -202,7 +204,6 @@ iD.Map = function(context) { return true; } - map.redraw = redraw; function redraw(difference, extent) { if (!surface || !redrawEnabled) return; From ce1288fbeff07c8810e1b8a0b17d704ab73c540f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 25 Apr 2016 15:14:10 +0200 Subject: [PATCH 31/39] indoor: eslint --- js/id/renderer/features.js | 8 ++++---- js/id/renderer/indoor.js | 2 +- js/id/ui/indoor_mode.js | 12 ++++++------ js/id/ui/preset/range.js | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index a68d561db6..03220195c3 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -106,14 +106,14 @@ iD.Features = function(context) { if (entity.tags.level && !inRange(current, entity.tags.level) && !inRange(current, entity.tags.repeat_on)) return true; - if (geometry === 'point' && !entity.tags.level && !entity.tags.repeat_on) + if (geometry === 'point' && !entity.tags.level) return true; if (entity.tags.max_level && entity.tags.min_level) { if (parseFloat(entity.tags.max_level) < current || parseFloat(entity.tags.min_level) > current) return true; } - else if (entity.tags['building'] || entity.tags['building:part']) { + else if (entity.tags.building || entity.tags['building:part']) { if (current < 0 || current >= parseFloat(entity.tags['building:levels'] || 0)) //one level <=> level=0 return true; } @@ -124,7 +124,7 @@ iD.Features = function(context) { }); defineFeature('indoor', function isIndoorOther(entity) { - return !!entity.tags.level || !!entity.tags.repeat_on; + return !!entity.tags.level; }); defineFeature('landuse', function isLanduse(entity, resolver, geometry) { @@ -456,7 +456,7 @@ iD.Features = function(context) { return false; } - if (range[2] === undefined && range[4] == undefined) { //exact match + if (range[2] === undefined && range[4] === undefined) { //exact match if (range[1] === level) { return true; } diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js index c5dc77fcd2..2856c5ede0 100644 --- a/js/id/renderer/indoor.js +++ b/js/id/renderer/indoor.js @@ -25,7 +25,7 @@ iD.Indoor = function (context) { indoor.toggle = function () { if (!context.surface()) { //hash calls before surface is initialized - setTimeout(indoor.toggle, 200); //TODO better? + setTimeout(indoor.toggle, 200); return false; } diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index ed02d56636..70c2f82182 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -71,19 +71,19 @@ iD.ui.IndoorMode = function (context) { var showButton = context.indoor().enabled(); if (!showButton) { - var entities = context.selectedIDs().map(function (id) { + var selectedEntities = context.selectedIDs().map(function (id) { return graph.entity(id); }); - showButton = entities.some(function isBuilding(e) { + showButton = selectedEntities.some(function isBuilding(e) { return e.tags.building; }); } if (!showButton) { - var entities = context.intersects(context.map().extent()); + var displayedEntities = context.intersects(context.map().extent()); - showButton = entities.some(function hasIndoorRelatedTag(e) { + showButton = displayedEntities.some(function hasIndoorRelatedTag(e) { return e.tags.level || e.tags.indoor; }); } @@ -116,9 +116,9 @@ iD.ui.IndoorMode = function (context) { function levelChangeFunc(dif) { return function () { d3.event.preventDefault(); - context.indoor().level(parseFloat(context.indoor().level()) + dif + ""); + context.indoor().level(parseFloat(context.indoor().level()) + dif + ''); indoorControl.select('input').attr('placeholder', context.indoor().level()); - } + }; } function buttonTooltip(description, cmd) { diff --git a/js/id/ui/preset/range.js b/js/id/ui/preset/range.js index 1e0ba7774b..b8e07e49e6 100644 --- a/js/id/ui/preset/range.js +++ b/js/id/ui/preset/range.js @@ -12,7 +12,7 @@ iD.ui.preset.range = function (field) { .attr('id', function (d) { return 'preset-input-' + field.id + '-' + d; }) .style('width', '50%') .style('border-radius', function (d) { return d === 0 ? '0 0 0 4px' : '0 0 4px 0'; }) - .attr('placeholder', function (d) { return field.t('placeholder-' + d); }) + .attr('placeholder', function (d) { return field.t('placeholder-' + d); }); inputs .on('input', change(true)) From 6a8a3ad5f9b0d261eceb705f5d48b56a4a952afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 25 Apr 2016 16:00:35 +0200 Subject: [PATCH 32/39] indoor: refactoring + codestyle --- js/id/renderer/indoor.js | 66 ++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js index 2856c5ede0..44bd7d40f1 100644 --- a/js/id/renderer/indoor.js +++ b/js/id/renderer/indoor.js @@ -1,4 +1,4 @@ -iD.Indoor = function (context) { +iD.Indoor = function(context) { var dispatch = d3.dispatch('levelChanged'), indoorMode = false, indoorLevel = '0', @@ -13,10 +13,10 @@ iD.Indoor = function (context) { }; indoor.level = function (newLevel) { - if (newLevel) { //setter + if (newLevel) { // setter indoorLevel = newLevel; if (indoorMode) - indoor.redraw(); + redraw(); else indoor.toggle(); } @@ -24,54 +24,62 @@ iD.Indoor = function (context) { }; indoor.toggle = function () { - if (!context.surface()) { //hash calls before surface is initialized + if (!context.surface()) { // hash calls before surface is initialized setTimeout(indoor.toggle, 200); return false; } - indoorMode = !indoorMode; - context.surface().classed('indoor-mode', indoorMode); + if (indoorMode) + disable(); + else + enable(); + }; + + indoor.levels = function () { + return [-1, 0, 1, 1.5]; + }; + + + function enable() { + indoorMode = true; var selectedFeatures = context.selectedIDs(); - if (indoorMode && selectedFeatures.length) { + if (selectedFeatures.length) { var entity = context.graph().entity(selectedFeatures[0]); if (entity.tags.level) indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).+/, '$1'); } - if (indoorMode) { - enabledFeaturesBeforeIndoor = features.enabled(); - _.each(features.keys(), features.disable); - _.each(['indoor', 'buildings', 'points', 'paths', 'traffic_roads', 'service_roads'], features.enable); - } - else { - _.each(_.difference(features.keys(), enabledFeaturesBeforeIndoor), features.disable); - _.each(enabledFeaturesBeforeIndoor, features.enable); + enabledFeaturesBeforeIndoor = features.enabled(); + _.each(features.keys(), features.disable); + _.each(['indoor', 'buildings', 'points', 'paths', 'traffic_roads', 'service_roads'], features.enable); - indoorLevel = '0'; - } - - indoor.redraw(); + redraw(); if (selectedFeatures.length) { context.enter(iD.modes.Select(context, selectedFeatures)); } - }; + } - indoor.redraw = function () { + function disable() { + indoorMode = false; + + _.each(_.difference(features.keys(), enabledFeaturesBeforeIndoor), features.disable); + _.each(enabledFeaturesBeforeIndoor, features.enable); + + indoorLevel = '0'; + redraw(); + } + + function redraw() { + context.surface().classed('indoor-mode', indoorMode); context.surface().classed('indoor-underground', indoorLevel < 0); context.surface().classed('indoor-aboveground', indoorLevel > 0); setBackgroundOpacity(indoorLevel < 0 ? 0.1 : 'revert'); features.reset(); - dispatch.levelChanged(); //update hash & combo, redraw map - }; - - indoor.levels = function () { - return [-1, 0, 1, 1.5]; - }; - - + dispatch.levelChanged(); // update hash & combo, redraw map + } function setBackgroundOpacity(d) { var bg = context.container().selectAll('.layer-background'); From 4bacbdf0dd6431c2f571adaff00d5d63bc44f571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 2 May 2016 15:45:19 +0200 Subject: [PATCH 33/39] indoor: style changes from user testing --- css/map.css | 11 ++++++++++- data/core.yaml | 10 +++++----- js/id/renderer/features.js | 12 ++++-------- js/id/renderer/indoor.js | 4 ++-- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/css/map.css b/css/map.css index 53432f5aa3..ce1cba6c41 100644 --- a/css/map.css +++ b/css/map.css @@ -1315,6 +1315,9 @@ path.fill.tag-indoor { clip-path: none !important; stroke-width: 30px; } +.indoor-mode path.way.area.stroke.tag-building.tag-max_level { + stroke-width: 6px; +} .indoor-mode path.fill.tag-indoor { display: block; fill: rgba(169, 169, 169, 0.3); @@ -1323,6 +1326,9 @@ path.fill.tag-indoor { .indoor-mode path.stroke.tag-indoor { stroke: rgb(169, 169, 169); } +.indoor-mode path.stroke.tag-indoor-corridor { + opacity: 0.1; +} .indoor-mode use.icon.areaicon.tag-building, .indoor-mode use.icon.areaicon.tag-amenity, .indoor-mode text.arealabel.tag-building, @@ -1331,9 +1337,12 @@ path.fill.tag-indoor { .indoor-mode text.arealabel-halo.tag-amenity { display: none; } -.indoor-mode.indoor-underground .way:not(.tag-level):not(.tag-building) { +.indoor-mode .way:not(.tag-level):not(.tag-building) { opacity: 0.1; } +.indoor-mode.indoor-underground .way:not(.tag-level):not(.tag-building) { + opacity: 0.05; +} /* Labels / Markers */ diff --git a/data/core.yaml b/data/core.yaml index 483a863a60..7ff84854ef 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -325,12 +325,12 @@ en: past_future: description: Past/Future tooltip: "Proposed, Construction, Abandoned, Demolished, etc." - indoor_different_level: - description: Indoor - different levels - tooltip: "When in indoor mode, it hides all levels except the selected one. Also lower buildings." indoor: - description: Indoor - tooltip: "All features with filled level or repeat_on." + description: Indoor Features + tooltip: "All features with filled level." + indoor_different_level: + description: Indoor - hidden levels + tooltip: "When in indoor mode, it hides all levels except the selected one." others: description: Others tooltip: "Everything Else" diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 03220195c3..907e90f6d0 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -100,6 +100,10 @@ iD.Features = function(context) { ); }, 250); + defineFeature('indoor', function isIndoorOther(entity) { + return !!entity.tags.level; + }); + defineFeature('indoor_different_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels var current = context.indoor().level(); @@ -117,14 +121,6 @@ iD.Features = function(context) { if (current < 0 || current >= parseFloat(entity.tags['building:levels'] || 0)) //one level <=> level=0 return true; } - - if (current > 0 && entity.tags.highway && !entity.tags.level) { - return true; - } - }); - - defineFeature('indoor', function isIndoorOther(entity) { - return !!entity.tags.level; }); defineFeature('landuse', function isLanduse(entity, resolver, geometry) { diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js index 44bd7d40f1..be6d094b19 100644 --- a/js/id/renderer/indoor.js +++ b/js/id/renderer/indoor.js @@ -69,13 +69,13 @@ iD.Indoor = function(context) { indoorLevel = '0'; redraw(); + setBackgroundOpacity('revert'); } function redraw() { context.surface().classed('indoor-mode', indoorMode); context.surface().classed('indoor-underground', indoorLevel < 0); - context.surface().classed('indoor-aboveground', indoorLevel > 0); - setBackgroundOpacity(indoorLevel < 0 ? 0.1 : 'revert'); + setBackgroundOpacity(indoorLevel < 0 ? 0.05 : 0.2); features.reset(); dispatch.levelChanged(); // update hash & combo, redraw map From ce6de5cba5679efc5c6d621af8f76dc0f7542aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Tue, 3 May 2016 14:03:19 +0200 Subject: [PATCH 34/39] indoor: add tests --- test/index.html | 5 +++ test/index_packaged.html | 1 + test/spec/renderer/indoor.js | 79 ++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 test/spec/renderer/indoor.js diff --git a/test/index.html b/test/index.html index 15775fc36d..6e03846091 100644 --- a/test/index.html +++ b/test/index.html @@ -61,6 +61,7 @@ + @@ -97,6 +98,7 @@ + @@ -128,6 +130,8 @@ + + @@ -289,6 +293,7 @@ + diff --git a/test/index_packaged.html b/test/index_packaged.html index cbfcf67a3b..642f3d0729 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -83,6 +83,7 @@ + diff --git a/test/spec/renderer/indoor.js b/test/spec/renderer/indoor.js new file mode 100644 index 0000000000..75bcc6fcec --- /dev/null +++ b/test/spec/renderer/indoor.js @@ -0,0 +1,79 @@ +describe('iD.Indoor', function() { + var context, + features, + indoor; + + beforeEach(function() { + context = iD(); + context.map().zoom(17); + features = context.features(); + indoor = context.indoor(); + + //mocks + var surface = d3.select(document.createElement('svg')); + context.surface = function () { + return surface; + }; + + var container = d3.select(document.createElement('div')); + context.container = function () { + return container; + }; + + }); + + describe('startup', function () { + it('returns correct level', function () { + indoor.level('134'); + expect(indoor.level()).to.equal('134'); + }); + + it('add correct classes', function () { + indoor.level('1'); + expect(context.surface().classed('indoor-mode'), 'surface to have class .indoor-mode').to.be.true; + }); + + it('disables feature indoor_different_level', function () { + indoor.level('1'); + expect(features.enabled('indoor_different_level'), 'indoor level ').to.be.false; + }); + + + + }); + + describe('filtering', function () { + + it('hides features on different level', function () { + + var node1 = iD.Node({tags: {amenity: 'bar', level: '1-2'}, version: 1}); + var node2 = iD.Node({tags: {amenity: 'bar', level: '-2--1'}, version: 1}); + var node3 = iD.Node({tags: {amenity: 'bar', level: '-2-4'}, version: 1}); + var node4 = iD.Node({tags: {amenity: 'bar'}, version: 1}); + var node5 = iD.Node({tags: {amenity: 'bar', level: 'asdf'}, version: 1}); + var node6 = iD.Node({tags: {amenity: 'bar', level: '3;4;1'}, version: 1}); + + var graph = iD.Graph([node1, node2, node3, node4, node5, node6]); + + // correct _hidden + var all = _.values(graph.base().entities), + dimensions = [1000, 1000]; + features.gatherStats(all, graph, dimensions); + + + indoor.level('1'); + expect(features.isHidden(node1, graph, node1.geometry(graph)), 'level=1-2 is shown in level 1').to.be.false; + expect(features.isHidden(node2, graph, node2.geometry(graph)), 'level=-2--1 is hidden in level 1').to.be.true; + expect(features.isHidden(node3, graph, node3.geometry(graph)), 'level=-2-4 is shown in level 1').to.be.false; + expect(features.isHidden(node4, graph, node4.geometry(graph)), '`no level` is hidden in level 1').to.be.true; + expect(features.isHidden(node5, graph, node5.geometry(graph)), 'level=asdf is hidden in level 1').to.be.true; + expect(features.isHidden(node6, graph, node6.geometry(graph)), 'level=3;4;1 is shown in level 1').to.be.false; + + + indoor.level('-1.5'); + expect(features.isHidden(node2, graph, node2.geometry(graph)), 'level=-2--1 is shown in level -1.5').to.be.false; + + }); + }); + +}); From 3f1803ba1106ea46a3b41b5e0d33d646f07b9af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 9 May 2016 22:47:35 +0200 Subject: [PATCH 35/39] indoor: fix decimal levels regexp + test --- js/id/renderer/features.js | 2 +- js/id/renderer/indoor.js | 2 +- test/spec/renderer/indoor.js | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 907e90f6d0..a78e10a847 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -444,7 +444,7 @@ iD.Features = function(context) { }; - var rangeRegExp = /^(-?\d+)(?:(-)(-?\d+)|(;-?\d)+)?$/; + var rangeRegExp = /^(-?\d+(?:\.\d+)?)(?:(-)(-?\d+(?:\.\d+)?)|(;-?\d(?:\.\d+)?)+)?$/; function inRange(level, rangeText) { var range = rangeText && rangeRegExp.exec(rangeText); diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js index be6d094b19..0898c209a2 100644 --- a/js/id/renderer/indoor.js +++ b/js/id/renderer/indoor.js @@ -47,7 +47,7 @@ iD.Indoor = function(context) { if (selectedFeatures.length) { var entity = context.graph().entity(selectedFeatures[0]); if (entity.tags.level) - indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).+/, '$1'); + indoorLevel = entity.tags.level.replace(/(-?\d+(\.\d+)?).*/, '$1'); } enabledFeaturesBeforeIndoor = features.enabled(); diff --git a/test/spec/renderer/indoor.js b/test/spec/renderer/indoor.js index 75bcc6fcec..0aea3cbe0f 100644 --- a/test/spec/renderer/indoor.js +++ b/test/spec/renderer/indoor.js @@ -1,9 +1,9 @@ -describe('iD.Indoor', function() { +describe('iD.Indoor', function () { var context, features, indoor; - beforeEach(function() { + beforeEach(function () { context = iD(); context.map().zoom(17); features = context.features(); @@ -39,7 +39,6 @@ describe('iD.Indoor', function() { }); - }); describe('filtering', function () { @@ -74,6 +73,19 @@ describe('iD.Indoor', function() { expect(features.isHidden(node2, graph, node2.geometry(graph)), 'level=-2--1 is shown in level -1.5').to.be.false; }); + + it('shows decimal level', function () { + + var node1 = iD.Node({tags: {amenity: 'bar', level: '0.5'}, version: 1}); + var all = [node1]; + var graph = iD.Graph(all); + + // update _hidden field in features + features.gatherStats(all, graph, [1000, 1000]); + + indoor.level('0.5'); + expect(features.isHidden(node1, graph, node1.geometry(graph)), 'level=0.5 is shown in level 0.5').to.be.false; + }); }); }); From 3f5097b023b704c79370f9ec8f7631c7587c7493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 9 May 2016 22:48:47 +0200 Subject: [PATCH 36/39] indoor: tagging - repeat_on means discreet values --- js/id/renderer/features.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index a78e10a847..4d2c7162e9 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -107,7 +107,7 @@ iD.Features = function(context) { defineFeature('indoor_different_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels var current = context.indoor().level(); - if (entity.tags.level && !inRange(current, entity.tags.level) && !inRange(current, entity.tags.repeat_on)) + if (entity.tags.level && !inRange(current, entity)) return true; if (geometry === 'point' && !entity.tags.level) @@ -445,7 +445,10 @@ iD.Features = function(context) { var rangeRegExp = /^(-?\d+(?:\.\d+)?)(?:(-)(-?\d+(?:\.\d+)?)|(;-?\d(?:\.\d+)?)+)?$/; - function inRange(level, rangeText) { + function inRange(level, entity) { + return textInRange(level, entity.tags.level) || textInRange(level, entity.tags.repeat_on, true); + } + function textInRange(level, rangeText, discreetValuesRange) { var range = rangeText && rangeRegExp.exec(rangeText); if (!range) { //blank text OR not matched @@ -458,8 +461,10 @@ iD.Features = function(context) { } } else if (range[2] === '-') { // range from - to, only numeric comparison - if (parseFloat(range[1]) <= level && parseFloat(range[3]) >= level) { - return true; + var min = parseFloat(range[1]); + var max = parseFloat(range[3]); + if (min <= level && max >= level) { + return discreetValuesRange ? isDecimalPartEqual(level, min) : true; } } else { // range list @@ -470,6 +475,9 @@ iD.Features = function(context) { return false; } + function isDecimalPartEqual(a, b) { + return Math.abs(a%1 - b%1) < 0.0001; //decimal part equals + } return d3.rebind(features, dispatch, 'on'); }; From a57c92e95f84c86235d33741b5c7230af3a6070d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 9 May 2016 23:27:14 +0200 Subject: [PATCH 37/39] indoor: refactor inRange() to indoor file --- js/id/renderer/features.js | 36 +-------------------------------- js/id/renderer/indoor.js | 41 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 4d2c7162e9..a75937ea90 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -107,7 +107,7 @@ iD.Features = function(context) { defineFeature('indoor_different_level', function isHiddenByLevel(entity, resolver, geometry) { //disabled in indoor_mode -> hides unwanted levels var current = context.indoor().level(); - if (entity.tags.level && !inRange(current, entity)) + if (entity.tags.level && !context.indoor().inRange(current, entity)) return true; if (geometry === 'point' && !entity.tags.level) @@ -444,40 +444,6 @@ iD.Features = function(context) { }; - var rangeRegExp = /^(-?\d+(?:\.\d+)?)(?:(-)(-?\d+(?:\.\d+)?)|(;-?\d(?:\.\d+)?)+)?$/; - function inRange(level, entity) { - return textInRange(level, entity.tags.level) || textInRange(level, entity.tags.repeat_on, true); - } - function textInRange(level, rangeText, discreetValuesRange) { - var range = rangeText && rangeRegExp.exec(rangeText); - - if (!range) { //blank text OR not matched - return false; - } - - if (range[2] === undefined && range[4] === undefined) { //exact match - if (range[1] === level) { - return true; - } - } - else if (range[2] === '-') { // range from - to, only numeric comparison - var min = parseFloat(range[1]); - var max = parseFloat(range[3]); - if (min <= level && max >= level) { - return discreetValuesRange ? isDecimalPartEqual(level, min) : true; - } - } - else { // range list - if (range[0].split(';').indexOf(level) !== -1) { - return true; - } - } - return false; - } - - function isDecimalPartEqual(a, b) { - return Math.abs(a%1 - b%1) < 0.0001; //decimal part equals - } return d3.rebind(features, dispatch, 'on'); }; diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js index 0898c209a2..a336ed2e72 100644 --- a/js/id/renderer/indoor.js +++ b/js/id/renderer/indoor.js @@ -1,4 +1,4 @@ -iD.Indoor = function(context) { +iD.Indoor = function (context) { var dispatch = d3.dispatch('levelChanged'), indoorMode = false, indoorLevel = '0', @@ -9,7 +9,7 @@ iD.Indoor = function(context) { function indoor() {} indoor.enabled = function () { - return indoorMode; + return indoorMode; }; indoor.level = function (newLevel) { @@ -92,5 +92,42 @@ iD.Indoor = function(context) { } + // ---- tagging related ----- + + var rangeRegExp = /^(-?\d+(?:\.\d+)?)(?:(-)(-?\d+(?:\.\d+)?)|(;-?\d(?:\.\d+)?)+)?$/; + indoor.inRange = function (level, entity) { + return textInRange(level, entity.tags.level) || textInRange(level, entity.tags.repeat_on, true); + } + function textInRange(level, rangeText, discreetValuesRange) { + var range = rangeText && rangeRegExp.exec(rangeText); + + if (!range) { //blank text OR not matched + return false; + } + + if (range[2] === undefined && range[4] === undefined) { //exact match + if (range[1] === level) { + return true; + } + } + else if (range[2] === '-') { // range from - to, only numeric comparison + var min = parseFloat(range[1]); + var max = parseFloat(range[3]); + if (min <= level && max >= level) { + return discreetValuesRange ? isDecimalPartEqual(level, min) : true; + } + } + else { // range list + if (range[0].split(';').indexOf(level) !== -1) { + return true; + } + } + return false; + } + + function isDecimalPartEqual(a, b) { + return Math.abs(a % 1 - b % 1) < 0.0001; //decimal part equals + } + return d3.rebind(indoor, dispatch, 'on'); }; From 5edb261c3dd80b88564896516fb14b4a1da3da55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Mon, 9 May 2016 23:58:33 +0200 Subject: [PATCH 38/39] indoor: switching floors using decimal values (if exist) --- js/id/renderer/indoor.js | 8 ++------ js/id/ui/indoor_mode.js | 32 +++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/js/id/renderer/indoor.js b/js/id/renderer/indoor.js index a336ed2e72..0892693723 100644 --- a/js/id/renderer/indoor.js +++ b/js/id/renderer/indoor.js @@ -35,11 +35,6 @@ iD.Indoor = function (context) { enable(); }; - indoor.levels = function () { - return [-1, 0, 1, 1.5]; - }; - - function enable() { indoorMode = true; @@ -97,7 +92,8 @@ iD.Indoor = function (context) { var rangeRegExp = /^(-?\d+(?:\.\d+)?)(?:(-)(-?\d+(?:\.\d+)?)|(;-?\d(?:\.\d+)?)+)?$/; indoor.inRange = function (level, entity) { return textInRange(level, entity.tags.level) || textInRange(level, entity.tags.repeat_on, true); - } + }; + function textInRange(level, rangeText, discreetValuesRange) { var range = rangeText && rangeRegExp.exec(rangeText); diff --git a/js/id/ui/indoor_mode.js b/js/id/ui/indoor_mode.js index 70c2f82182..942679adfc 100644 --- a/js/id/ui/indoor_mode.js +++ b/js/id/ui/indoor_mode.js @@ -13,13 +13,13 @@ iD.ui.IndoorMode = function (context) { indoorControl = selection.append('div') - .attr('class', 'indoormode-control joined '); + .attr('class', 'indoormode-control joined'); indoorControl.append('div') //combo .attr('class', 'col4 indoormode-level-combo') .append('input') .attr('type', 'text') - .call(d3.combobox().data([0, 1, 2, 3, 4, 5, 6].map(comboValues))) + .call(d3.combobox().data([].map(comboValues))) .on('blur', setLevel) .on('change', setLevel); spinControl = indoorControl.append('div'); @@ -63,7 +63,7 @@ iD.ui.IndoorMode = function (context) { context.map() .on('move.indoor_mode', _.debounce(update, 400)) - .on('drawn.indoor_mode', update); + .on('drawn.indoor_mode', _.debounce(update, 400)); function update() { @@ -99,7 +99,7 @@ iD.ui.IndoorMode = function (context) { indoorControl.select('.combobox-input') .attr('placeholder', context.indoor().level()) .value('') - .call(d3.combobox().data(context.indoor().levels().map(comboValues))); + .call(d3.combobox().data(getLevelsInTags().map(comboValues))); } else { @@ -108,6 +108,20 @@ iD.ui.IndoorMode = function (context) { } } + function getLevelsInTags() { + var displayedEntities = context.intersects(context.map().extent()); + var levelsSet = d3.set(); + displayedEntities.forEach(function addToSet(e) { + if (e.tags.level) { + var match = e.tags.level.replace(/(-?\d+(\.\d+)?).*/, '$1'); //should parse ranges and so on + levelsSet.add(match); + } + }); + + var sorted = levelsSet.values().sort(d3.descending); + return sorted; + } + function toggleIndoor() { d3.event.preventDefault(); context.indoor().toggle(); @@ -116,7 +130,15 @@ iD.ui.IndoorMode = function (context) { function levelChangeFunc(dif) { return function () { d3.event.preventDefault(); - context.indoor().level(parseFloat(context.indoor().level()) + dif + ''); + var current = parseFloat(context.indoor().level()); + var target = current + dif; + getLevelsInTags().forEach(function findClosestToCurrent(l){ + if ((current < l && l < target) || (target < l && l < current)) { + target = dif>0 ? Math.min(target, l) : Math.max(target, l); + } + }); + + context.indoor().level(target + ''); indoorControl.select('input').attr('placeholder', context.indoor().level()); }; } From b37cccaef04381d2f00b4d7f6f9be738e6c5daf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsky=CC=81?= Date: Tue, 10 May 2016 00:02:25 +0200 Subject: [PATCH 39/39] indoor: build english and presets --- data/presets/presets.json | 24 ++++++++++++------------ dist/locales/en.json | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/data/presets/presets.json b/data/presets/presets.json index 71c72a1b79..1250b9ae00 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -2563,8 +2563,7 @@ "icon": "building", "fields": [ "address", - "levels", - "indoor_levels" + "levels" ], "geometry": [ "area" @@ -2624,7 +2623,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" @@ -2751,8 +2751,7 @@ "icon": "building", "fields": [ "address", - "levels", - "indoor_levels" + "levels" ], "geometry": [ "area" @@ -2767,7 +2766,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" @@ -2881,8 +2881,7 @@ "icon": "building", "fields": [ "address", - "levels", - "indoor_levels" + "levels" ], "geometry": [ "area" @@ -2928,7 +2927,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" @@ -3018,8 +3018,7 @@ "icon": "building", "fields": [ "address", - "levels", - "indoor_levels" + "levels" ], "geometry": [ "area" @@ -3061,7 +3060,8 @@ "icon": "building", "fields": [ "address", - "levels" + "levels", + "indoor_levels" ], "geometry": [ "area" diff --git a/dist/locales/en.json b/dist/locales/en.json index af67ff3f58..5bca1a7336 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -395,13 +395,13 @@ "description": "Past/Future", "tooltip": "Proposed, Construction, Abandoned, Demolished, etc." }, - "indoor_different_level": { - "description": "Indoor - different levels", - "tooltip": "When in indoor mode, it hides all levels except the selected one. Also lower buildings." - }, "indoor": { - "description": "Indoor", - "tooltip": "All features with filled level or repeat_on." + "description": "Indoor Features", + "tooltip": "All features with filled level." + }, + "indoor_different_level": { + "description": "Indoor - hidden levels", + "tooltip": "When in indoor mode, it hides all levels except the selected one." }, "others": { "description": "Others",