From e77a0453afff58470040e51179decaa21a1031a5 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Wed, 18 Jan 2017 20:56:15 +0530 Subject: [PATCH 01/25] interim --- .vscode/launch.json | 19 ++ css/app.css | 45 ++++ data/presets/fields.json | 2 +- data/presets/fields/lanes.json | 2 +- modules/osm/lanes.js | 67 +++++- modules/ui/field.js | 52 +++++ modules/ui/fields/lanes.js | 381 ++++++++++++++++++++++++++------- test/spec/osm/lanes.js | 17 +- 8 files changed, 490 insertions(+), 95 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 modules/ui/field.js diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000000..064c90f7b3c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Chrome against localhost", + "type": "chrome", + "request": "launch", + "url": "http://localhost:8080/#background=Bing&id=w26283961&map=18.71/12.98164/77.63874", + "webRoot": "${workspaceRoot}" + }, + { + "name": "Attach to Chrome", + "type": "chrome", + "request": "attach", + "port": 9222, + "webRoot": "${workspaceRoot}" + } + ] +} \ No newline at end of file diff --git a/css/app.css b/css/app.css index 74874394e32..6e200c65e86 100644 --- a/css/app.css +++ b/css/app.css @@ -1221,6 +1221,51 @@ button.save.has-count .count::before { border-bottom-right-radius: 4px; } +/* lanes */ + +.form-field-lanes { + +} + +.form-field-lanes > .lanes-info { + margin-bottom: 10px; +} + + + +.lane-tags > label { + position: relative; + padding: 5px 10px; + display: block; + height: 30px; + background-color: white; + color: #7092FF; + cursor: pointer; +} + +.lane-tags > label:hover { + background-color: #ececec; +} + +.lane-tags > label:not(:last-child) { + border-bottom: 1px solid #ccc; +} + +.lane-tags > label:last-child { + border-radius: 0 0 3px 3px; +} + +.lane-tags label > span { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.lane-tags > label.active { + background: #E8EBFF; +} + /* preset form multicombo */ .form-field-multicombo { diff --git a/data/presets/fields.json b/data/presets/fields.json index 68ec1dbf4cc..3ad05e74ade 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -827,7 +827,7 @@ }, "lanes": { "key": "lanes", - "type": "number", + "type": "lanes", "label": "Lanes", "placeholder": "1, 2, 3..." }, diff --git a/data/presets/fields/lanes.json b/data/presets/fields/lanes.json index 8b4a10628e2..833f91f4043 100644 --- a/data/presets/fields/lanes.json +++ b/data/presets/fields/lanes.json @@ -1,6 +1,6 @@ { "key": "lanes", - "type": "number", + "type": "lanes", "label": "Lanes", "placeholder":"1, 2, 3..." } diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index f8c136d8d44..5eac22c7c5f 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -72,6 +72,8 @@ export function osmLanes(entity) { mapToLanesObj(lanesObj, hgvLanes, 'hgv'); mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway'); + // TODO: need to make sure forward lanes is consistent across all tags, + // eg if psv:lanes:forward is 3 and lanes:forward is 2, changes lanes:forward =3, return { metadata: { count: laneCount, @@ -89,7 +91,24 @@ export function osmLanes(entity) { hgvLanes: hgvLanes, bicyclewayLanes: bicyclewayLanes }, - lanes: lanesObj + lanes: lanesObj, + lanesArray: flattenLanesArray(lanesArray( + { + count: laneCount, + oneway: isOneWay, + forward: forward, + backward: backward, + bothways: bothways, + turnLanes: turnLanes, + maxspeed: maxspeed, + maxspeedLanes: maxspeedLanes, + psvLanes: psvLanes, + busLanes: busLanes, + taxiLanes: taxiLanes, + hovLanes: hovLanes, + hgvLanes: hgvLanes, + bicyclewayLanes: bicyclewayLanes + })) }; } @@ -169,7 +188,7 @@ function parseLaneDirections(tags, isOneWay, laneCount) { function parseTurnLanes(tag){ - if (!tag) return; + if (!tag) return []; var validValues = [ 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', @@ -188,7 +207,7 @@ function parseTurnLanes(tag){ function parseMaxspeedLanes(tag, maxspeed) { - if (!tag) return; + if (!tag) return []; return tag.split('|') .map(function (s) { @@ -201,7 +220,7 @@ function parseMaxspeedLanes(tag, maxspeed) { function parseMiscLanes(tag) { - if (!tag) return; + if (!tag) return []; var validValues = [ 'yes', 'no', 'designated' @@ -214,9 +233,9 @@ function parseMiscLanes(tag) { }); } - +// TODO: need to append lanes? and make it return an array? function parseBicycleWay(tag) { - if (!tag) return; + if (!tag) return []; var validValues = [ 'yes', 'no', 'designated', 'lane' @@ -244,3 +263,39 @@ function mapToLanesObj(lanesObj, data, key) { lanesObj.unspecified[i][key] = l; }); } + +function flattenLanesArray(lanes) { + var order = ['backward', 'forward']; + var ret = [].concat(lanes[order[0]], lanes[order[1]]); + for (var i = 0; i < ret.length; i++) { + ret[i] = _.assign(ret[i] || {}, lanes.unspecified[i]); + } + return ret; +} + +function lanesArray(lanesData) { + var metadata = _.cloneDeep(lanesData); + // var arr = new Array(metadata.count); + var consideredLaneTags = [ 'busLanes', 'hgvLanes', 'hovLanes', 'maxspeedLanes', 'psvLanes', 'taxiLanes', 'turnLanes' ]; + var obj = {}; + + obj.forward = new Array(metadata.forward); + obj.backward = new Array(metadata.backward); + // obj.bothways = new Array(metadata.bothways); // jo + obj.unspecified = new Array(metadata.count); //_.fill(Array(metadata.count), { }); + + consideredLaneTags.forEach(function (laneTag) { + var lane = metadata[laneTag]; + Object.keys(lane).forEach(function (direction) { + lane[direction] + .forEach(function (tag, i) { + if (!obj[direction][i]) obj[direction][i] = {}; + if (i < obj[direction].length) { + obj[direction][i][laneTag] = tag; + } + }); + }); + }); + + return obj; +} \ No newline at end of file diff --git a/modules/ui/field.js b/modules/ui/field.js new file mode 100644 index 00000000000..af817558d47 --- /dev/null +++ b/modules/ui/field.js @@ -0,0 +1,52 @@ +import { uiFields } from './fields/index'; + +function UIField(field, entity, show, context) { + field = _.clone(field); + + field.input = uiFields[field.type](field, context) + .on('change', function(t, onInput) { + dispatch.call('change', field, t, onInput); + }); + + if (field.input.entity) field.input.entity(entity); + + field.keys = field.keys || [field.key]; + + field.show = show; + + field.shown = function() { + return field.id === 'name' || field.show || _.some(field.keys, function(key) { return !!tags[key]; }); + }; + + field.modified = function() { + var original = context.graph().base().entities[entity.id]; + return _.some(field.keys, function(key) { + return original ? tags[key] !== original.tags[key] : tags[key]; + }); + }; + + field.revert = function() { + var original = context.graph().base().entities[entity.id], + t = {}; + field.keys.forEach(function(key) { + t[key] = original ? original.tags[key] : undefined; + }); + return t; + }; + + field.present = function() { + return _.some(field.keys, function(key) { + return tags[key]; + }); + }; + + field.remove = function() { + var t = {}; + field.keys.forEach(function(key) { + t[key] = undefined; + }); + return t; + }; + + return field; + } \ No newline at end of file diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index c2c57b25bc8..63bfcfff914 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -1,126 +1,224 @@ import * as d3 from 'd3'; import { utilRebind } from '../../util/rebind'; import { utilGetDimensions } from '../../util/dimensions'; +import { uiFieldCheck } from './check'; +import _ from 'lodash'; +import { d3combobox } from '../../lib/d3.combobox.js'; +import { utilGetSetValue } from '../../util/get_set_value'; +function validLanes() { + return [ + 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', + 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' + ]; +} + export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), LANE_WIDTH = 40, LANE_HEIGHT = 200, wayID, lanesData; - - function lanes(selection) { - lanesData = context.entity(wayID).lanes(); - - if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { - selection.call(lanes.off); - return; - } - + + function lanesInfoUI(selection) { var wrap = selection.selectAll('.preset-input-wrap') .data([0]); + var keysConsidered = ['count', 'forward', 'backward', 'maxspeed']; + var metadata = lanesData.metadata; wrap = wrap.enter() .append('div') - .attr('class', 'preset-input-wrap') + .attr('class', 'cf preset-input-wrap') + .append('ul') .merge(wrap); - var surface = wrap.selectAll('.surface') + var list = wrap.selectAll('ul') .data([0]); - var d = utilGetDimensions(wrap); - var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; + list = list.enter() + .append('ul') + .merge(list); - surface = surface.enter() - .append('svg') - .attr('width', d[0]) - .attr('height', 300) - .attr('class', 'surface') - .merge(surface); + var items = list.selectAll('li') + .data(keysConsidered); - var lanesSelection = surface.selectAll('.lanes') - .data([0]); + // Enter + var enter = items.enter() + .append('li') + .attr('class', function(d) { return 'cf preset-access-' + d; }); + + enter + .append('span') + .attr('class', 'col6 label preset-label-access') + .attr('for', function(d) { return 'preset-input-access-' + d; }) + .text(function(d) { return d; }); - lanesSelection = lanesSelection.enter() - .append('g') - .attr('class', 'lanes') - .merge(lanesSelection); + enter + .append('div') + .attr('class', 'col6 preset-input-access-wrap') + .append('input') + .attr('type', 'text') + .attr('class', 'preset-input-access') + .attr('id', function(d) { return 'preset-input-access-' + d; }) + .each(function(d) { + this.value = metadata[d]; + + }); - lanesSelection - .attr('transform', function () { - return 'translate(' + (freeSpace / 2) + ', 0)'; + // Update + items = items.merge(enter); + items.selectAll('input') + .property('value', function (d) { + return metadata[d]; }); + items.selectAll('input') + .on('change', change) + .on('blur', change); + } - var lane = lanesSelection.selectAll('.lane') - .data(lanesData.lanes); + function turnLanesUI(selection) { + + + var laneTags = selection.selectAll('.lane-tags').data([0]); - lane.exit() - .remove(); + + laneTags + .enter() + .append('label') + .attr('class','form-label') + .text('Turn Lanes'); - var enter = lane.enter() - .append('g') - .attr('class', 'lane'); + var laneTagsEnter = laneTags + .enter() + .append('div') + .attr('class', 'lane-tags cf preset-input-wrap') + .classed('checkselect', 'true'); - enter - .append('g') - .append('rect') - .attr('y', 50) - .attr('width', LANE_WIDTH) - .attr('height', LANE_HEIGHT); + var inputNumber = laneTagsEnter.append('input') + .attr('type', 'field.type') + .attr('id', 'fieldId') + .attr('placeholder', 'inspector.unknown') + .attr('type', 'text'); - enter - .append('g') - .attr('class', 'forward') - .append('text') - .attr('y', 40) - .attr('x', 14) - .text('▲'); + var spinControl = laneTagsEnter.selectAll('.spin-control') + .data([0]); - enter - .append('g') - .attr('class', 'bothways') - .append('text') - .attr('y', 40) - .attr('x', 14) - .text('▲▼'); + var spinControlEnter = spinControl.enter() + .append('div') + .attr('class', 'spin-control'); - enter - .append('g') - .attr('class', 'backward') - .append('text') - .attr('y', 40) - .attr('x', 14) - .text('▼'); + spinControlEnter + .append('button') + .datum(1) + .attr('class', 'increment') + .attr('tabindex', -1); + spinControlEnter + .append('button') + .datum(-1) + .attr('class', 'decrement') + .attr('tabindex', -1); - lane = lane - .merge(enter); + spinControl = spinControl + .merge(spinControlEnter); - lane - .attr('transform', function(d) { - return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)'; + spinControl.selectAll('button') + .on('click', function(d) { + d3.event.preventDefault(); + var num = parseInt(inputNumber.node().value || 0, 10); + if (!isNaN(num) && num + d > 0 && num + d <= lanesData.metadata.count) inputNumber.node().value = num + d; }); - lane.select('.forward') - .style('visibility', function(d) { - return d.direction === 'forward' ? 'visible' : 'hidden'; - }); - lane.select('.bothways') - .style('visibility', function(d) { - return d.direction === 'bothways' ? 'visible' : 'hidden'; - }); - lane.select('.backward') - .style('visibility', function(d) { - return d.direction === 'backward' ? 'visible' : 'hidden'; - }); + var label = laneTagsEnter.selectAll('.label') + .data(validLanes()); + + var labelEnter = label.enter() + .append('label'); + + labelEnter + .append('input') + .property('indeterminate', field.type === 'check') + .attr('type', 'checkbox') + .attr('id', 'preset-input-' + field.id); + + labelEnter + .append('span') + .text(function (d) { return d;}) + .attr('class', 'value'); + + label = label.merge(labelEnter); } + function lanes(selection) { + + lanesData = context.entity(wayID).lanes(); + var lanesArray = lanesData.lanesArray; + + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { + selection.call(lanes.off); + return; + } + + var lanesInfo = selection.selectAll('.lanes-info').data([0]); + lanesInfo = lanesInfo.enter() + .append('div') + .attr('class', 'lanes-info') + .merge(lanesInfo); + console.log('called', lanesData.metadata); + lanesInfoUI(lanesInfo); + + var turnLanes = selection.selectAll('.turn-lanes').data([0]); + turnLanes = turnLanes.enter() + .append('div') + .attr('class', 'turn-lanes') + .merge(turnLanes); + turnLanesUI(turnLanes); + + var wrap = selection.selectAll('.lane-input-wrap') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'lane-input-wrap') + .merge(wrap); + + + var surface = wrap.selectAll('.surface') + .data([0]); + + + var d = utilGetDimensions(wrap); + var freeSpace = d[0] - lanesData.metadata.count * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; + + + } + + function change(d) { + var tag = {}; + if (d === 'count') { + tag.lanes = utilGetSetValue(d3.select(this)) || undefined; + } + if (d === 'forward') { + tag['lanes:forward'] = utilGetSetValue(d3.select(this)) || undefined; + } + if (d === 'backward') { + tag['lanes:backward'] = utilGetSetValue(d3.select(this)) || undefined; + } + if (d === 'maxspeed') { + tag['maxspeed:lanes'] = utilGetSetValue(d3.select(this)) || undefined; + } + if (d === 'oneway') { + tag.oneway = utilGetSetValue(d3.select(this)) || undefined; + } + dispatch.call('change', this, tag); + } + lanes.entity = function(_) { if (!wayID || wayID !== _.id) { wayID = _.id; @@ -131,5 +229,130 @@ export function uiFieldLanes(field, context) { lanes.focus = function() {}; lanes.off = function() {}; + function laneSvg() { + // surface = surface.enter() + // .append('svg') + // .attr('width', d[0]) + // .attr('height', 300) + // .attr('class', 'surface') + // .merge(surface); + + + // var lanesSelection = surface.selectAll('.lanes') + // .data([0]); + + // lanesSelection = lanesSelection.enter() + // .append('g') + // .attr('class', 'lanes') + // .merge(lanesSelection); + + // lanesSelection + // .attr('transform', function () { + // return 'translate(' + (freeSpace / 2) + ', 0)'; + // }); + + + // // var lanesArray = + // var lane = lanesSelection.selectAll('.lane') + // .data(new Array(lanesData.metadata.count)); + + // lane.exit() + // .remove(); + + // var enter = lane.enter() + // .append('g') + // .attr('class', 'lane'); + + // enter + // .append('g') + // .append('rect') + // .attr('y', 50) + // .attr('width', LANE_WIDTH) + // .attr('height', LANE_HEIGHT) + // .attr('transform', function (d, i) { + // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; + // }); + + // enter + // .append('g') + // .attr('class', 'forward') + // .append('text') + // .attr('y', 40) + // .attr('x', 14) + // .text('▲') + // .attr('transform', function (d, i) { + // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; + // }); + + // enter + // .append('g') + // .attr('class', 'bothways') + // .append('text') + // .attr('y', 40) + // .attr('x', 14) + // .text('▲▼') + // .attr('transform', function (d, i) { + // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; + // }); + + // enter + // .append('g') + // .attr('class', 'backward') + // .append('text') + // .attr('y', 40) + // .attr('x', 14) + // .text('▼') + // .attr('transform', function (d, i) { + // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; + // }); + + + // lane = lane + // .merge(enter); + + // lane + // .attr('transform', function(d, i) { + // return 'translate(' + (LANE_WIDTH * i * 1.5) + ', 0)'; + // }); + + + // var te = wrap.selectAll('.lane-text').data([0]); + + // te + // .enter() + // .append('div') + // .attr('class', 'lane-text') + // .text('check 123'); + + // var sel = wrap.selectAll('.lane-text'); + + // sel.on('click', function() { + // var t = {}; + // t['kushan'] = 'joshi' + Math.random(); + // dispatch.call('change', this, t); + // d3.event.stopPropagation(); + + // }); + + + + + // te.exit().remove(); + + // lane.select('.forward') + // .style('visibility', function(d) { + // return d.direction === 'forward' ? 'visible' : 'hidden'; + // }); + + // lane.select('.bothways') + // .style('visibility', function(d) { + // return d.direction === 'bothways' ? 'visible' : 'hidden'; + // }); + + // lane.select('.backward') + // .style('visibility', function(d) { + // return d.direction === 'backward' ? 'visible' : 'hidden'; + // }); + } return utilRebind(lanes, dispatch, 'on'); } diff --git a/test/spec/osm/lanes.js b/test/spec/osm/lanes.js index 4a072a28c9e..c704af8bace 100644 --- a/test/spec/osm/lanes.js +++ b/test/spec/osm/lanes.js @@ -1,4 +1,4 @@ -describe('iD.Lanes', function() { +describe.only('iD.Lanes', function() { describe('default lane tags', function() { @@ -271,6 +271,7 @@ describe('iD.Lanes', function() { backward: 0, bothways: 0 }); + }); it('skips provided lanes:backward value when oneway=yes', function() { @@ -624,7 +625,7 @@ describe('iD.Lanes', function() { ]); }); - it('turnLanes is undefined when not present', function() { + it('turnLanes is [] when not present', function() { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -634,14 +635,14 @@ describe('iD.Lanes', function() { }).lanes().metadata; expect(metadata.turnLanes.unspecified) - .to.equal(undefined); + .to.deep.equal([]); expect(metadata.turnLanes.forward) - .to.equal(undefined); + .to.deep.equal([]); expect(metadata.turnLanes.backward) - .to.equal(undefined); + .to.deep.equal([]); }); - it('turnLanes.forward and turnLanes.backward are both undefined when both are not provided', function() { + it('turnLanes.forward and turnLanes.backward are both [] when both are not provided', function() { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -654,9 +655,9 @@ describe('iD.Lanes', function() { expect(metadata.turnLanes.unspecified) .to.deep.equal([['through'], ['through', 'slight_right']]); expect(metadata.turnLanes.forward) - .to.equal(undefined); + .to.deep.equal([]); expect(metadata.turnLanes.backward) - .to.equal(undefined); + .to.deep.equal([]); }); it('parses turnLane correctly when lanes:both_ways=1', function() { From 8b6ac28624f8a8e49a972c91fdb0292431d7c333 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 19 Jan 2017 09:46:26 +0530 Subject: [PATCH 02/25] interim 2 --- css/app.css | 20 +++++++- modules/ui/fields/lanes.js | 95 +++++++++++++++----------------------- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/css/app.css b/css/app.css index 6e200c65e86..7a6f1966f14 100644 --- a/css/app.css +++ b/css/app.css @@ -1227,11 +1227,29 @@ button.save.has-count .count::before { } -.form-field-lanes > .lanes-info { +.form-field-lanes .lanes-info { margin-bottom: 10px; } +.form-field-lanes .preset-input-wrap input { + border-radius: 0; + border-width: 0; + border-bottom-width: 1px; +} + +.form-field-lanes .preset-input-lanes-wrap input { + border-radius: 0; + border-width: 0; + border-left-width: 1px; +} +.form-field-lanes .preset-input-wrap li { + border-bottom: 1px solid #CCC; +} + +.form-field-lanes .preset-input-wrap li:last-child { + border-bottom: 0; +} .lane-tags > label { position: relative; diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 63bfcfff914..ded355ed97d 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -7,17 +7,17 @@ import { d3combobox } from '../../lib/d3.combobox.js'; import { utilGetSetValue } from '../../util/get_set_value'; -function validLanes() { - return [ +var validLanes = [ 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' ]; -} + export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), LANE_WIDTH = 40, LANE_HEIGHT = 200, + currentLane = 0, wayID, lanesData; @@ -29,7 +29,7 @@ export function uiFieldLanes(field, context) { wrap = wrap.enter() .append('div') - .attr('class', 'cf preset-input-wrap') + .attr('class', 'preset-input-wrap') .append('ul') .merge(wrap); @@ -51,17 +51,17 @@ export function uiFieldLanes(field, context) { enter .append('span') - .attr('class', 'col6 label preset-label-access') - .attr('for', function(d) { return 'preset-input-access-' + d; }) + .attr('class', 'col6 label preset-label-') + .attr('for', function(d) { return 'preset-input-' + d; }) .text(function(d) { return d; }); enter .append('div') - .attr('class', 'col6 preset-input-access-wrap') + .attr('class', 'col6 preset-input-lanes-wrap') .append('input') .attr('type', 'text') - .attr('class', 'preset-input-access') - .attr('id', function(d) { return 'preset-input-access-' + d; }) + .attr('class', 'preset-input-lanes') + .attr('id', function(d) { return 'preset-input-lanes-' + d; }) .each(function(d) { this.value = metadata[d]; @@ -71,6 +71,7 @@ export function uiFieldLanes(field, context) { items = items.merge(enter); items.selectAll('input') .property('value', function (d) { + console.log('there'); return metadata[d]; }); @@ -80,62 +81,27 @@ export function uiFieldLanes(field, context) { } function turnLanesUI(selection) { - - - var laneTags = selection.selectAll('.lane-tags').data([0]); - - - laneTags + selection.selectAll('.form-label') + .data([0]) .enter() .append('label') .attr('class','form-label') .text('Turn Lanes'); - var laneTagsEnter = laneTags - .enter() - .append('div') - .attr('class', 'lane-tags cf preset-input-wrap') - .classed('checkselect', 'true'); - - var inputNumber = laneTagsEnter.append('input') - .attr('type', 'field.type') - .attr('id', 'fieldId') - .attr('placeholder', 'inspector.unknown') - .attr('type', 'text'); - - var spinControl = laneTagsEnter.selectAll('.spin-control') + var wrap = selection.selectAll('.preset-input-wrap') .data([0]); - - var spinControlEnter = spinControl.enter() + var metadata = lanesData.metadata; + + wrap = wrap.enter() .append('div') - .attr('class', 'spin-control'); - - spinControlEnter - .append('button') - .datum(1) - .attr('class', 'increment') - .attr('tabindex', -1); - - spinControlEnter - .append('button') - .datum(-1) - .attr('class', 'decrement') - .attr('tabindex', -1); - - spinControl = spinControl - .merge(spinControlEnter); - - spinControl.selectAll('button') - .on('click', function(d) { - d3.event.preventDefault(); - var num = parseInt(inputNumber.node().value || 0, 10); - if (!isNaN(num) && num + d > 0 && num + d <= lanesData.metadata.count) inputNumber.node().value = num + d; - }); - + .attr('class', 'preset-input-wrap checkselect') + // .append('ul'); + // // .merge(wrap); + - var label = laneTagsEnter.selectAll('.label') - .data(validLanes()); + var label = wrap.selectAll('.label') + .data(validLanes); var labelEnter = label.enter() .append('label'); @@ -151,7 +117,11 @@ export function uiFieldLanes(field, context) { .text(function (d) { return d;}) .attr('class', 'value'); - label = label.merge(labelEnter); + // label = label.merge(labelEnter); + + selection.selectAll('input').property('checked', function () { + return Math.random() > 0.4; + }); } @@ -176,8 +146,12 @@ export function uiFieldLanes(field, context) { var turnLanes = selection.selectAll('.turn-lanes').data([0]); turnLanes = turnLanes.enter() .append('div') - .attr('class', 'turn-lanes') + .attr('class', 'turn-lanes localized-wrap') + // .append('div') + // .attr('class', 'entry') + // .attr('style', 'margin-top: 10px;opacity: 1;overflow: visible;') .merge(turnLanes); + turnLanesUI(turnLanes); var wrap = selection.selectAll('.lane-input-wrap') @@ -200,6 +174,7 @@ export function uiFieldLanes(field, context) { } function change(d) { + console.log('hola') var tag = {}; if (d === 'count') { tag.lanes = utilGetSetValue(d3.select(this)) || undefined; @@ -225,7 +200,9 @@ export function uiFieldLanes(field, context) { } }; - lanes.tags = function() {}; + lanes.tags = function(tags) { + console.log(tags, field.key, 'lolololoxsxs'); + }; lanes.focus = function() {}; lanes.off = function() {}; From 40fa12f0d9aeae33c63d8b78ef5e0b135ad85259 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 19 Jan 2017 12:32:32 +0530 Subject: [PATCH 03/25] select lane part 1 --- css/app.css | 6 +++ modules/ui/fields/lanes.js | 79 ++++++++++++++++++++++++++++++-------- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/css/app.css b/css/app.css index 7a6f1966f14..b3e7792ee2d 100644 --- a/css/app.css +++ b/css/app.css @@ -1251,6 +1251,12 @@ button.save.has-count .count::before { border-bottom: 0; } +.form-field-lanes .entry { + margin-top: 10px; + opacity: 1; + overflow: visible; +} + .lane-tags > label { position: relative; padding: 5px 10px; diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index ded355ed97d..1508405a8a7 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -64,14 +64,12 @@ export function uiFieldLanes(field, context) { .attr('id', function(d) { return 'preset-input-lanes-' + d; }) .each(function(d) { this.value = metadata[d]; - }); // Update items = items.merge(enter); items.selectAll('input') .property('value', function (d) { - console.log('there'); return metadata[d]; }); @@ -85,19 +83,15 @@ export function uiFieldLanes(field, context) { .data([0]) .enter() .append('label') - .attr('class','form-label') + .attr('class','form-label entry') .text('Turn Lanes'); var wrap = selection.selectAll('.preset-input-wrap') .data([0]); - var metadata = lanesData.metadata; wrap = wrap.enter() .append('div') - .attr('class', 'preset-input-wrap checkselect') - // .append('ul'); - // // .merge(wrap); - + .attr('class', 'lane-tags preset-input-wrap checkselect') var label = wrap.selectAll('.label') @@ -117,11 +111,55 @@ export function uiFieldLanes(field, context) { .text(function (d) { return d;}) .attr('class', 'value'); - // label = label.merge(labelEnter); - - selection.selectAll('input').property('checked', function () { + var input = selection.selectAll('input'); + input.property('checked', function () { return Math.random() > 0.4; }); + + input.on('click', function() { + var key = utilGetSetValue(d3.select(this)) || undefined; + console.log(key); + d3.event.stopPropagation(); + }); + } + + function laneSelectorUI(selection) { + var items; + selection.selectAll('.form-label') + .data([0]) + .enter() + .append('label') + .attr('class','form-label entry') + .text('Select Lane'); + + var wrap = selection.selectAll('.preset-input-wrap') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'lane-tags preset-input-wrap checkselect') + .merge(wrap); + + var list = wrap.selectAll('ul') + .data([0]); + + list = list.enter() + .append('ul') + .merge(list); + + items = list.selectAll('li') + .data(_.fill(Array(lanesData.metadata.count), 0).map(function (n, i) { + return i; + })); + + items.enter() + .append('li') + .append('span').text(function (d) { + return d; + }); + + items.exit().remove(); + } @@ -140,19 +178,26 @@ export function uiFieldLanes(field, context) { .append('div') .attr('class', 'lanes-info') .merge(lanesInfo); - console.log('called', lanesData.metadata); - lanesInfoUI(lanesInfo); + lanesInfo.call(lanesInfoUI); + + var laneSelector = selection.selectAll('.lanes-selector').data([0]); + + laneSelector = laneSelector.enter() + .append('div') + .attr('class', 'lanes-selector localized-wrap') + .merge(laneSelector); + + laneSelector.call(laneSelectorUI); + var turnLanes = selection.selectAll('.turn-lanes').data([0]); turnLanes = turnLanes.enter() .append('div') .attr('class', 'turn-lanes localized-wrap') - // .append('div') - // .attr('class', 'entry') - // .attr('style', 'margin-top: 10px;opacity: 1;overflow: visible;') .merge(turnLanes); + turnLanes.call(turnLanesUI); + - turnLanesUI(turnLanes); var wrap = selection.selectAll('.lane-input-wrap') .data([0]); From 8b8522f6d2e2f43e2ea16f2337d9ab9299b45472 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 19 Jan 2017 17:56:36 +0530 Subject: [PATCH 04/25] interim 3 --- .eslintrc | 2 +- css/app.css | 36 ++++ modules/osm/lanes.js | 8 +- modules/ui/field.js | 52 ----- modules/ui/fields/lanes.js | 427 ++++++++++++++++++++----------------- 5 files changed, 270 insertions(+), 255 deletions(-) delete mode 100644 modules/ui/field.js diff --git a/.eslintrc b/.eslintrc index 0e0fbd05145..5a358c79ab8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,7 +17,7 @@ "rules": { "dot-notation": "error", "eqeqeq": ["error", "smart"], - "indent": ["off", 4], + "indent": ["error", 4], "keyword-spacing": "error", "linebreak-style": ["error", "unix"], "no-caller": "error", diff --git a/css/app.css b/css/app.css index b3e7792ee2d..f38d52727b4 100644 --- a/css/app.css +++ b/css/app.css @@ -1257,6 +1257,42 @@ button.save.has-count .count::before { overflow: visible; } +.lane-selector ul { + display: flex; + flex-direction: row; + +} + +.lane-selector .lane-item { + cursor: pointer; + display: flex; + flex-grow: 1; + border-right: 1px solid #ccc; + border-bottom: 0; + flex-direction: column; + align-items: center; + background-color: white; +} + +.lane-selector .lane-item.active { + background-color: #E8EBFF; +} +.lane-selector .lane-item.active:hover { + background-color: #E8EBFF; +} +.lane-selector .lane-item:hover { + background-color: #ececec; +} + +.lane-selector .lane-item:last-child { + border-right: 0; +} + +.lane-selector .lane-item span { + height: 30px; + padding: 5px 10px 5px 10px; +} + .lane-tags > label { position: relative; padding: 5px 10px; diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 5eac22c7c5f..78219dccea4 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -89,7 +89,9 @@ export function osmLanes(entity) { taxiLanes: taxiLanes, hovLanes: hovLanes, hgvLanes: hgvLanes, - bicyclewayLanes: bicyclewayLanes + bicyclewayLanes: bicyclewayLanes, + leftHandDrive: false, + reverse: parseInt(tags.oneway, 10) === -1 }, lanes: lanesObj, lanesArray: flattenLanesArray(lanesArray( @@ -107,7 +109,9 @@ export function osmLanes(entity) { taxiLanes: taxiLanes, hovLanes: hovLanes, hgvLanes: hgvLanes, - bicyclewayLanes: bicyclewayLanes + bicyclewayLanes: bicyclewayLanes, + leftHandDrive: false, + reverse: parseInt(tags.oneway, 10) === -1 })) }; } diff --git a/modules/ui/field.js b/modules/ui/field.js deleted file mode 100644 index af817558d47..00000000000 --- a/modules/ui/field.js +++ /dev/null @@ -1,52 +0,0 @@ -import { uiFields } from './fields/index'; - -function UIField(field, entity, show, context) { - field = _.clone(field); - - field.input = uiFields[field.type](field, context) - .on('change', function(t, onInput) { - dispatch.call('change', field, t, onInput); - }); - - if (field.input.entity) field.input.entity(entity); - - field.keys = field.keys || [field.key]; - - field.show = show; - - field.shown = function() { - return field.id === 'name' || field.show || _.some(field.keys, function(key) { return !!tags[key]; }); - }; - - field.modified = function() { - var original = context.graph().base().entities[entity.id]; - return _.some(field.keys, function(key) { - return original ? tags[key] !== original.tags[key] : tags[key]; - }); - }; - - field.revert = function() { - var original = context.graph().base().entities[entity.id], - t = {}; - field.keys.forEach(function(key) { - t[key] = original ? original.tags[key] : undefined; - }); - return t; - }; - - field.present = function() { - return _.some(field.keys, function(key) { - return tags[key]; - }); - }; - - field.remove = function() { - var t = {}; - field.keys.forEach(function(key) { - t[key] = undefined; - }); - return t; - }; - - return field; - } \ No newline at end of file diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 1508405a8a7..a8dd0584eae 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -20,224 +20,251 @@ export function uiFieldLanes(field, context) { currentLane = 0, wayID, lanesData; - - function lanesInfoUI(selection) { - var wrap = selection.selectAll('.preset-input-wrap') - .data([0]); - var keysConsidered = ['count', 'forward', 'backward', 'maxspeed']; - var metadata = lanesData.metadata; - - wrap = wrap.enter() - .append('div') - .attr('class', 'preset-input-wrap') - .append('ul') - .merge(wrap); - - var list = wrap.selectAll('ul') - .data([0]); - - list = list.enter() - .append('ul') - .merge(list); - - - var items = list.selectAll('li') - .data(keysConsidered); - - // Enter - var enter = items.enter() - .append('li') - .attr('class', function(d) { return 'cf preset-access-' + d; }); - - enter - .append('span') - .attr('class', 'col6 label preset-label-') - .attr('for', function(d) { return 'preset-input-' + d; }) - .text(function(d) { return d; }); - - enter - .append('div') - .attr('class', 'col6 preset-input-lanes-wrap') - .append('input') - .attr('type', 'text') - .attr('class', 'preset-input-lanes') - .attr('id', function(d) { return 'preset-input-lanes-' + d; }) - .each(function(d) { - this.value = metadata[d]; - }); - // Update - items = items.merge(enter); - items.selectAll('input') - .property('value', function (d) { - return metadata[d]; - }); + function lanes(selection) { + lanesData = context.entity(wayID).lanes(); + var lanesArray = lanesData.lanesArray; + window.lanesData = lanesData; + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { + selection.call(lanes.off); + return; + } + + var lanesInfo = selection.selectAll('.lanes-info').data([0]); + lanesInfo = lanesInfo.enter() + .append('div') + .attr('class', 'lanes-info') + .merge(lanesInfo); + lanesInfo.call(lanesInfoUI); + + var laneSelector = selection.selectAll('.lane-selector').data([0]); + + laneSelector = laneSelector.enter() + .append('div') + .attr('class', 'lane-selector localized-wrap') + .merge(laneSelector); + + laneSelector.call(laneSelectorUI); + + var turnLanes = selection.selectAll('.turn-lanes').data([0]); + turnLanes = turnLanes.enter() + .append('div') + .attr('class', 'turn-lanes localized-wrap') + .merge(turnLanes); + turnLanes.call(turnLanesUI); + + var wrap = selection.selectAll('.lane-input-wrap') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'lane-input-wrap') + .merge(wrap); + + var surface = wrap.selectAll('.surface') + .data([0]); - items.selectAll('input') - .on('change', change) - .on('blur', change); - } + var d = utilGetDimensions(wrap); + var freeSpace = d[0] - lanesData.metadata.count * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; - function turnLanesUI(selection) { - selection.selectAll('.form-label') - .data([0]) - .enter() - .append('label') - .attr('class','form-label entry') - .text('Turn Lanes'); + function render() { + if (context.hasEntity(wayID)) { + lanes(selection); + } + } - var wrap = selection.selectAll('.preset-input-wrap') - .data([0]); - wrap = wrap.enter() - .append('div') - .attr('class', 'lane-tags preset-input-wrap checkselect') - - - var label = wrap.selectAll('.label') - .data(validLanes); - - var labelEnter = label.enter() - .append('label'); - - labelEnter - .append('input') - .property('indeterminate', field.type === 'check') - .attr('type', 'checkbox') - .attr('id', 'preset-input-' + field.id); - - labelEnter - .append('span') - .text(function (d) { return d;}) - .attr('class', 'value'); - - var input = selection.selectAll('input'); - input.property('checked', function () { - return Math.random() > 0.4; - }); - - input.on('click', function() { - var key = utilGetSetValue(d3.select(this)) || undefined; - console.log(key); - d3.event.stopPropagation(); - }); - } - - function laneSelectorUI(selection) { - var items; - selection.selectAll('.form-label') - .data([0]) - .enter() - .append('label') - .attr('class','form-label entry') - .text('Select Lane'); - - var wrap = selection.selectAll('.preset-input-wrap') - .data([0]); + function lanesInfoUI(selection) { + var wrap = selection.selectAll('.preset-input-wrap') + .data([0]); + var keysConsidered = ['count', 'forward', 'backward', 'reverse']; - wrap = wrap.enter() - .append('div') - .attr('class', 'lane-tags preset-input-wrap checkselect') - .merge(wrap); - - var list = wrap.selectAll('ul') - .data([0]); + var metadata = lanesData.metadata; + + wrap = wrap.enter() + .append('div') + .attr('class', 'preset-input-wrap') + .append('ul') + .merge(wrap); + + var list = wrap.selectAll('ul') + .data([0]); + + list = list.enter() + .append('ul') + .merge(list); + + + var items = list.selectAll('li') + .data(keysConsidered); + + // Enter + var enter = items.enter() + .append('li') + .attr('class', function(d) { return 'cf preset-access-' + d; }); + + enter + .append('span') + .attr('class', 'col6 label preset-label-') + .attr('for', function(d) { return 'preset-input-' + d; }) + .text(function(d) { return d; }); + + enter + .append('div') + .attr('class', 'col6 preset-input-lanes-wrap') + .append('input') + .attr('type', 'text') + .attr('class', 'preset-input-lanes') + .attr('id', function(d) { return 'preset-input-lanes-' + d; }) + .each(function(d) { + this.value = metadata[d]; + }); + + // Update + items = items.merge(enter); + items.selectAll('input') + .property('value', function (d) { + return metadata[d]; + }); - list = list.enter() - .append('ul') - .merge(list); + items.selectAll('input') + .on('change', _.debounce(change, 700)) + .on('blur', _.debounce(change, 700)); + + function change(d) { + console.log('hola'); + var tag = {}; + if (d === 'count') { + tag.lanes = utilGetSetValue(d3.select(this)) || undefined; + } + if (d === 'forward') { + tag['lanes:forward'] = utilGetSetValue(d3.select(this)) || undefined; + } + if (d === 'backward') { + tag['lanes:backward'] = utilGetSetValue(d3.select(this)) || undefined; + } + if (d === 'reverse') { + if (utilGetSetValue(d3.select(this)) === 'true') { + tag.oneway = '-1'; + } else { + tag.oneway = undefined; + } + } + console.log(tag); + dispatch.call('change', this, tag); + } + } - items = list.selectAll('li') - .data(_.fill(Array(lanesData.metadata.count), 0).map(function (n, i) { - return i; - })); - - items.enter() - .append('li') - .append('span').text(function (d) { - return d; + function turnLanesUI(selection) { + var lanesArray = lanesData.lanesArray; + selection.selectAll('.form-label') + .data([0]) + .enter() + .append('label') + .attr('class','form-label entry') + .text('Turn Lanes'); + + var wrap = selection.selectAll('.preset-input-wrap') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'lane-tags preset-input-wrap checkselect'); + + + var label = wrap.selectAll('.label') + .data(validLanes); + + var labelEnter = label.enter() + .append('label'); + + labelEnter + .append('input') + .property('indeterminate', field.type === 'check') + .attr('type', 'checkbox') + .attr('id', 'preset-input-' + field.id); + + labelEnter + .append('span') + .text(function (d) { return d;}) + .attr('class', 'value'); + console.log(lanesArray[currentLane]); + var input = selection.selectAll('input'); + input.property('checked', function (d) { + if (_.isArray(lanesArray[currentLane].turnLanes)) { + return lanesArray[currentLane].turnLanes.filter(function (el) { + return el === d; + }).length === 1; + } + return false; + // return Math.random() > 0.4; }); - items.exit().remove(); - - } - - - function lanes(selection) { - - lanesData = context.entity(wayID).lanes(); - var lanesArray = lanesData.lanesArray; - - if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { - selection.call(lanes.off); - return; + input.on('click', function() { + var key = utilGetSetValue(d3.select(this)) || undefined; + console.log(key); + d3.event.stopPropagation(); + }); } - var lanesInfo = selection.selectAll('.lanes-info').data([0]); - lanesInfo = lanesInfo.enter() - .append('div') - .attr('class', 'lanes-info') - .merge(lanesInfo); - lanesInfo.call(lanesInfoUI); - - var laneSelector = selection.selectAll('.lanes-selector').data([0]); - - laneSelector = laneSelector.enter() - .append('div') - .attr('class', 'lanes-selector localized-wrap') - .merge(laneSelector); - - laneSelector.call(laneSelectorUI); - - - var turnLanes = selection.selectAll('.turn-lanes').data([0]); - turnLanes = turnLanes.enter() - .append('div') - .attr('class', 'turn-lanes localized-wrap') - .merge(turnLanes); - turnLanes.call(turnLanesUI); - - - - var wrap = selection.selectAll('.lane-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'lane-input-wrap') - .merge(wrap); + function laneSelectorUI(selection) { + var items; + selection.selectAll('.form-label') + .data([0]) + .enter() + .append('label') + .attr('class','form-label entry') + .text('Select Lane'); + + var wrap = selection.selectAll('.preset-input-wrap') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'lane-tags preset-input-wrap checkselect') + .merge(wrap); - - var surface = wrap.selectAll('.surface') - .data([0]); + var list = wrap.selectAll('ul') + .data([0]); + list = list.enter() + .append('ul') + .merge(list); - var d = utilGetDimensions(wrap); - var freeSpace = d[0] - lanesData.metadata.count * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; + items = list.selectAll('.lane-item') + .data(_.fill(Array(lanesData.metadata.count), 0).map(function (n, i) { + return i; + })); + + items.enter() + .append('div') + .attr('class', 'lane-item') + .attr('id', function (d) { + return 'lane-' + d; + }) + .classed('active', function (d) { + return d === currentLane; + }) + .append('span') + .text(function (d) { + return d + 1; + }); + + var input = selection.selectAll('.lane-item'); + + input.on('click', function () { + var lane = parseInt(d3.select(this).selectAll('span').text(), 10) - 1; + selection.select('#lane-' + currentLane).classed('active', false); + currentLane = lane; + d3.select(this).classed('active', true); + render(); + }); + items.exit().remove(); + } - } - function change(d) { - console.log('hola') - var tag = {}; - if (d === 'count') { - tag.lanes = utilGetSetValue(d3.select(this)) || undefined; - } - if (d === 'forward') { - tag['lanes:forward'] = utilGetSetValue(d3.select(this)) || undefined; - } - if (d === 'backward') { - tag['lanes:backward'] = utilGetSetValue(d3.select(this)) || undefined; - } - if (d === 'maxspeed') { - tag['maxspeed:lanes'] = utilGetSetValue(d3.select(this)) || undefined; - } - if (d === 'oneway') { - tag.oneway = utilGetSetValue(d3.select(this)) || undefined; - } - dispatch.call('change', this, tag); - } + lanes.entity = function(_) { if (!wayID || wayID !== _.id) { From bf0e8b42e5b510254fa5ec81447cd3c3a4b4c7ea Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sat, 21 Jan 2017 10:19:02 +0530 Subject: [PATCH 05/25] add turn selector UI? --- .eslintrc | 2 +- development_server.js | 14 +- modules/osm/lanes.js | 32 +--- modules/ui/entity_editor.js | 1 - modules/ui/fields/lanes.js | 291 ++++++++++++++++++++++++++---------- 5 files changed, 223 insertions(+), 117 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5a358c79ab8..0e0fbd05145 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,7 +17,7 @@ "rules": { "dot-notation": "error", "eqeqeq": ["error", "smart"], - "indent": ["error", 4], + "indent": ["off", 4], "keyword-spacing": "error", "linebreak-style": ["error", "unix"], "no-caller": "error", diff --git a/development_server.js b/development_server.js index c8b40bc538a..4060527c5b3 100644 --- a/development_server.js +++ b/development_server.js @@ -10,8 +10,8 @@ var gaze = require('gaze'); var ecstatic = require('ecstatic'); var building = false; - - +var cache; +var cacheCount = 0; if (process.argv[2] === 'develop') { build(); @@ -56,7 +56,8 @@ function build() { }), commonjs(), json() - ] + ], + cache: cache }).then(function (bundle) { bundle.write({ @@ -67,7 +68,12 @@ function build() { }); building = false; console.timeEnd('Rebuilt'); - + cache = bundle; + if (cacheCount === 5) { + cache = undefined; + cacheCount = 0; + } + cacheCount++; }, function(err) { building = false; console.error(err); diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 78219dccea4..8f2c72644b4 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -93,27 +93,8 @@ export function osmLanes(entity) { leftHandDrive: false, reverse: parseInt(tags.oneway, 10) === -1 }, - lanes: lanesObj, - lanesArray: flattenLanesArray(lanesArray( - { - count: laneCount, - oneway: isOneWay, - forward: forward, - backward: backward, - bothways: bothways, - turnLanes: turnLanes, - maxspeed: maxspeed, - maxspeedLanes: maxspeedLanes, - psvLanes: psvLanes, - busLanes: busLanes, - taxiLanes: taxiLanes, - hovLanes: hovLanes, - hgvLanes: hgvLanes, - bicyclewayLanes: bicyclewayLanes, - leftHandDrive: false, - reverse: parseInt(tags.oneway, 10) === -1 - })) - }; + lanes: lanesObj + }; } @@ -268,15 +249,6 @@ function mapToLanesObj(lanesObj, data, key) { }); } -function flattenLanesArray(lanes) { - var order = ['backward', 'forward']; - var ret = [].concat(lanes[order[0]], lanes[order[1]]); - for (var i = 0; i < ret.length; i++) { - ret[i] = _.assign(ret[i] || {}, lanes.unspecified[i]); - } - return ret; -} - function lanesArray(lanesData) { var metadata = _.cloneDeep(lanesData); // var arr = new Array(metadata.count); diff --git a/modules/ui/entity_editor.js b/modules/ui/entity_editor.js index e5fc7bb9070..ffe2b871650 100644 --- a/modules/ui/entity_editor.js +++ b/modules/ui/entity_editor.js @@ -180,7 +180,6 @@ export function uiEntityEditor(context) { var blacklist = ['description', 'note', 'fixme']; if (_.some(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v; - var cleaned = v.split(';') .map(function(s) { return s.trim(); }) .join(keepSpaces(k) ? '; ' : ';'); diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index a8dd0584eae..e84e7c7606a 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -8,66 +8,72 @@ import { utilGetSetValue } from '../../util/get_set_value'; var validLanes = [ - 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', - 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' - ]; + 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', + 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' +]; export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), LANE_WIDTH = 40, LANE_HEIGHT = 200, + // TODO: currentLane if big like 6 goes crazy if other wayID has less than 6 lanes currentLane = 0, + curDirection = 'unspecified', wayID, lanesData; function lanes(selection) { - lanesData = context.entity(wayID).lanes(); - var lanesArray = lanesData.lanesArray; - window.lanesData = lanesData; - if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { - selection.call(lanes.off); - return; - } + console.log('hollaa mee', currentLane, curDirection) + lanesData = context.entity(wayID).lanes(); + var lanesArray = lanesData.lanesArray; + window.lanesData = lanesData; + window.currentLane = currentLane; + window.curDirection = curDirection; + + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { + selection.call(lanes.off); + return; + } - var lanesInfo = selection.selectAll('.lanes-info').data([0]); - lanesInfo = lanesInfo.enter() + var lanesInfo = selection.selectAll('.lanes-info').data([0]); + lanesInfo = lanesInfo.enter() .append('div') .attr('class', 'lanes-info') .merge(lanesInfo); - lanesInfo.call(lanesInfoUI); + lanesInfo.call(lanesInfoUI); - var laneSelector = selection.selectAll('.lane-selector').data([0]); + var laneSelector = selection.selectAll('.lane-selector').data([0]); - laneSelector = laneSelector.enter() + laneSelector = laneSelector.enter() .append('div') .attr('class', 'lane-selector localized-wrap') .merge(laneSelector); - laneSelector.call(laneSelectorUI); + laneSelector.call(laneSelectorUI); - var turnLanes = selection.selectAll('.turn-lanes').data([0]); - turnLanes = turnLanes.enter() + var turnLanes = selection.selectAll('.turn-lanes').data([0]); + turnLanes = turnLanes.enter() .append('div') .attr('class', 'turn-lanes localized-wrap') .merge(turnLanes); - turnLanes.call(turnLanesUI); + turnLanes.call(turnLanesUI); - var wrap = selection.selectAll('.lane-input-wrap') + var wrap = selection.selectAll('.lane-input-wrap') .data([0]); - wrap = wrap.enter() + wrap = wrap.enter() .append('div') .attr('class', 'lane-input-wrap') .merge(wrap); - var surface = wrap.selectAll('.surface') + var surface = wrap.selectAll('.surface') .data([0]); var d = utilGetDimensions(wrap); var freeSpace = d[0] - lanesData.metadata.count * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; - function render() { + function render() { if (context.hasEntity(wayID)) { lanes(selection); } @@ -75,6 +81,8 @@ export function uiFieldLanes(field, context) { function lanesInfoUI(selection) { + // TODO: make this thing dynamic and show keysConsidered + // wrt to oneway. var wrap = selection.selectAll('.preset-input-wrap') .data([0]); var keysConsidered = ['count', 'forward', 'backward', 'reverse']; @@ -117,8 +125,8 @@ export function uiFieldLanes(field, context) { .attr('class', 'preset-input-lanes') .attr('id', function(d) { return 'preset-input-lanes-' + d; }) .each(function(d) { - this.value = metadata[d]; - }); + this.value = metadata[d]; + }); // Update items = items.merge(enter); @@ -127,36 +135,50 @@ export function uiFieldLanes(field, context) { return metadata[d]; }); - items.selectAll('input') - .on('change', _.debounce(change, 700)) - .on('blur', _.debounce(change, 700)); - - function change(d) { - console.log('hola'); - var tag = {}; - if (d === 'count') { - tag.lanes = utilGetSetValue(d3.select(this)) || undefined; - } - if (d === 'forward') { - tag['lanes:forward'] = utilGetSetValue(d3.select(this)) || undefined; - } - if (d === 'backward') { - tag['lanes:backward'] = utilGetSetValue(d3.select(this)) || undefined; - } - if (d === 'reverse') { - if (utilGetSetValue(d3.select(this)) === 'true') { - tag.oneway = '-1'; + + // selection.selectAll('.preset-access-count') + // .attr('hidden', function () { + // return metadata.oneway; + // }); + + var input = items.selectAll('input'); + + input + .on('change',change) + .on('blur', change); + + function change(d) { + if (metadata.oneway) { + if (d === 'count') { + var count = utilGetSetValue(d3.select(this)); + count = parseInt(count, 10); + if (!_.isNaN(count)) metadata.count = count; + } + if (d === 'reverse') { + if (utilGetSetValue(d3.select(this)) === 'true') { + metadata.reverse = true; + } else { + metadata.reverse = false; + } + } } else { - tag.oneway = undefined; + if (d === 'forward') { + var forward = utilGetSetValue(d3.select(this)); + forward = parseInt(forward, 10); + if (!_.isNaN(forward)) metadata.forward = forward; + } + if (d === 'backward') { + var backward = utilGetSetValue(d3.select(this)); + backward = parseInt(backward, 10); + if (!_.isNaN(forward)) metadata.backward = backward; + } } - } - console.log(tag); - dispatch.call('change', this, tag); + megaChange(); } } function turnLanesUI(selection) { - var lanesArray = lanesData.lanesArray; + var turnLanes = lanesData.metadata.turnLanes; selection.selectAll('.form-label') .data([0]) .enter() @@ -188,27 +210,54 @@ export function uiFieldLanes(field, context) { .append('span') .text(function (d) { return d;}) .attr('class', 'value'); - console.log(lanesArray[currentLane]); var input = selection.selectAll('input'); input.property('checked', function (d) { - if (_.isArray(lanesArray[currentLane].turnLanes)) { - return lanesArray[currentLane].turnLanes.filter(function (el) { + if (turnLanes[curDirection] && _.isArray(turnLanes[curDirection][currentLane])) { + return turnLanes[curDirection][currentLane].filter(function (el) { return el === d; }).length === 1; - } + } return false; - // return Math.random() > 0.4; - }); + }) + .property('direction', function (d) {return d;}); + - input.on('click', function() { - var key = utilGetSetValue(d3.select(this)) || undefined; - console.log(key); + input.on('click', change); + + function change() { d3.event.stopPropagation(); - }); + var direction = d3.select(this).property('direction'); + + var newDirs = []; + input.each(function (d, i) { + var value = d3.select(this).property('checked'); + if (value) { + newDirs.push(d); + } + }); + + if (direction === 'none' || newDirs.length === 0) { + newDirs = ['none']; + } else { + _.pull(newDirs, 'none'); + } + + turnLanes[curDirection][currentLane] = newDirs; + + megaChange(); + + } } function laneSelectorUI(selection) { var items; + var metadata = lanesData.metadata; + var oneway = metadata.oneway; + var len; + + if (oneway) len = metadata.count; + else len = metadata.forward + metadata.backward; + selection.selectAll('.form-label') .data([0]) .enter() @@ -232,7 +281,7 @@ export function uiFieldLanes(field, context) { .merge(list); items = list.selectAll('.lane-item') - .data(_.fill(Array(lanesData.metadata.count), 0).map(function (n, i) { + .data(_.fill(Array(len), 0).map(function (n, i) { return i; })); @@ -242,38 +291,118 @@ export function uiFieldLanes(field, context) { .attr('id', function (d) { return 'lane-' + d; }) - .classed('active', function (d) { - return d === currentLane; - }) - .append('span') - .text(function (d) { - return d + 1; - }); - - var input = selection.selectAll('.lane-item'); - - input.on('click', function () { - var lane = parseInt(d3.select(this).selectAll('span').text(), 10) - 1; - selection.select('#lane-' + currentLane).classed('active', false); - currentLane = lane; - d3.select(this).classed('active', true); - render(); - }); - items.exit().remove(); + .append('span'); + + var input = selection.selectAll('.lane-item'); + + input.selectAll('span') + .text(function (d) { + if (metadata.oneway) return d + 1; + if (d < metadata.forward) { + return (d + 1) + '▲'; + } + return (d - metadata.forward + 1) + '▼'; + }); + + input.classed('active', function (d) { + if (metadata.oneway) return d === currentLane; + if (curDirection === 'forward') { + return d === currentLane; + } + return currentLane + metadata.forward === d; + }); + input.on('click', function (d) { + if (metadata.oneway) { + currentLane = d; + curDirection = 'unspecified'; + } else { + if (d < metadata.forward) { + currentLane = d; + curDirection = 'forward'; + } else { + currentLane = d - metadata.forward; + curDirection = 'backward'; + } + } + + + render(); + }); + items.exit().remove(); } + function megaChange() { + var tag = {}; + var metadata = lanesData.metadata; + + + tag.lanes = Number(metadata.count).toString(); + + // Explanation: Removing tags on the basis of oneway tagging, + // assuming if it is a oneway, the forward and backward tags + // would be pruned, vice versa for oneway=no. + if (metadata.oneway) { + + if (metadata.reverse) tag.oneway = '-1'; + else tag.oneway = 'yes'; + + tag['lanes:forward'] = undefined; + tag['lanes:backward'] = undefined; + + tag['turn:lanes'] = formPipes(metadata.turnLanes.unspecified, metadata.count, 'none'); + tag['turn:lanes:forward'] = undefined; + tag['turn:lanes:backward'] = undefined; + } else { + tag.lanes = (metadata.forward + metadata.backward) + ''; + + tag['lanes:forward'] = metadata.forward + ''; + tag['lanes:backward'] = metadata.backward + ''; + + tag['turn:lanes'] = undefined; + tag['turn:lanes:forward'] = formPipes(metadata.turnLanes.forward, metadata.forward, 'none'); + tag['turn:lanes:backward'] = formPipes(metadata.turnLanes.backward, metadata.backward, 'none'); + } + + console.log('final tag', tag); + console.log('turn:lane ===',tag['turn:lanes']); + console.log('turn:lane;forward ==',tag['turn:lanes:forward']); + console.log('turn:lane;backward ==',tag['turn:lanes:backward']); + dispatch.call('change', this, tag); + } + + function formPipes(data, len, nullKey) { + var str = data.map(function (lane) { + return lane.join(';'); + }); + while (str.length < len) { + str.push(nullKey); + } + str = str.join('|'); + return str === '' ? undefined: str; + } + + function pruneStr(str, s) { + + } } lanes.entity = function(_) { - if (!wayID || wayID !== _.id) { - wayID = _.id; + if (!wayID) { + if (wayID !== _.id) { + currentLane = 0; + wayID = _.id; + } + if (_.isOneWay()) { + curDirection = 'unspecified'; + } else { + curDirection = 'forward'; + } } }; lanes.tags = function(tags) { - console.log(tags, field.key, 'lolololoxsxs'); }; lanes.focus = function() {}; lanes.off = function() {}; From a539956d6e567ac2e564e8932962b6c1791211fb Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Tue, 31 Jan 2017 14:22:41 +0530 Subject: [PATCH 06/25] add some turn lane icons --- .vscode/settings.json | 3 + css/app.css | 8 ++ modules/geo/geo.js | 22 +++++ modules/geo/index.js | 1 + modules/modes/select.js | 22 ++++- modules/osm/lanes.js | 2 +- modules/svg/lanes.js | 81 +++++++++++++++++++ modules/ui/fields/lanes.js | 3 - modules/ui/index.js | 2 + modules/ui/lane_visualizer.js | 146 ++++++++++++++++++++++++++++++++++ modules/ui/map_in_map.js | 31 +++++--- modules/ui/radial_menu.js | 3 +- svg/iD-sprite.src.svg | 79 ++++++++++++++++++ 13 files changed, 385 insertions(+), 18 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 modules/svg/lanes.js create mode 100644 modules/ui/lane_visualizer.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..0c4a6693fab --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "vsicons.presets.angular": false +} \ No newline at end of file diff --git a/css/app.css b/css/app.css index f38d52727b4..2f9456b59e0 100644 --- a/css/app.css +++ b/css/app.css @@ -1326,6 +1326,14 @@ button.save.has-count .count::before { background: #E8EBFF; } +/*lane-visualizer*/ + +.lane-visualizer-background { + fill: #000; + fill-opacity: 0.5; +} + + /* preset form multicombo */ .form-field-multicombo { diff --git a/modules/geo/geo.js b/modules/geo/geo.js index 096c0a18e0d..7ccf0b6f5cd 100644 --- a/modules/geo/geo.js +++ b/modules/geo/geo.js @@ -103,6 +103,10 @@ export function geoAngle(a, b, projection) { return Math.atan2(b[1] - a[1], b[0] - a[0]); } +function geoAngle2(a, b) { + return (Math.atan2(b[1] - a[1], b[0] - a[0]) * 180)/Math.PI; +} + // Rotate all points counterclockwise around a pivot point by given angle export function geoRotate(points, angle, around) { @@ -274,3 +278,21 @@ export function geoPathLength(path) { } return length; } + +export function geoPointOnLine(point, nodes, projection) { + var dist = geoEuclideanDistance, + points = nodes.map(function(n) { return projection(n.loc); }), + min = Infinity, + idx, loc; + + for (var i = 0; i < points.length - 1; i++) { + console.log(point, points[i+1], points[i]); + var cross = geoAngle2(point, points[i], projection) - geoAngle2(point, points[i+1], projection); + var segmentLen = dist(points[i], points[i+1]); + var splitLen = dist(points[i], point) + dist(points[i+1], point); + console.log(cross); + if (cross.toFixed(5) === 0 && segmentLen.toFixed(5) === splitLen.toFixed(5)) { + return nodes[i]; + } + } +} \ No newline at end of file diff --git a/modules/geo/index.js b/modules/geo/index.js index 5e37b16fe04..82fe277a773 100644 --- a/modules/geo/index.js +++ b/modules/geo/index.js @@ -21,3 +21,4 @@ export { geoPointInPolygon } from './geo.js'; export { geoPolygonContainsPolygon } from './geo.js'; export { geoPolygonIntersectsPolygon } from './geo.js'; export { geoSphericalDistance } from './geo.js'; +export { geoPointOnLine } from './geo.js'; \ No newline at end of file diff --git a/modules/modes/select.js b/modules/modes/select.js index 27032262e84..29427f731f9 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -28,7 +28,7 @@ import { import { modeBrowse } from './browse'; import { modeDragNode } from './drag_node'; import * as Operations from '../operations/index'; -import { uiRadialMenu, uiSelectionList } from '../ui/index'; +import { uiRadialMenu, uiLaneVisualizer, uiSelectionList } from '../ui/index'; import { uiCmd } from '../ui/cmd'; import { utilEntityOrMemberSelector, utilEntitySelector } from '../util/index'; @@ -54,6 +54,7 @@ export function modeSelect(context, selectedIDs) { modeDragNode(context).selectedIDs(selectedIDs).behavior ], inspector, + laneVisualizer, radialMenu, newFeature = false, suppressMenu = false, @@ -142,13 +143,22 @@ export function modeSelect(context, selectedIDs) { if (radialMenu) { context.surface().call(radialMenu.close); } + if (laneVisualizer) { + context.surface().call(laneVisualizer.close); + } } function positionMenu() { + var entity = singular(); + + if (entity && laneVisualizer && entity.type === 'way') { + laneVisualizer.center(context.mouse()); + laneVisualizer.wayID(entity.id); + } + if (suppressMenu || !radialMenu) { return; } - var entity = singular(); if (entity && context.geometry(entity.id) === 'relation') { suppressMenu = true; } else if (entity && entity.type === 'node') { @@ -158,10 +168,12 @@ export function modeSelect(context, selectedIDs) { viewport = geoExtent(context.projection.clipExtent()).polygon(); if (geoPointInPolygon(point, viewport)) { radialMenu.center(point); + } else { suppressMenu = true; } } + console.log(entity); } @@ -170,6 +182,9 @@ export function modeSelect(context, selectedIDs) { if (!suppressMenu && radialMenu) { context.surface().call(radialMenu); } + if (laneVisualizer) { + context.surface().call(laneVisualizer); + } } @@ -427,6 +442,8 @@ export function modeSelect(context, selectedIDs) { radialMenu = uiRadialMenu(context, operations); + laneVisualizer = uiLaneVisualizer(context); + context.ui().sidebar .select(singular() ? singular().id : null, newFeature); @@ -486,6 +503,7 @@ export function modeSelect(context, selectedIDs) { keybinding.off(); closeMenu(); radialMenu = undefined; + laneVisualizer = undefined; context.history() .on('undone.select', null) diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 8f2c72644b4..0378286e81b 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -174,7 +174,7 @@ function parseLaneDirections(tags, isOneWay, laneCount) { function parseTurnLanes(tag){ if (!tag) return []; - + // TODO: need to add reverse_left and reverse_right var validValues = [ 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js new file mode 100644 index 00000000000..94d8ddb30b8 --- /dev/null +++ b/modules/svg/lanes.js @@ -0,0 +1,81 @@ +import { geoAngle } from '../geo/index'; + + +export function svgTurns(projection) { + + return function drawTurns(selection, graph, turns) { + + function key(turn) { + return [turn.from.node + turn.via.node + turn.to.node].join('-'); + } + + function icon(turn) { + var u = turn.u ? '-u' : ''; + if (!turn.restriction) + return '#turn-yes' + u; + var restriction = graph.entity(turn.restriction).tags.restriction; + return '#turn-' + + (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u; + } + + var groups = selection.selectAll('.layer-hit').selectAll('g.turn') + .data(turns, key); + + groups.exit() + .remove(); + + + var enter = groups.enter() + .append('g') + .attr('class', 'turn'); + + var nEnter = enter + .filter(function (turn) { return !turn.u; }); + + nEnter.append('rect') + .attr('transform', 'translate(-22, -12)') + .attr('width', '44') + .attr('height', '24'); + + nEnter.append('use') + .attr('transform', 'translate(-22, -12)') + .attr('width', '44') + .attr('height', '24'); + + + var uEnter = enter + .filter(function (turn) { return turn.u; }); + + uEnter.append('circle') + .attr('r', '16'); + + uEnter.append('use') + .attr('transform', 'translate(-16, -16)') + .attr('width', '32') + .attr('height', '32'); + + + groups = groups + .merge(enter); + + groups + .attr('transform', function (turn) { + var v = graph.entity(turn.via.node), + t = graph.entity(turn.to.node), + a = geoAngle(v, t, projection), + p = projection(v.loc), + r = turn.u ? 0 : 60; + + return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' + + 'rotate(' + a * 180 / Math.PI + ')'; + }); + + groups.select('use') + .attr('xlink:href', icon); + + groups.select('rect'); + groups.select('circle'); + + return this; + }; +} diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index e84e7c7606a..3ed70f88b43 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -16,7 +16,6 @@ var validLanes = [ export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), LANE_WIDTH = 40, - LANE_HEIGHT = 200, // TODO: currentLane if big like 6 goes crazy if other wayID has less than 6 lanes currentLane = 0, curDirection = 'unspecified', @@ -24,9 +23,7 @@ export function uiFieldLanes(field, context) { lanesData; function lanes(selection) { - console.log('hollaa mee', currentLane, curDirection) lanesData = context.entity(wayID).lanes(); - var lanesArray = lanesData.lanesArray; window.lanesData = lanesData; window.currentLane = currentLane; window.curDirection = curDirection; diff --git a/modules/ui/index.js b/modules/ui/index.js index 37bfc22cc9b..050dcd0e5a5 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -47,3 +47,5 @@ export { uiTooltipHtml } from './tooltipHtml'; export { uiUndoRedo } from './undo_redo'; export { uiViewOnOSM } from './view_on_osm'; export { uiZoom } from './zoom'; + +export { uiLaneVisualizer } from './lane_visualizer'; \ No newline at end of file diff --git a/modules/ui/lane_visualizer.js b/modules/ui/lane_visualizer.js new file mode 100644 index 00000000000..2e02dc6cd2c --- /dev/null +++ b/modules/ui/lane_visualizer.js @@ -0,0 +1,146 @@ +import * as d3 from 'd3'; +import _ from 'lodash'; +import { geoAngle, geoChooseEdge, geoPointOnLine } from '../geo/index'; + +function createSVGLink(directions) { + var dir = directions.sort(function(a, b) { + return a.charCodeAt(0) - b.charCodeAt(0); + }); + dir = dir.join('-'); + if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; + return dir; +} +export function uiLaneVisualizer(context) { + var menu, + wrapper, + wayId, + center = [0, 0], + tooltip, + metadata; + + var laneVisualizer = function (selection) { + if (!wayId) return; + metadata = context.entity(wayId).lanes().metadata; + if (!metadata) return; + + const iconWidth = 40; + const count = metadata.count; + + function render() { + if (context.hasEntity(wayId)) { + laneVisualizer.close(); + laneVisualizer(selection); + } + } + + var projection = context.projection; + var graph = context.graph(); + var way = graph.entity(wayId); + + var nodes = _.uniq(graph.childNodes(way)); + var choice = geoChooseEdge(nodes, context.mouse(), context.projection); + var prev = nodes[choice.index - 1]; + var next = nodes[choice.index]; + var angle = (geoAngle(prev, next, projection) * 180 / Math.PI) + 180; + + context.history() + .on('change.lanes', render); + + selection.node().parentNode.focus(); + + function click() { + d3.event.stopPropagation(); + laneVisualizer.close(); + } + + + wrapper = selection + .append('g') + .attr('class', 'lane-visualizer') + .attr('transform', 'translate('+ center+ ') rotate('+ angle + ')') + .attr('opacity', 0); + + wrapper + .transition() + .attr('opacity', 1); + + var menu = wrapper + .append('g') + .attr('transform', 'translate(0, ' +(-1*count*iconWidth)/2 + ')'); + + + + var rect = menu + .append('rect') + .attr('class', 'lane-visualizer-background') + .attr('width', iconWidth) + .attr('height', count*iconWidth); + + var button = menu.selectAll() + .data(_.fill(Array(metadata.count), 0).map(function (n, i) { + return i; + })) + .enter() + .append('g') + .attr('class', 'radial-menu-item radial-menu-item-move') + .attr('transform', function(d, i) { + return 'translate(' + [ iconWidth/2, (iconWidth/2 + i*iconWidth) ]+ ') rotate(-90)'; + }); + + button + .append('circle') + .attr('r', 15); + + button + .append('use') + .attr('transform', 'translate(-15,-12)') + .attr('width', '20') + .attr('height', '20') + .attr('xlink:href', function (d, i) { + return '#lane-' + createSVGLink(metadata.turnLanes.unspecified[i]); + }); + + // menu + // .append('rect') + // .attr('width', 20) + // .attr('height', 30) + // .attr('stroke-width', 50) + // .attr('stroke-linecap', 'round'); + + }; + + laneVisualizer.center = function (_) { + if (!arguments.length) return center; + center = _; + console.log(center); + return laneVisualizer; + }; + + + laneVisualizer.wayID = function (_) { + if (!arguments.length) return _; + wayId = _; + return laneVisualizer; + }; + + laneVisualizer.close = function() { + console.log('closing') + if (wrapper) { + + wrapper + .style('pointer-events', 'none') + .transition() + .attr('opacity', 0) + .remove(); + + context.history() + .on('change.lanes', null); + } + + + if (tooltip) { + tooltip.remove(); + } + }; + return laneVisualizer; +} diff --git a/modules/ui/map_in_map.js b/modules/ui/map_in_map.js index f4e903e48bf..5b19007da6d 100644 --- a/modules/ui/map_in_map.js +++ b/modules/ui/map_in_map.js @@ -237,27 +237,36 @@ export function uiMapInMap(context) { // redraw viewport bounding box if (gesture !== 'pan') { - var getPath = d3.geoPath(projection), - bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] }; + // var getPath = d3.geoPath(projection), + var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] }; viewport = wrap.selectAll('.map-in-map-viewport') .data([0]); viewport = viewport.enter() - .append('svg') - .attr('class', 'map-in-map-viewport') + .append('canvas') + // .attr('class', 'map-in-map-viewport') + .attr('height', 300) + .attr('width', 300) .merge(viewport); - var path = viewport.selectAll('.map-in-map-bbox') + var path2 = viewport.selectAll('.map-in-map-bbox') .data([bbox]); - path.enter() - .append('path') - .attr('class', 'map-in-map-bbox') - .merge(path) - .attr('d', getPath) - .classed('thick', function(d) { return getPath.area(d) < 30; }); + var ctx = d3.select('canvas').node().getContext('2d'); + var path = d3.geoPath() + .projection(projection) + .context(ctx); + ctx.beginPath(); + path(bbox); + ctx.stroke(); + // path.enter() + // .append('path') + // .attr('class', 'map-in-map-bbox') + // .merge(path) + // .attr('d', getPath) + // .classed('thick', function(d) { return getPath.area(d) < 30; }); } } diff --git a/modules/ui/radial_menu.js b/modules/ui/radial_menu.js index 8c62abb5b25..0f545f7a2db 100644 --- a/modules/ui/radial_menu.js +++ b/modules/ui/radial_menu.js @@ -8,8 +8,8 @@ export function uiRadialMenu(context, operations) { center = [0, 0], tooltip; - var radialMenu = function(selection) { + if (operations) return; if (!operations.length) return; selection.node().parentNode.focus(); @@ -47,6 +47,7 @@ export function uiRadialMenu(context, operations) { .attr('stroke-width', 50) .attr('stroke-linecap', 'round'); + console.log(operations); var button = menu.selectAll() .data(operations) .enter() diff --git a/svg/iD-sprite.src.svg b/svg/iD-sprite.src.svg index dbed29ff0ee..859baae60fc 100644 --- a/svg/iD-sprite.src.svg +++ b/svg/iD-sprite.src.svg @@ -187,6 +187,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c3e1fae24fa6ea67ada36f5ccfecf0c19c97a28c Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Tue, 31 Jan 2017 19:53:53 +0530 Subject: [PATCH 07/25] add colors --- modules/osm/lanes.js | 59 +++++++++++++++-- modules/svg/midpoints.js | 2 +- modules/ui/fields/lanes.js | 6 +- modules/ui/lane_visualizer.js | 118 +++++++++++++++++++--------------- 4 files changed, 121 insertions(+), 64 deletions(-) diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 0378286e81b..7435174e5d1 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -14,7 +14,14 @@ export function osmLanes(entity) { var forward = laneDirections.forward; var backward = laneDirections.backward; var bothways = laneDirections.bothways; + + // sometimes just forward and backward are available + laneCount = laneDirections.laneCount; + // TODO: if you change the forward backward and click on trash at top, + // laneCount and forward/backward goes out of sync + // TODO: sometimes people just do turn:lanes:backward=|||| and dont mention + // any kind of count. need to handle it // parse the piped string 'x|y|z' format var turnLanes = {}; turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']); @@ -74,8 +81,7 @@ export function osmLanes(entity) { // TODO: need to make sure forward lanes is consistent across all tags, // eg if psv:lanes:forward is 3 and lanes:forward is 2, changes lanes:forward =3, - return { - metadata: { + var metadata = { count: laneCount, oneway: isOneWay, forward: forward, @@ -92,9 +98,12 @@ export function osmLanes(entity) { bicyclewayLanes: bicyclewayLanes, leftHandDrive: false, reverse: parseInt(tags.oneway, 10) === -1 - }, + }; + return { + metadata: metadata, + accessSeq: makeAccessSeq(metadata, true), lanes: lanesObj - }; + }; } @@ -137,7 +146,7 @@ function parseLaneDirections(tags, isOneWay, laneCount) { var forward = parseInt(tags['lanes:forward'], 10); var backward = parseInt(tags['lanes:backward'], 10); var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0; - + var count = laneCount; if (parseInt(tags.oneway, 10) === -1) { forward = 0; bothways = 0; @@ -164,10 +173,12 @@ function parseLaneDirections(tags, isOneWay, laneCount) { } backward = laneCount - bothways - forward; } + return { forward: forward, backward: backward, - bothways: bothways + bothways: bothways, + laneCount: forward + backward+ bothways }; } @@ -274,4 +285,38 @@ function lanesArray(lanesData) { }); return obj; -} \ No newline at end of file +} + +function makeAccessSeq(metadata, leftHand) { + if (metadata.oneway) { + return _.fill(Array(metadata.count), 0).map(function (n, i) { + return { + dir: 'unspecified', + index: i + }; + }); + } + + var forward = metadata.forward; + var backward = metadata.backward; + + var forSeq = _.fill(Array(forward), 0).map(function (n, i) { + return { + dir: 'forward', + index: i + }; + }); + var backSeq = _.fill(Array(backward), 0).map(function (n, i) { + return { + dir: 'backward', + index: backward - i - 1 + }; + }); + + if (leftHand) { + return [].concat(forSeq, backSeq); + } + return [].concat(backSeq, forSeq); +} + +window.makeAccessSeq = makeAccessSeq; \ No newline at end of file diff --git a/modules/svg/midpoints.js b/modules/svg/midpoints.js index a64b922254a..31e00bcd4fc 100644 --- a/modules/svg/midpoints.js +++ b/modules/svg/midpoints.js @@ -27,7 +27,7 @@ export function svgMidpoints(projection, context) { continue; if (context.selectedIDs().indexOf(entity.id) < 0) continue; - + console.log(context.selectedIDs().indexOf(entity.id)); var nodes = graph.childNodes(entity); for (var j = 0; j < nodes.length - 1; j++) { diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 3ed70f88b43..3ef35c75451 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -85,7 +85,7 @@ export function uiFieldLanes(field, context) { var keysConsidered = ['count', 'forward', 'backward', 'reverse']; var metadata = lanesData.metadata; - + window.metadata = metadata; wrap = wrap.enter() .append('div') .attr('class', 'preset-input-wrap') @@ -250,8 +250,8 @@ export function uiFieldLanes(field, context) { var items; var metadata = lanesData.metadata; var oneway = metadata.oneway; - var len; - + var len = metadata.count; + // TODO: clean up this mess of vvvvv if (oneway) len = metadata.count; else len = metadata.forward + metadata.backward; diff --git a/modules/ui/lane_visualizer.js b/modules/ui/lane_visualizer.js index 2e02dc6cd2c..a8683fdcf0a 100644 --- a/modules/ui/lane_visualizer.js +++ b/modules/ui/lane_visualizer.js @@ -2,14 +2,6 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { geoAngle, geoChooseEdge, geoPointOnLine } from '../geo/index'; -function createSVGLink(directions) { - var dir = directions.sort(function(a, b) { - return a.charCodeAt(0) - b.charCodeAt(0); - }); - dir = dir.join('-'); - if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; - return dir; -} export function uiLaneVisualizer(context) { var menu, wrapper, @@ -17,22 +9,19 @@ export function uiLaneVisualizer(context) { center = [0, 0], tooltip, metadata; - + var laneVisualizer = function (selection) { + var leftHand = false; + if (!wayId) return; - metadata = context.entity(wayId).lanes().metadata; + metadata = context.entity(wayId).lanes().metadata; if (!metadata) return; - + + var seq = context.entity(wayId).lanes().accessSeq; + const iconWidth = 40; const count = metadata.count; - function render() { - if (context.hasEntity(wayId)) { - laneVisualizer.close(); - laneVisualizer(selection); - } - } - var projection = context.projection; var graph = context.graph(); var way = graph.entity(wayId); @@ -41,8 +30,8 @@ export function uiLaneVisualizer(context) { var choice = geoChooseEdge(nodes, context.mouse(), context.projection); var prev = nodes[choice.index - 1]; var next = nodes[choice.index]; - var angle = (geoAngle(prev, next, projection) * 180 / Math.PI) + 180; - + var angle = (geoAngle(prev, next, projection) * 180 / Math.PI); + context.history() .on('change.lanes', render); @@ -53,11 +42,11 @@ export function uiLaneVisualizer(context) { laneVisualizer.close(); } - wrapper = selection .append('g') .attr('class', 'lane-visualizer') - .attr('transform', 'translate('+ center+ ') rotate('+ angle + ')') + // .attr('transform', 'translate('+ center + ')') + .attr('transform', 'translate(' + center + ') rotate(' + (angle+ 90) + ')') .attr('opacity', 0); wrapper @@ -66,29 +55,39 @@ export function uiLaneVisualizer(context) { var menu = wrapper .append('g') - .attr('transform', 'translate(0, ' +(-1*count*iconWidth)/2 + ')'); + .attr('transform', 'translate(' + count*iconWidth/(-2) + ', 0)'); - - - var rect = menu + menu .append('rect') .attr('class', 'lane-visualizer-background') - .attr('width', iconWidth) - .attr('height', count*iconWidth); + .attr('width', count* iconWidth) + .attr('height', iconWidth); var button = menu.selectAll() - .data(_.fill(Array(metadata.count), 0).map(function (n, i) { - return i; - })) - .enter() - .append('g') - .attr('class', 'radial-menu-item radial-menu-item-move') - .attr('transform', function(d, i) { - return 'translate(' + [ iconWidth/2, (iconWidth/2 + i*iconWidth) ]+ ') rotate(-90)'; - }); + .data(seq) + .enter() + .append('g') + .attr('class', 'radial-menu-item radial-menu-item-move') + .attr('transform', function (d, i) { + var reverse = 0; + if (d.dir === 'backward') { + reverse = 180; + } + return 'translate(' + [iconWidth / 2 + i * iconWidth, (iconWidth / 2)] + ') rotate(' + reverse + ')'; + }); button .append('circle') + .style('fill', function(d) { + switch (d.dir) { + case 'forward': + return '#dfffdf'; + case 'backward': + return '#ffd8d8'; + default: + return ''; + } + }) .attr('r', 15); button @@ -96,23 +95,37 @@ export function uiLaneVisualizer(context) { .attr('transform', 'translate(-15,-12)') .attr('width', '20') .attr('height', '20') - .attr('xlink:href', function (d, i) { - return '#lane-' + createSVGLink(metadata.turnLanes.unspecified[i]); + .attr('xlink:href', function (d) { + return '#lane-' + createSVGLink(d); }); - - // menu - // .append('rect') - // .attr('width', 20) - // .attr('height', 30) - // .attr('stroke-width', 50) - // .attr('stroke-linecap', 'round'); + - }; + function render() { + if (context.hasEntity(wayId)) { + laneVisualizer.close(); + laneVisualizer(selection); + } + } + }; + function createSVGLink(d) { + var directions; + console.log(d.dir); + directions = metadata.turnLanes[d.dir][d.index]; + + // TODO: fix this vv + if (!directions) return ''; + var dir = directions.sort(function (a, b) { + return a.charCodeAt(0) - b.charCodeAt(0); + }); + dir = dir.join('-'); + if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; + + return dir; + } laneVisualizer.center = function (_) { if (!arguments.length) return center; center = _; - console.log(center); return laneVisualizer; }; @@ -123,10 +136,9 @@ export function uiLaneVisualizer(context) { return laneVisualizer; }; - laneVisualizer.close = function() { - console.log('closing') + laneVisualizer.close = function () { if (wrapper) { - + wrapper .style('pointer-events', 'none') .transition() @@ -134,9 +146,9 @@ export function uiLaneVisualizer(context) { .remove(); context.history() - .on('change.lanes', null); + .on('change.lanes', null); } - + if (tooltip) { tooltip.remove(); From cd0308bdbdda34b3c27987bc55e3da863b194310 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Wed, 1 Feb 2017 13:51:11 +0530 Subject: [PATCH 08/25] Add left hand drive check --- modules/osm/lanes.js | 6 ++---- modules/ui/fields/lanes.js | 9 +++++---- modules/ui/lane_visualizer.js | 21 +++++++++++++++------ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 7435174e5d1..cca156d5aa4 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -96,12 +96,10 @@ export function osmLanes(entity) { hovLanes: hovLanes, hgvLanes: hgvLanes, bicyclewayLanes: bicyclewayLanes, - leftHandDrive: false, reverse: parseInt(tags.oneway, 10) === -1 }; return { metadata: metadata, - accessSeq: makeAccessSeq(metadata, true), lanes: lanesObj }; } @@ -287,7 +285,7 @@ function lanesArray(lanesData) { return obj; } -function makeAccessSeq(metadata, leftHand) { +export function getLayoutSeq(metadata, leftHand) { if (metadata.oneway) { return _.fill(Array(metadata.count), 0).map(function (n, i) { return { @@ -319,4 +317,4 @@ function makeAccessSeq(metadata, leftHand) { return [].concat(backSeq, forSeq); } -window.makeAccessSeq = makeAccessSeq; \ No newline at end of file +window.getLayoutSeq = getLayoutSeq; \ No newline at end of file diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 3ef35c75451..1a763054118 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -3,10 +3,8 @@ import { utilRebind } from '../../util/rebind'; import { utilGetDimensions } from '../../util/dimensions'; import { uiFieldCheck } from './check'; import _ from 'lodash'; -import { d3combobox } from '../../lib/d3.combobox.js'; import { utilGetSetValue } from '../../util/get_set_value'; - var validLanes = [ 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' @@ -20,19 +18,22 @@ export function uiFieldLanes(field, context) { currentLane = 0, curDirection = 'unspecified', wayID, - lanesData; + lanesData, + driveLeft; + function lanes(selection) { lanesData = context.entity(wayID).lanes(); window.lanesData = lanesData; window.currentLane = currentLane; window.curDirection = curDirection; - + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { selection.call(lanes.off); return; } + var lanesInfo = selection.selectAll('.lanes-info').data([0]); lanesInfo = lanesInfo.enter() .append('div') diff --git a/modules/ui/lane_visualizer.js b/modules/ui/lane_visualizer.js index a8683fdcf0a..b86c7ac0279 100644 --- a/modules/ui/lane_visualizer.js +++ b/modules/ui/lane_visualizer.js @@ -1,6 +1,9 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { geoAngle, geoChooseEdge, geoPointOnLine } from '../geo/index'; +import { getLayoutSeq } from '../osm/lanes'; +import { dataDriveLeft } from '../../data'; +import { geoPointInPolygon } from '../geo'; export function uiLaneVisualizer(context) { var menu, @@ -8,17 +11,14 @@ export function uiLaneVisualizer(context) { wayId, center = [0, 0], tooltip, - metadata; + metadata, + driveLeft; var laneVisualizer = function (selection) { - var leftHand = false; - if (!wayId) return; metadata = context.entity(wayId).lanes().metadata; if (!metadata) return; - var seq = context.entity(wayId).lanes().accessSeq; - const iconWidth = 40; const count = metadata.count; @@ -26,6 +26,15 @@ export function uiLaneVisualizer(context) { var graph = context.graph(); var way = graph.entity(wayId); + var loc = way.extent(context.graph()).center(); + driveLeft = _.some(dataDriveLeft.features, function(f) { + return _.some(f.geometry.coordinates, function(d) { + return geoPointInPolygon(loc, d); + }); + }); + + var layout = getLayoutSeq(metadata, driveLeft); + var nodes = _.uniq(graph.childNodes(way)); var choice = geoChooseEdge(nodes, context.mouse(), context.projection); var prev = nodes[choice.index - 1]; @@ -64,7 +73,7 @@ export function uiLaneVisualizer(context) { .attr('height', iconWidth); var button = menu.selectAll() - .data(seq) + .data(layout) .enter() .append('g') .attr('class', 'radial-menu-item radial-menu-item-move') From 14c583bb1c8eb7fc2504b3c87d44625d026746fe Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Wed, 1 Feb 2017 23:05:24 +0530 Subject: [PATCH 09/25] basic svg Lanes --- modules/geo/geo.js | 28 +----- modules/geo/index.js | 2 +- modules/osm/lanes.js | 6 +- modules/renderer/map.js | 6 +- modules/svg/index.js | 1 + modules/svg/lanes.js | 181 ++++++++++++++++++++++------------ modules/svg/midpoints.js | 4 +- modules/ui/lane_visualizer.js | 3 +- 8 files changed, 138 insertions(+), 93 deletions(-) diff --git a/modules/geo/geo.js b/modules/geo/geo.js index 7ccf0b6f5cd..f0c78dda0ec 100644 --- a/modules/geo/geo.js +++ b/modules/geo/geo.js @@ -11,6 +11,11 @@ export function geoInterp(p1, p2, t) { p1[1] + (p2[1] - p1[1]) * t]; } +// returns a point which divides the line joining p1, p2 +// in the ratio m:n +export function geoDivideSegment(p1, p2, m, n) { + return [(p2[0]*n + p2[0]*m)/(m+n), (p2[1]*n + p2[1]*m)/(m+n)]; +} // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product. // Returns a positive value, if OAB makes a counter-clockwise turn, @@ -103,11 +108,6 @@ export function geoAngle(a, b, projection) { return Math.atan2(b[1] - a[1], b[0] - a[0]); } -function geoAngle2(a, b) { - return (Math.atan2(b[1] - a[1], b[0] - a[0]) * 180)/Math.PI; -} - - // Rotate all points counterclockwise around a pivot point by given angle export function geoRotate(points, angle, around) { return points.map(function(point) { @@ -278,21 +278,3 @@ export function geoPathLength(path) { } return length; } - -export function geoPointOnLine(point, nodes, projection) { - var dist = geoEuclideanDistance, - points = nodes.map(function(n) { return projection(n.loc); }), - min = Infinity, - idx, loc; - - for (var i = 0; i < points.length - 1; i++) { - console.log(point, points[i+1], points[i]); - var cross = geoAngle2(point, points[i], projection) - geoAngle2(point, points[i+1], projection); - var segmentLen = dist(points[i], points[i+1]); - var splitLen = dist(points[i], point) + dist(points[i+1], point); - console.log(cross); - if (cross.toFixed(5) === 0 && segmentLen.toFixed(5) === splitLen.toFixed(5)) { - return nodes[i]; - } - } -} \ No newline at end of file diff --git a/modules/geo/index.js b/modules/geo/index.js index 82fe277a773..8e060f7f787 100644 --- a/modules/geo/index.js +++ b/modules/geo/index.js @@ -21,4 +21,4 @@ export { geoPointInPolygon } from './geo.js'; export { geoPolygonContainsPolygon } from './geo.js'; export { geoPolygonIntersectsPolygon } from './geo.js'; export { geoSphericalDistance } from './geo.js'; -export { geoPointOnLine } from './geo.js'; \ No newline at end of file +export { geoDivideSegment } from './geo.js'; \ No newline at end of file diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index cca156d5aa4..c6e92a21f9e 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -285,11 +285,13 @@ function lanesArray(lanesData) { return obj; } -export function getLayoutSeq(metadata, leftHand) { +export function getLayoutSeq(metadata, leftHand, kind) { + if (!metadata) return []; if (metadata.oneway) { return _.fill(Array(metadata.count), 0).map(function (n, i) { return { dir: 'unspecified', + lanes: metadata[kind].unspecified, index: i }; }); @@ -301,12 +303,14 @@ export function getLayoutSeq(metadata, leftHand) { var forSeq = _.fill(Array(forward), 0).map(function (n, i) { return { dir: 'forward', + lanes: metadata[kind].forward, index: i }; }); var backSeq = _.fill(Array(backward), 0).map(function (n, i) { return { dir: 'backward', + lanes: metadata[kind].backward, index: backward - i - 1 }; }); diff --git a/modules/renderer/map.js b/modules/renderer/map.js index ec10c9201a0..baf0fed15fe 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -10,6 +10,7 @@ import { svgAreas, svgLabels, svgLayers, + svgLanes, svgLines, svgMidpoints, svgPoints, @@ -44,6 +45,7 @@ export function rendererMap(context) { drawLines = svgLines(projection), drawAreas = svgAreas(projection, context), drawMidpoints = svgMidpoints(projection, context), + drawLanes = svgLanes(projection, context), drawLabels = svgLabels(projection, context), supersurface = d3.select(null), wrapper = d3.select(null), @@ -159,7 +161,8 @@ export function rendererMap(context) { all = context.features().filter(all, graph); surface.selectAll('.data-layer-osm') .call(drawVertices, graph, all, filter, map.extent(), map.zoom()) - .call(drawMidpoints, graph, all, filter, map.trimmedExtent()); + .call(drawMidpoints, graph, all, filter, map.trimmedExtent()) + .call(drawLanes, graph, all, filter, map.trimmedExtent(), map.center()); dispatch.call('drawn', this, {full: false}); } }); @@ -252,6 +255,7 @@ export function rendererMap(context) { .call(drawLines, graph, data, filter) .call(drawAreas, graph, data, filter) .call(drawMidpoints, graph, data, filter, map.trimmedExtent()) + .call(drawLanes, graph, data, filter, map.trimmedExtent(), map.center()) .call(drawLabels, graph, data, filter, dimensions, !difference && !extent) .call(drawPoints, graph, data, filter); diff --git a/modules/svg/index.js b/modules/svg/index.js index 9b48ba93d39..0120b5c1461 100644 --- a/modules/svg/index.js +++ b/modules/svg/index.js @@ -5,6 +5,7 @@ export { svgGpx } from './gpx.js'; export { svgIcon } from './icon.js'; export { svgLabels } from './labels.js'; export { svgLayers } from './layers.js'; +export { svgLanes } from './lanes.js'; export { svgLines } from './lines.js'; export { svgMapillaryImages } from './mapillary_images.js'; export { svgMapillarySigns } from './mapillary_signs.js'; diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index 94d8ddb30b8..e64410b9ab5 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -1,81 +1,134 @@ -import { geoAngle } from '../geo/index'; - - -export function svgTurns(projection) { - - return function drawTurns(selection, graph, turns) { - - function key(turn) { - return [turn.from.node + turn.via.node + turn.to.node].join('-'); +import _ from 'lodash'; +import { + svgPointTransform, + svgTagClasses +} from './index'; + +import { + geoAngle, + geoEuclideanDistance, + geoInterp, + geoLineIntersection +} from '../geo/index'; +import { getLayoutSeq } from '../osm/lanes'; +import { dataDriveLeft } from '../../data'; +import { geoPointInPolygon } from '../geo'; + + +export function svgLanes(projection, context) { + + return function drawMidpoints(selection, graph, entities, filter, extent, mapCenter) { + // return ; + var entity = getEntity(); + var showLanes = []; + var metadata; + var iconPosition; + var driveLeft; + var layoutSeq; + + if (entity) { + metadata = entity.lanes().metadata; + iconPosition = findPosition(); + driveLeft = isDriveLeft(); + layoutSeq = getLayoutSeq(metadata, driveLeft, 'turnLanes'); + layer = selection.selectAll('.layer-hit'); + showLanes = [0]; } + var layer = selection.selectAll('.layer-hit'); - function icon(turn) { - var u = turn.u ? '-u' : ''; - if (!turn.restriction) - return '#turn-yes' + u; - var restriction = graph.entity(turn.restriction).tags.restriction; - return '#turn-' + - (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u; - } - - var groups = selection.selectAll('.layer-hit').selectAll('g.turn') - .data(turns, key); + var groups = layer + .selectAll('g.midpoint') + .data(showLanes); groups.exit() .remove(); - var enter = groups.enter() - .append('g') - .attr('class', 'turn'); - - var nEnter = enter - .filter(function (turn) { return !turn.u; }); - - nEnter.append('rect') - .attr('transform', 'translate(-22, -12)') - .attr('width', '44') - .attr('height', '24'); + .insert('g', ':first-child') + .attr('class', 'midpoint'); - nEnter.append('use') - .attr('transform', 'translate(-22, -12)') - .attr('width', '44') - .attr('height', '24'); - - - var uEnter = enter - .filter(function (turn) { return turn.u; }); - - uEnter.append('circle') - .attr('r', '16'); - - uEnter.append('use') - .attr('transform', 'translate(-16, -16)') - .attr('width', '32') - .attr('height', '32'); + enter.append('polygon') + .attr('points', '-6,8 10,0 -6,-8') + .attr('class', 'shadow'); + enter.append('polygon') + .attr('points', '-3,4 5,0 -3,-4') + .attr('class', 'fill'); groups = groups - .merge(enter); - - groups - .attr('transform', function (turn) { - var v = graph.entity(turn.via.node), - t = graph.entity(turn.to.node), - a = geoAngle(v, t, projection), - p = projection(v.loc), - r = turn.u ? 0 : 60; - - return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' + - 'rotate(' + a * 180 / Math.PI + ')'; + .merge(enter) + .attr('transform', function (d) { + var translate = svgPointTransform(projection), + a = graph.entity(iconPosition.edge[0]), + b = graph.entity(iconPosition.edge[1]); + var angleVal = Math.round(geoAngle(a, b, projection) * (180 / Math.PI)); + return translate(iconPosition)+ ' rotate(' + angleVal + ')'; + }) + .call(svgTagClasses().tags( + // TODO: what if entity is null + function () { return entity && entity.tags; } + )); + + // Propagate data bindings. + groups.select('polygon.shadow'); + groups.select('polygon.fill'); + + function isDriveLeft() { + return _.some(dataDriveLeft.features, function (f) { + return _.some(f.geometry.coordinates, function (d) { + return geoPointInPolygon(mapCenter, d); + }); }); + } - groups.select('use') - .attr('xlink:href', icon); + function getEntity() { + if (context.selectedIDs().length !== 1) return null; + var entity = graph.entity(context.selectedIDs()[0]); - groups.select('rect'); - groups.select('circle'); + if (entity.type !== 'way') return null; + if (!filter(entity)) return null; + if (!entity.tags.highway) return null; + return entity; + } - return this; + function findPosition() { + var loc; + if (!entity) { + return { + loc: undefined, + edge: [] + }; + } + var nodes = graph.childNodes(entity); + var poly = extent.polygon(); + + for (var j = nodes.length - 1; j > 0; j--) { + var a = nodes[j - 1]; + var b = nodes[j]; + if (geoEuclideanDistance(projection(a.loc), projection(b.loc)) > 40) { + var point = geoInterp(a.loc, b.loc, 0.75); + if (extent.intersects(point)) { + loc = point; + } else { + for (var k = 0; k < 4; k++) { + point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]); + if (point && + geoEuclideanDistance(projection(a.loc), projection(point)) > 20 && + geoEuclideanDistance(projection(b.loc), projection(point)) > 20) { + loc = point; + break; + } + } + } + if (loc) { + return { + loc: loc, + edge: [a.id, b.id], + }; + } + } + } + } }; + } diff --git a/modules/svg/midpoints.js b/modules/svg/midpoints.js index 31e00bcd4fc..b75c672c2e6 100644 --- a/modules/svg/midpoints.js +++ b/modules/svg/midpoints.js @@ -17,7 +17,7 @@ export function svgMidpoints(projection, context) { return function drawMidpoints(selection, graph, entities, filter, extent) { var poly = extent.polygon(), midpoints = {}; - + return; for (var i = 0; i < entities.length; i++) { var entity = entities[i]; @@ -92,7 +92,7 @@ export function svgMidpoints(projection, context) { .selectAll('g.midpoint') .filter(midpointFilter) .data(_.values(midpoints), function(d) { return d.id; }); - + console.log(_.values(midpoints), function(d) { return d.id;}); groups.exit() .remove(); diff --git a/modules/ui/lane_visualizer.js b/modules/ui/lane_visualizer.js index b86c7ac0279..b55a3449b82 100644 --- a/modules/ui/lane_visualizer.js +++ b/modules/ui/lane_visualizer.js @@ -1,6 +1,6 @@ import * as d3 from 'd3'; import _ from 'lodash'; -import { geoAngle, geoChooseEdge, geoPointOnLine } from '../geo/index'; +import { geoAngle, geoChooseEdge } from '../geo/index'; import { getLayoutSeq } from '../osm/lanes'; import { dataDriveLeft } from '../../data'; import { geoPointInPolygon } from '../geo'; @@ -16,6 +16,7 @@ export function uiLaneVisualizer(context) { var laneVisualizer = function (selection) { if (!wayId) return; + return; metadata = context.entity(wayId).lanes().metadata; if (!metadata) return; From d354c80f90d604801d84d6f83d3c1a26bd568372 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 2 Feb 2017 00:42:40 +0530 Subject: [PATCH 10/25] add svg rect --- modules/svg/lanes.js | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index e64410b9ab5..35cec4279ae 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -18,26 +18,29 @@ import { geoPointInPolygon } from '../geo'; export function svgLanes(projection, context) { return function drawMidpoints(selection, graph, entities, filter, extent, mapCenter) { - // return ; + // return; var entity = getEntity(); var showLanes = []; var metadata; var iconPosition; var driveLeft; var layoutSeq; - + const iconWidth = 40; + if (entity) { metadata = entity.lanes().metadata; iconPosition = findPosition(); driveLeft = isDriveLeft(); layoutSeq = getLayoutSeq(metadata, driveLeft, 'turnLanes'); layer = selection.selectAll('.layer-hit'); - showLanes = [0]; + if (iconPosition) { + showLanes = [0]; + } } var layer = selection.selectAll('.layer-hit'); var groups = layer - .selectAll('g.midpoint') + .selectAll('g.lanes-visual') .data(showLanes); groups.exit() @@ -45,11 +48,28 @@ export function svgLanes(projection, context) { var enter = groups.enter() .insert('g', ':first-child') - .attr('class', 'midpoint'); + .attr('class', 'lanes-visual'); + + enter + .append('g') + .attr('class', 'lanes-visual-wrapper') + .append('rect') + .attr('class', 'lane-visual-background'); + + layer + .selectAll('.lanes-visual-wrapper') + .attr('transform', function() { + return 'translate(' + metadata.count*iconWidth/(-2) + ', 0)'; + }); + - enter.append('polygon') - .attr('points', '-6,8 10,0 -6,-8') - .attr('class', 'shadow'); + selection.selectAll('rect') + .attr('width', function() { + return metadata.count* iconWidth; + }) + .attr('height', function() { + return iconWidth; + }); enter.append('polygon') .attr('points', '-3,4 5,0 -3,-4') @@ -61,7 +81,7 @@ export function svgLanes(projection, context) { var translate = svgPointTransform(projection), a = graph.entity(iconPosition.edge[0]), b = graph.entity(iconPosition.edge[1]); - var angleVal = Math.round(geoAngle(a, b, projection) * (180 / Math.PI)); + var angleVal = Math.round(geoAngle(a, b, projection) * (180 / Math.PI)) + 90; return translate(iconPosition)+ ' rotate(' + angleVal + ')'; }) .call(svgTagClasses().tags( From 482658e38a4a10bcede40b6f45cb1b1d90934a1b Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 2 Feb 2017 01:46:36 +0530 Subject: [PATCH 11/25] d3 update --- modules/osm/lanes.js | 4 ++ modules/svg/lanes.js | 87 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index c6e92a21f9e..2a77477eff4 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -23,6 +23,10 @@ export function osmLanes(entity) { // TODO: sometimes people just do turn:lanes:backward=|||| and dont mention // any kind of count. need to handle it // parse the piped string 'x|y|z' format + + // TODO: if you add 8 forward turn lanes and thrn put forward count as 4 + // goes out of sync. Reducing the number of forward turn lanes doesnt reduce turn lanes + // forward var turnLanes = {}; turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']); turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']); diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index 35cec4279ae..1069651c629 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -24,9 +24,9 @@ export function svgLanes(projection, context) { var metadata; var iconPosition; var driveLeft; - var layoutSeq; + var layoutSeq = []; const iconWidth = 40; - + // TODO: on removing map features svgLanes stays there if (entity) { metadata = entity.lanes().metadata; iconPosition = findPosition(); @@ -50,26 +50,66 @@ export function svgLanes(projection, context) { .insert('g', ':first-child') .attr('class', 'lanes-visual'); - enter + var wrapper = enter .append('g') - .attr('class', 'lanes-visual-wrapper') - .append('rect') + .attr('class', 'lanes-visual-wrapper'); + + wrapper.append('rect') .attr('class', 'lane-visual-background'); + wrapper.append('g') + .attr('class', 'lane-visual-items'); + layer .selectAll('.lanes-visual-wrapper') - .attr('transform', function() { - return 'translate(' + metadata.count*iconWidth/(-2) + ', 0)'; + .attr('transform', function () { + return 'translate(' + metadata.count * iconWidth / (-2) + ', 0)'; }); - selection.selectAll('rect') - .attr('width', function() { - return metadata.count* iconWidth; - }) - .attr('height', function() { - return iconWidth; - }); + .attr('width', function () { + return metadata.count * iconWidth; + }) + .attr('height', function () { + return iconWidth; + }); + + var button = groups.selectAll('lane-visual-items') + .data(layoutSeq) + .enter() + .append('g') + .attr('class', 'lane-visual-items radial-menu-item radial-menu-item-move') + .attr('transform', function (d, i) { + var reverse = 0; + if (d.dir === 'backward') { + reverse = 180; + } + return 'translate(' + [iconWidth / 2 + i * iconWidth, (iconWidth / 2)] + ') rotate(' + reverse + ')'; + }); + + button + .append('circle') + .style('fill', function (d) { + switch (d.dir) { + case 'forward': + return '#dfffdf'; + case 'backward': + return '#ffd8d8'; + default: + return ''; + } + }) + .attr('r', 15); + + button + .append('use') + .attr('transform', 'translate(-15,-12)') + .attr('width', '20') + .attr('height', '20') + .attr('xlink:href', function (d) { + console.log(d); + return '#lane-' + createSVGLink(d); + }); enter.append('polygon') .attr('points', '-3,4 5,0 -3,-4') @@ -82,7 +122,7 @@ export function svgLanes(projection, context) { a = graph.entity(iconPosition.edge[0]), b = graph.entity(iconPosition.edge[1]); var angleVal = Math.round(geoAngle(a, b, projection) * (180 / Math.PI)) + 90; - return translate(iconPosition)+ ' rotate(' + angleVal + ')'; + return translate(iconPosition) + ' rotate(' + angleVal + ')'; }) .call(svgTagClasses().tags( // TODO: what if entity is null @@ -121,7 +161,7 @@ export function svgLanes(projection, context) { } var nodes = graph.childNodes(entity); var poly = extent.polygon(); - + for (var j = nodes.length - 1; j > 0; j--) { var a = nodes[j - 1]; var b = nodes[j]; @@ -149,6 +189,21 @@ export function svgLanes(projection, context) { } } } + function createSVGLink(d) { + var directions; + console.log(d.dir); + directions = metadata.turnLanes[d.dir][d.index]; + + // TODO: fix this vv + if (!directions) return ''; + var dir = directions.sort(function (a, b) { + return a.charCodeAt(0) - b.charCodeAt(0); + }); + dir = dir.join('-'); + if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; + + return dir; + } }; } From 99c50676909328be06a800784638a0b2c935b930 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 1 Feb 2017 18:03:09 -0500 Subject: [PATCH 12/25] Cleanup, simplify, and add some d3 commentary --- modules/svg/lanes.js | 212 +++++++++++++++++++++++-------------------- 1 file changed, 113 insertions(+), 99 deletions(-) diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index 1069651c629..d29da9b09bf 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -17,78 +17,123 @@ import { geoPointInPolygon } from '../geo'; export function svgLanes(projection, context) { - return function drawMidpoints(selection, graph, entities, filter, extent, mapCenter) { - // return; + return function drawLanes(selection, graph, entities, filter, extent, mapCenter) { var entity = getEntity(); - var showLanes = []; var metadata; - var iconPosition; var driveLeft; var layoutSeq = []; - const iconWidth = 40; + var iconWidth = 40; + // TODO: on removing map features svgLanes stays there if (entity) { metadata = entity.lanes().metadata; - iconPosition = findPosition(); driveLeft = isDriveLeft(); layoutSeq = getLayoutSeq(metadata, driveLeft, 'turnLanes'); - layer = selection.selectAll('.layer-hit'); - if (iconPosition) { - showLanes = [0]; - } } - var layer = selection.selectAll('.layer-hit'); - - var groups = layer - .selectAll('g.lanes-visual') - .data(showLanes); - groups.exit() + var wrapperData = findPosition(); + + // wrapper DATA BIND + // `wrapperData` is an array set up how we want the DOM to look. + // Always think about data first when working with D3! + // The `data` bind matches DOM nodes to wrapperData array elements + // Each DOM node bound to data will have a special __data__ property + // you can see it in Chrome developer tools + var wrapper = selection.selectAll('.layer-hit') + .selectAll('.lanes-wrapper') + .data(wrapperData ? [wrapperData] : []); + + // wrapper EXIT + // exit selection includes existing DOM nodes with no match in wrapperData + // if you don't remove them, they just stay around forever. + wrapper.exit() .remove(); - var enter = groups.enter() + // wrapper ENTER + // enter selection includes wrapperData with no match to DOM nodes + // the normal thing to do here is create the missing DOM nodes + var enter = wrapper.enter() .insert('g', ':first-child') - .attr('class', 'lanes-visual'); + .attr('class', 'lanes-wrapper'); - var wrapper = enter - .append('g') - .attr('class', 'lanes-visual-wrapper'); + enter + .append('rect') + .attr('class', 'lanes-background'); - wrapper.append('rect') - .attr('class', 'lane-visual-background'); + // enter.append('polygon') + // .attr('points', '-3,4 5,0 -3,-4') + // .attr('class', 'fill'); - wrapper.append('g') - .attr('class', 'lane-visual-items'); - layer - .selectAll('.lanes-visual-wrapper') - .attr('transform', function () { - return 'translate(' + metadata.count * iconWidth / (-2) + ', 0)'; - }); + // wrapper UPDATE + // update selection runs every time for all the matched DOM elements. + // `merge` brings in the nodes that were just entered + // Assignment is important here because selections are immutable, + // so we need to replace wrapper with the new wrapper before using it. + wrapper = wrapper + .merge(enter); - selection.selectAll('rect') - .attr('width', function () { - return metadata.count * iconWidth; - }) - .attr('height', function () { - return iconWidth; + wrapper + .attr('transform', function (d) { + var p = projection(d.loc), + a = graph.entity(d.edge[0]), + b = graph.entity(d.edge[1]), + ang = Math.round(geoAngle(a, b, projection) * (180 / Math.PI)) + 90; + + p[0] -= metadata.count * iconWidth / 2; + return 'translate(' + p[0] + ',' + p[1] + ') rotate(' + ang + ')'; }); - var button = groups.selectAll('lane-visual-items') - .data(layoutSeq) - .enter() + wrapper.selectAll('.lanes-background') + .attr('width', function () { return metadata.count * iconWidth; }) + .attr('height', function () { return iconWidth; }); + + + + // lanes DATA BIND + var lanes = wrapper.selectAll('.lanes-lane') + .data(layoutSeq); + + // lanes EXIT + lanes.exit() + .remove(); + + // lanes ENTER + enter = lanes.enter() .append('g') - .attr('class', 'lane-visual-items radial-menu-item radial-menu-item-move') + .attr('class', 'lanes-lane'); + + enter + .append('circle') + .attr('class', 'lanes-circle') + .attr('r', 15); + + enter + .append('use') + .attr('transform', 'translate(-10,-13)') + .attr('width', '20') + .attr('height', '20') + .attr('xlink:href', function (d) { + // return '#lane-' + createSVGLink(d); + return '#icon-up'; + }); + + // lanes UPDATE + lanes = lanes + .merge(enter); + + lanes .attr('transform', function (d, i) { - var reverse = 0; - if (d.dir === 'backward') { - reverse = 180; - } - return 'translate(' + [iconWidth / 2 + i * iconWidth, (iconWidth / 2)] + ') rotate(' + reverse + ')'; + var transform = 'translate(' + [iconWidth / 2 + i * iconWidth, (iconWidth / 2)] + ')'; + if (d.dir === 'backward') { transform += ' rotate(180)'; } + return transform; }); - button - .append('circle') + // Watch out! `select` here not only selects the first .lanes-circle node, + // but it also propagates __data__ from lanes down to that circle. In this + // situation, it's the behavior we want, so we can style the circle based on `d.dir`. + // `select` propagates __data__ to children, `selectAll` does not. + lanes.select('.lanes-circle') .style('fill', function (d) { switch (d.dir) { case 'forward': @@ -96,42 +141,11 @@ export function svgLanes(projection, context) { case 'backward': return '#ffd8d8'; default: - return ''; + return '#d8d8d8'; } - }) - .attr('r', 15); - - button - .append('use') - .attr('transform', 'translate(-15,-12)') - .attr('width', '20') - .attr('height', '20') - .attr('xlink:href', function (d) { - console.log(d); - return '#lane-' + createSVGLink(d); }); - enter.append('polygon') - .attr('points', '-3,4 5,0 -3,-4') - .attr('class', 'fill'); - groups = groups - .merge(enter) - .attr('transform', function (d) { - var translate = svgPointTransform(projection), - a = graph.entity(iconPosition.edge[0]), - b = graph.entity(iconPosition.edge[1]); - var angleVal = Math.round(geoAngle(a, b, projection) * (180 / Math.PI)) + 90; - return translate(iconPosition) + ' rotate(' + angleVal + ')'; - }) - .call(svgTagClasses().tags( - // TODO: what if entity is null - function () { return entity && entity.tags; } - )); - - // Propagate data bindings. - groups.select('polygon.shadow'); - groups.select('polygon.fill'); function isDriveLeft() { return _.some(dataDriveLeft.features, function (f) { @@ -141,6 +155,7 @@ export function svgLanes(projection, context) { }); } + function getEntity() { if (context.selectedIDs().length !== 1) return null; var entity = graph.entity(context.selectedIDs()[0]); @@ -151,14 +166,11 @@ export function svgLanes(projection, context) { return entity; } + function findPosition() { var loc; - if (!entity) { - return { - loc: undefined, - edge: [] - }; - } + if (!entity) return; + var nodes = graph.childNodes(entity); var poly = extent.polygon(); @@ -189,21 +201,23 @@ export function svgLanes(projection, context) { } } } - function createSVGLink(d) { - var directions; - console.log(d.dir); - directions = metadata.turnLanes[d.dir][d.index]; - - // TODO: fix this vv - if (!directions) return ''; - var dir = directions.sort(function (a, b) { - return a.charCodeAt(0) - b.charCodeAt(0); - }); - dir = dir.join('-'); - if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; - return dir; - } + + // function createSVGLink(d) { + // var directions; + // console.log(d.dir); + // directions = metadata.turnLanes[d.dir][d.index]; + + // // TODO: fix this vv + // if (!directions) return ''; + // var dir = directions.sort(function (a, b) { + // return a.charCodeAt(0) - b.charCodeAt(0); + // }); + // dir = dir.join('-'); + // if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; + + // return dir; + // } }; } From 883316dfc5275af47152bc0906c170374bb39da2 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 2 Feb 2017 11:55:33 +0530 Subject: [PATCH 13/25] svgLanes update fix --- modules/modes/select.js | 18 +----------------- modules/osm/lanes.js | 22 +++++++++++++++++++--- modules/svg/lanes.js | 34 +++++++++------------------------- modules/svg/midpoints.js | 2 +- modules/ui/fields/lanes.js | 1 - modules/ui/lane_visualizer.js | 1 - modules/ui/radial_menu.js | 2 -- 7 files changed, 30 insertions(+), 50 deletions(-) diff --git a/modules/modes/select.js b/modules/modes/select.js index 29427f731f9..d87c76c984c 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -28,7 +28,7 @@ import { import { modeBrowse } from './browse'; import { modeDragNode } from './drag_node'; import * as Operations from '../operations/index'; -import { uiRadialMenu, uiLaneVisualizer, uiSelectionList } from '../ui/index'; +import { uiRadialMenu, uiSelectionList } from '../ui/index'; import { uiCmd } from '../ui/cmd'; import { utilEntityOrMemberSelector, utilEntitySelector } from '../util/index'; @@ -54,7 +54,6 @@ export function modeSelect(context, selectedIDs) { modeDragNode(context).selectedIDs(selectedIDs).behavior ], inspector, - laneVisualizer, radialMenu, newFeature = false, suppressMenu = false, @@ -143,20 +142,11 @@ export function modeSelect(context, selectedIDs) { if (radialMenu) { context.surface().call(radialMenu.close); } - if (laneVisualizer) { - context.surface().call(laneVisualizer.close); - } } function positionMenu() { var entity = singular(); - - if (entity && laneVisualizer && entity.type === 'way') { - laneVisualizer.center(context.mouse()); - laneVisualizer.wayID(entity.id); - } - if (suppressMenu || !radialMenu) { return; } if (entity && context.geometry(entity.id) === 'relation') { @@ -182,9 +172,6 @@ export function modeSelect(context, selectedIDs) { if (!suppressMenu && radialMenu) { context.surface().call(radialMenu); } - if (laneVisualizer) { - context.surface().call(laneVisualizer); - } } @@ -442,8 +429,6 @@ export function modeSelect(context, selectedIDs) { radialMenu = uiRadialMenu(context, operations); - laneVisualizer = uiLaneVisualizer(context); - context.ui().sidebar .select(singular() ? singular().id : null, newFeature); @@ -503,7 +488,6 @@ export function modeSelect(context, selectedIDs) { keybinding.off(); closeMenu(); radialMenu = undefined; - laneVisualizer = undefined; context.history() .on('undone.select', null) diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 2a77477eff4..b5883d8ff49 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -295,7 +295,7 @@ export function getLayoutSeq(metadata, leftHand, kind) { return _.fill(Array(metadata.count), 0).map(function (n, i) { return { dir: 'unspecified', - lanes: metadata[kind].unspecified, + turnLanes: createSVGLink(metadata.turnLanes.unspecified[i]), index: i }; }); @@ -307,14 +307,14 @@ export function getLayoutSeq(metadata, leftHand, kind) { var forSeq = _.fill(Array(forward), 0).map(function (n, i) { return { dir: 'forward', - lanes: metadata[kind].forward, + turnLanes: createSVGLink(metadata.turnLanes.forward[i]), index: i }; }); var backSeq = _.fill(Array(backward), 0).map(function (n, i) { return { dir: 'backward', - lanes: metadata[kind].backward, + turnLanes: createSVGLink(metadata.turnLanes.backward[backward - i - 1]), index: backward - i - 1 }; }); @@ -325,4 +325,20 @@ export function getLayoutSeq(metadata, leftHand, kind) { return [].concat(backSeq, forSeq); } +function createSVGLink(dirArray) { + var directions =_.cloneDeep(dirArray); + // console.log(d.dir); + // directions = metadata.turnLanes[d.dir][d.index]; + + // TODO: fix this vv + if (!directions) return ''; + var dir = directions.sort(function (a, b) { + return a.charCodeAt(0) - b.charCodeAt(0); + }); + dir = dir.join('-'); + if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; + + return dir; + } + window.getLayoutSeq = getLayoutSeq; \ No newline at end of file diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index d29da9b09bf..af4851f307f 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -79,12 +79,13 @@ export function svgLanes(projection, context) { a = graph.entity(d.edge[0]), b = graph.entity(d.edge[1]), ang = Math.round(geoAngle(a, b, projection) * (180 / Math.PI)) + 90; - - p[0] -= metadata.count * iconWidth / 2; return 'translate(' + p[0] + ',' + p[1] + ') rotate(' + ang + ')'; }); wrapper.selectAll('.lanes-background') + .attr('transform', function () { + return 'translate(' + metadata.count * iconWidth / (-2) + ', 0)'; + }) .attr('width', function () { return metadata.count * iconWidth; }) .attr('height', function () { return iconWidth; }); @@ -112,11 +113,7 @@ export function svgLanes(projection, context) { .append('use') .attr('transform', 'translate(-10,-13)') .attr('width', '20') - .attr('height', '20') - .attr('xlink:href', function (d) { - // return '#lane-' + createSVGLink(d); - return '#icon-up'; - }); + .attr('height', '20'); // lanes UPDATE lanes = lanes @@ -124,9 +121,13 @@ export function svgLanes(projection, context) { lanes .attr('transform', function (d, i) { - var transform = 'translate(' + [iconWidth / 2 + i * iconWidth, (iconWidth / 2)] + ')'; + var transform = 'translate(' + [iconWidth / 2 + i * iconWidth - metadata.count * iconWidth / (2) , (iconWidth / 2)] + ')'; if (d.dir === 'backward') { transform += ' rotate(180)'; } return transform; + }) + .select('use') + .attr('xlink:href', function (d) { + return '#lane-' + d.turnLanes; }); // Watch out! `select` here not only selects the first .lanes-circle node, @@ -201,23 +202,6 @@ export function svgLanes(projection, context) { } } } - - - // function createSVGLink(d) { - // var directions; - // console.log(d.dir); - // directions = metadata.turnLanes[d.dir][d.index]; - - // // TODO: fix this vv - // if (!directions) return ''; - // var dir = directions.sort(function (a, b) { - // return a.charCodeAt(0) - b.charCodeAt(0); - // }); - // dir = dir.join('-'); - // if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; - - // return dir; - // } }; } diff --git a/modules/svg/midpoints.js b/modules/svg/midpoints.js index b75c672c2e6..78a4360feb9 100644 --- a/modules/svg/midpoints.js +++ b/modules/svg/midpoints.js @@ -17,7 +17,7 @@ export function svgMidpoints(projection, context) { return function drawMidpoints(selection, graph, entities, filter, extent) { var poly = extent.polygon(), midpoints = {}; - return; + for (var i = 0; i < entities.length; i++) { var entity = entities[i]; diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 1a763054118..842299ecc48 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -1,7 +1,6 @@ import * as d3 from 'd3'; import { utilRebind } from '../../util/rebind'; import { utilGetDimensions } from '../../util/dimensions'; -import { uiFieldCheck } from './check'; import _ from 'lodash'; import { utilGetSetValue } from '../../util/get_set_value'; diff --git a/modules/ui/lane_visualizer.js b/modules/ui/lane_visualizer.js index b55a3449b82..37ffa5c765e 100644 --- a/modules/ui/lane_visualizer.js +++ b/modules/ui/lane_visualizer.js @@ -16,7 +16,6 @@ export function uiLaneVisualizer(context) { var laneVisualizer = function (selection) { if (!wayId) return; - return; metadata = context.entity(wayId).lanes().metadata; if (!metadata) return; diff --git a/modules/ui/radial_menu.js b/modules/ui/radial_menu.js index 0f545f7a2db..1abc0ebff20 100644 --- a/modules/ui/radial_menu.js +++ b/modules/ui/radial_menu.js @@ -9,7 +9,6 @@ export function uiRadialMenu(context, operations) { tooltip; var radialMenu = function(selection) { - if (operations) return; if (!operations.length) return; selection.node().parentNode.focus(); @@ -47,7 +46,6 @@ export function uiRadialMenu(context, operations) { .attr('stroke-width', 50) .attr('stroke-linecap', 'round'); - console.log(operations); var button = menu.selectAll() .data(operations) .enter() From 2f813e1659b0a89fe19b49b11963743c1972104c Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 2 Feb 2017 13:10:11 +0530 Subject: [PATCH 14/25] add right flipping of turnLanes --- modules/osm/lanes.js | 10 +- modules/renderer/map.js | 4 +- modules/svg/lanes.js | 8 +- modules/ui/fields/lanes.js | 25 +---- modules/ui/index.js | 2 - modules/ui/lane_visualizer.js | 167 ---------------------------------- svg/iD-sprite.src.svg | 6 +- 7 files changed, 20 insertions(+), 202 deletions(-) delete mode 100644 modules/ui/lane_visualizer.js diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index b5883d8ff49..d34e5c61cd0 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -1,5 +1,9 @@ import _ from 'lodash'; +export var validTurnLanes = [ + 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', + 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' +]; export function osmLanes(entity) { if (entity.type !== 'way') return null; @@ -188,17 +192,13 @@ function parseLaneDirections(tags, isOneWay, laneCount) { function parseTurnLanes(tag){ if (!tag) return []; // TODO: need to add reverse_left and reverse_right - var validValues = [ - 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', - 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' - ]; return tag.split('|') .map(function (s) { if (s === '') s = 'none'; return s.split(';') .map(function (d) { - return validValues.indexOf(d) === -1 ? 'unknown': d; + return validTurnLanes.indexOf(d) === -1 ? 'unknown': d; }); }); } diff --git a/modules/renderer/map.js b/modules/renderer/map.js index baf0fed15fe..9b8794fc016 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -162,7 +162,7 @@ export function rendererMap(context) { surface.selectAll('.data-layer-osm') .call(drawVertices, graph, all, filter, map.extent(), map.zoom()) .call(drawMidpoints, graph, all, filter, map.trimmedExtent()) - .call(drawLanes, graph, all, filter, map.trimmedExtent(), map.center()); + .call(drawLanes, graph, all, filter, map.trimmedExtent(), map.zoom(), map.center()); dispatch.call('drawn', this, {full: false}); } }); @@ -255,7 +255,7 @@ export function rendererMap(context) { .call(drawLines, graph, data, filter) .call(drawAreas, graph, data, filter) .call(drawMidpoints, graph, data, filter, map.trimmedExtent()) - .call(drawLanes, graph, data, filter, map.trimmedExtent(), map.center()) + .call(drawLanes, graph, data, filter, map.trimmedExtent(), map.zoom(), map.center()) .call(drawLabels, graph, data, filter, dimensions, !difference && !extent) .call(drawPoints, graph, data, filter); diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index af4851f307f..3f44a109f48 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -17,12 +17,13 @@ import { geoPointInPolygon } from '../geo'; export function svgLanes(projection, context) { - return function drawLanes(selection, graph, entities, filter, extent, mapCenter) { + return function drawLanes(selection, graph, entities, filter, extent, zoom, mapCenter) { var entity = getEntity(); var metadata; var driveLeft; var layoutSeq = []; var iconWidth = 40; + var zoomLimit = zoom >= 18; // TODO: on removing map features svgLanes stays there if (entity) { @@ -41,7 +42,7 @@ export function svgLanes(projection, context) { // you can see it in Chrome developer tools var wrapper = selection.selectAll('.layer-hit') .selectAll('.lanes-wrapper') - .data(wrapperData ? [wrapperData] : []); + .data(wrapperData && zoomLimit ? [wrapperData] : []); // wrapper EXIT // exit selection includes existing DOM nodes with no match in wrapperData @@ -122,12 +123,13 @@ export function svgLanes(projection, context) { lanes .attr('transform', function (d, i) { var transform = 'translate(' + [iconWidth / 2 + i * iconWidth - metadata.count * iconWidth / (2) , (iconWidth / 2)] + ')'; + transform += d.turnLanes.indexOf('right') > -1 ? ' scale(-1, 1)' : ''; if (d.dir === 'backward') { transform += ' rotate(180)'; } return transform; }) .select('use') .attr('xlink:href', function (d) { - return '#lane-' + d.turnLanes; + return '#lane-' + d.turnLanes.split('right').join('left'); }); // Watch out! `select` here not only selects the first .lanes-circle node, diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 842299ecc48..486a33034e2 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -3,12 +3,7 @@ import { utilRebind } from '../../util/rebind'; import { utilGetDimensions } from '../../util/dimensions'; import _ from 'lodash'; import { utilGetSetValue } from '../../util/get_set_value'; - -var validLanes = [ - 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', - 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' -]; - +import { validTurnLanes } from '../../osm/lanes'; export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), @@ -17,22 +12,18 @@ export function uiFieldLanes(field, context) { currentLane = 0, curDirection = 'unspecified', wayID, - lanesData, - driveLeft; + lanesData; function lanes(selection) { lanesData = context.entity(wayID).lanes(); window.lanesData = lanesData; - window.currentLane = currentLane; - window.curDirection = curDirection; - + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { selection.call(lanes.off); return; } - var lanesInfo = selection.selectAll('.lanes-info').data([0]); lanesInfo = lanesInfo.enter() .append('div') @@ -59,17 +50,11 @@ export function uiFieldLanes(field, context) { var wrap = selection.selectAll('.lane-input-wrap') .data([0]); - wrap = wrap.enter() + wrap.enter() .append('div') .attr('class', 'lane-input-wrap') .merge(wrap); - var surface = wrap.selectAll('.surface') - .data([0]); - - var d = utilGetDimensions(wrap); - var freeSpace = d[0] - lanesData.metadata.count * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; - function render() { if (context.hasEntity(wayID)) { lanes(selection); @@ -192,7 +177,7 @@ export function uiFieldLanes(field, context) { var label = wrap.selectAll('.label') - .data(validLanes); + .data(validTurnLanes); var labelEnter = label.enter() .append('label'); diff --git a/modules/ui/index.js b/modules/ui/index.js index 050dcd0e5a5..37bfc22cc9b 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -47,5 +47,3 @@ export { uiTooltipHtml } from './tooltipHtml'; export { uiUndoRedo } from './undo_redo'; export { uiViewOnOSM } from './view_on_osm'; export { uiZoom } from './zoom'; - -export { uiLaneVisualizer } from './lane_visualizer'; \ No newline at end of file diff --git a/modules/ui/lane_visualizer.js b/modules/ui/lane_visualizer.js deleted file mode 100644 index 37ffa5c765e..00000000000 --- a/modules/ui/lane_visualizer.js +++ /dev/null @@ -1,167 +0,0 @@ -import * as d3 from 'd3'; -import _ from 'lodash'; -import { geoAngle, geoChooseEdge } from '../geo/index'; -import { getLayoutSeq } from '../osm/lanes'; -import { dataDriveLeft } from '../../data'; -import { geoPointInPolygon } from '../geo'; - -export function uiLaneVisualizer(context) { - var menu, - wrapper, - wayId, - center = [0, 0], - tooltip, - metadata, - driveLeft; - - var laneVisualizer = function (selection) { - if (!wayId) return; - metadata = context.entity(wayId).lanes().metadata; - if (!metadata) return; - - const iconWidth = 40; - const count = metadata.count; - - var projection = context.projection; - var graph = context.graph(); - var way = graph.entity(wayId); - - var loc = way.extent(context.graph()).center(); - driveLeft = _.some(dataDriveLeft.features, function(f) { - return _.some(f.geometry.coordinates, function(d) { - return geoPointInPolygon(loc, d); - }); - }); - - var layout = getLayoutSeq(metadata, driveLeft); - - var nodes = _.uniq(graph.childNodes(way)); - var choice = geoChooseEdge(nodes, context.mouse(), context.projection); - var prev = nodes[choice.index - 1]; - var next = nodes[choice.index]; - var angle = (geoAngle(prev, next, projection) * 180 / Math.PI); - - context.history() - .on('change.lanes', render); - - selection.node().parentNode.focus(); - - function click() { - d3.event.stopPropagation(); - laneVisualizer.close(); - } - - wrapper = selection - .append('g') - .attr('class', 'lane-visualizer') - // .attr('transform', 'translate('+ center + ')') - .attr('transform', 'translate(' + center + ') rotate(' + (angle+ 90) + ')') - .attr('opacity', 0); - - wrapper - .transition() - .attr('opacity', 1); - - var menu = wrapper - .append('g') - .attr('transform', 'translate(' + count*iconWidth/(-2) + ', 0)'); - - menu - .append('rect') - .attr('class', 'lane-visualizer-background') - .attr('width', count* iconWidth) - .attr('height', iconWidth); - - var button = menu.selectAll() - .data(layout) - .enter() - .append('g') - .attr('class', 'radial-menu-item radial-menu-item-move') - .attr('transform', function (d, i) { - var reverse = 0; - if (d.dir === 'backward') { - reverse = 180; - } - return 'translate(' + [iconWidth / 2 + i * iconWidth, (iconWidth / 2)] + ') rotate(' + reverse + ')'; - }); - - button - .append('circle') - .style('fill', function(d) { - switch (d.dir) { - case 'forward': - return '#dfffdf'; - case 'backward': - return '#ffd8d8'; - default: - return ''; - } - }) - .attr('r', 15); - - button - .append('use') - .attr('transform', 'translate(-15,-12)') - .attr('width', '20') - .attr('height', '20') - .attr('xlink:href', function (d) { - return '#lane-' + createSVGLink(d); - }); - - - function render() { - if (context.hasEntity(wayId)) { - laneVisualizer.close(); - laneVisualizer(selection); - } - } - - }; - function createSVGLink(d) { - var directions; - console.log(d.dir); - directions = metadata.turnLanes[d.dir][d.index]; - - // TODO: fix this vv - if (!directions) return ''; - var dir = directions.sort(function (a, b) { - return a.charCodeAt(0) - b.charCodeAt(0); - }); - dir = dir.join('-'); - if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; - - return dir; - } - laneVisualizer.center = function (_) { - if (!arguments.length) return center; - center = _; - return laneVisualizer; - }; - - - laneVisualizer.wayID = function (_) { - if (!arguments.length) return _; - wayId = _; - return laneVisualizer; - }; - - laneVisualizer.close = function () { - if (wrapper) { - - wrapper - .style('pointer-events', 'none') - .transition() - .attr('opacity', 0) - .remove(); - - context.history() - .on('change.lanes', null); - } - - - if (tooltip) { - tooltip.remove(); - } - }; - return laneVisualizer; -} diff --git a/svg/iD-sprite.src.svg b/svg/iD-sprite.src.svg index 859baae60fc..97808e72a32 100644 --- a/svg/iD-sprite.src.svg +++ b/svg/iD-sprite.src.svg @@ -210,12 +210,12 @@ - + - + @@ -225,7 +225,7 @@ - + From 9104b018b6ee9b2e6c23485e5fedc9a8e527d8ef Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Thu, 2 Feb 2017 19:27:05 +0530 Subject: [PATCH 15/25] add lane count check --- modules/osm/lanes.js | 271 +++++++-------- modules/ui/fields/lanes.js | 6 +- test/spec/osm/lanes.js | 652 ++++++++++++++++++++++--------------- 3 files changed, 513 insertions(+), 416 deletions(-) diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index d34e5c61cd0..03c9569c9b1 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -14,13 +14,6 @@ export function osmLanes(entity) { var laneCount = getLaneCount(tags, isOneWay); var maxspeed = parseMaxspeed(tags); - var laneDirections = parseLaneDirections(tags, isOneWay, laneCount); - var forward = laneDirections.forward; - var backward = laneDirections.backward; - var bothways = laneDirections.bothways; - - // sometimes just forward and backward are available - laneCount = laneDirections.laneCount; // TODO: if you change the forward backward and click on trash at top, // laneCount and forward/backward goes out of sync @@ -71,47 +64,59 @@ export function osmLanes(entity) { bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']); bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']); - var lanesObj = { - forward: [], - backward: [], - unspecified: [] - }; - - // map forward/backward/unspecified of each lane type to lanesObj - mapToLanesObj(lanesObj, turnLanes, 'turnLane'); - mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed'); - mapToLanesObj(lanesObj, psvLanes, 'psv'); - mapToLanesObj(lanesObj, busLanes, 'bus'); - mapToLanesObj(lanesObj, taxiLanes, 'taxi'); - mapToLanesObj(lanesObj, hovLanes, 'hov'); - mapToLanesObj(lanesObj, hgvLanes, 'hgv'); - mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway'); - // TODO: need to make sure forward lanes is consistent across all tags, // eg if psv:lanes:forward is 3 and lanes:forward is 2, changes lanes:forward =3, var metadata = { - count: laneCount, - oneway: isOneWay, - forward: forward, - backward: backward, - bothways: bothways, - turnLanes: turnLanes, - maxspeed: maxspeed, - maxspeedLanes: maxspeedLanes, - psvLanes: psvLanes, - busLanes: busLanes, - taxiLanes: taxiLanes, - hovLanes: hovLanes, - hgvLanes: hgvLanes, - bicyclewayLanes: bicyclewayLanes, - reverse: parseInt(tags.oneway, 10) === -1 + count: laneCount, + oneway: isOneWay, + turnLanes: turnLanes, + maxspeedLanes: maxspeedLanes, + psvLanes: psvLanes, + busLanes: busLanes, + taxiLanes: taxiLanes, + hovLanes: hovLanes, + hgvLanes: hgvLanes, + bicyclewayLanes: bicyclewayLanes, + reverse: parseInt(tags.oneway, 10) === -1 }; + + tallyLaneCount(metadata); + parseLaneDirections(tags, isOneWay, metadata); + return { metadata: metadata, - lanes: lanesObj + getLayoutSeq: getLayoutSeq }; } +// Overides the lanes, lanes:forward, lanes:backward +// if the other lane tags dont tally. +function tallyLaneCount(metadata) { + var consideredLaneTags = ['busLanes', 'hgvLanes', 'hovLanes', 'psvLanes', 'taxiLanes', 'turnLanes']; + var maxUnspecified = 0; + var maxForward = 0; + var maxBackward = 0; + + consideredLaneTags.forEach(function (tag) { + if (metadata[tag].unspecified.length > maxUnspecified) + maxUnspecified = metadata[tag].unspecified.length; + + if (metadata[tag].forward.length > maxForward) + maxForward = metadata[tag].forward.length; + + if (metadata[tag].backward.length > maxBackward) + maxBackward = metadata[tag].backward.length; + }); + + if (metadata.oneway) { + metadata.count = maxUnspecified > metadata.count ? maxUnspecified : metadata.count; + } else { + metadata.forward = maxForward > 0 ? maxForward : undefined; + metadata.backward = maxBackward > 0 ? maxBackward : undefined; + } + // TODO: how to update if they decrease metadata.forward and the array would be bigger + // this function would override it. +} function getLaneCount(tags, isOneWay) { var count; @@ -122,7 +127,6 @@ function getLaneCount(tags, isOneWay) { } } - switch (tags.highway) { case 'trunk': case 'motorway': @@ -136,7 +140,7 @@ function getLaneCount(tags, isOneWay) { return count; } - +// TODO: needs fix, difference between maxspeed and lane:maxspeed function parseMaxspeed(tags) { var maxspeed = tags.maxspeed; if (_.isNumber(maxspeed)) return maxspeed; @@ -147,23 +151,22 @@ function parseMaxspeed(tags) { } } - -function parseLaneDirections(tags, isOneWay, laneCount) { - var forward = parseInt(tags['lanes:forward'], 10); - var backward = parseInt(tags['lanes:backward'], 10); +// gives priority to '*:lanes:direction' over 'lanes:direction' +function parseLaneDirections(tags, isOneWay, metadata) { + var laneCount = metadata.count; + var forward = metadata.forward || parseInt(tags['lanes:forward'], 10); + var backward = metadata.backward || parseInt(tags['lanes:backward'], 10); var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0; - var count = laneCount; - if (parseInt(tags.oneway, 10) === -1) { - forward = 0; - bothways = 0; - backward = laneCount; - } - else if (isOneWay) { - forward = laneCount; - bothways = 0; - backward = 0; + + // should just rely on lane count + if (isOneWay) { + metadata.forward = 0; + metadata.bothways = 0; + metadata.backward = 0; + return; } - else if (_.isNaN(forward) && _.isNaN(backward)) { + + if (_.isNaN(forward) && _.isNaN(backward)) { backward = Math.floor((laneCount - bothways) / 2); forward = laneCount - bothways - backward; } @@ -179,17 +182,15 @@ function parseLaneDirections(tags, isOneWay, laneCount) { } backward = laneCount - bothways - forward; } + metadata.forward = forward; + metadata.bothways = bothways; + metadata.backward = backward; + metadata.count = forward + backward + bothways; - return { - forward: forward, - backward: backward, - bothways: bothways, - laneCount: forward + backward+ bothways - }; + return; } - -function parseTurnLanes(tag){ +function parseTurnLanes(tag) { if (!tag) return []; // TODO: need to add reverse_left and reverse_right @@ -198,7 +199,7 @@ function parseTurnLanes(tag){ if (s === '') s = 'none'; return s.split(';') .map(function (d) { - return validTurnLanes.indexOf(d) === -1 ? 'unknown': d; + return validTurnLanes.indexOf(d) === -1 ? 'unknown' : d; }); }); } @@ -212,7 +213,7 @@ function parseMaxspeedLanes(tag, maxspeed) { if (s === 'none') return s; var m = parseInt(s, 10); if (s === '' || m === maxspeed) return null; - return _.isNaN(m) ? 'unknown': m; + return _.isNaN(m) ? 'unknown' : m; }); } @@ -227,7 +228,7 @@ function parseMiscLanes(tag) { return tag.split('|') .map(function (s) { if (s === '') s = 'no'; - return validValues.indexOf(s) === -1 ? 'unknown': s; + return validValues.indexOf(s) === -1 ? 'unknown' : s; }); } @@ -242,103 +243,71 @@ function parseBicycleWay(tag) { return tag.split('|') .map(function (s) { if (s === '') s = 'no'; - return validValues.indexOf(s) === -1 ? 'unknown': s; + return validValues.indexOf(s) === -1 ? 'unknown' : s; }); } +// TODO: exporting it directly since parameter leftHand is needed +export function getLayoutSeq(metadata, leftHand) { -function mapToLanesObj(lanesObj, data, key) { - if (data.forward) data.forward.forEach(function(l, i) { - if (!lanesObj.forward[i]) lanesObj.forward[i] = {}; - lanesObj.forward[i][key] = l; - }); - if (data.backward) data.backward.forEach(function(l, i) { - if (!lanesObj.backward[i]) lanesObj.backward[i] = {}; - lanesObj.backward[i][key] = l; - }); - if (data.unspecified) data.unspecified.forEach(function(l, i) { - if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {}; - lanesObj.unspecified[i][key] = l; - }); -} - -function lanesArray(lanesData) { - var metadata = _.cloneDeep(lanesData); - // var arr = new Array(metadata.count); - var consideredLaneTags = [ 'busLanes', 'hgvLanes', 'hovLanes', 'maxspeedLanes', 'psvLanes', 'taxiLanes', 'turnLanes' ]; - var obj = {}; - - obj.forward = new Array(metadata.forward); - obj.backward = new Array(metadata.backward); - // obj.bothways = new Array(metadata.bothways); // jo - obj.unspecified = new Array(metadata.count); //_.fill(Array(metadata.count), { }); - - consideredLaneTags.forEach(function (laneTag) { - var lane = metadata[laneTag]; - Object.keys(lane).forEach(function (direction) { - lane[direction] - .forEach(function (tag, i) { - if (!obj[direction][i]) obj[direction][i] = {}; - if (i < obj[direction].length) { - obj[direction][i][laneTag] = tag; - } - }); - }); - }); - - return obj; -} + function turnLanesSeq(obj) { + var dir = obj.dir; + var index = obj.index; + // will be '' if array goes out of bound + obj.turnLanes = createSVGLink(metadata.turnLanes[dir][index]); + return obj; + } -export function getLayoutSeq(metadata, leftHand, kind) { if (!metadata) return []; + + var seq = []; + if (metadata.oneway) { - return _.fill(Array(metadata.count), 0).map(function (n, i) { + seq = _.fill(Array(metadata.count), 0) + .map(function (n, i) { + return { + dir: 'unspecified', + index: i + }; + }); + } else { + var forward = metadata.forward; + var backward = metadata.backward; + + var forSeq = _.fill(Array(forward), 0).map(function (n, i) { return { - dir: 'unspecified', - turnLanes: createSVGLink(metadata.turnLanes.unspecified[i]), + dir: 'forward', index: i }; }); + // backward seq is always reversed in any hand drive. + // eg: turn:lanes:backward=0|1|2|3, turn:lanes:forward=0|1|2 + // LHD = 0,1,23,2,1,0; RHD= 3,2,1,00,1,2 + var backSeq = _.fill(Array(backward), 0).map(function (n, i) { + return { + dir: 'backward', + index: backward - i - 1 + }; + }); + + seq = leftHand ? [].concat(forSeq, backSeq) + : [].concat(backSeq, forSeq); } - - var forward = metadata.forward; - var backward = metadata.backward; - - var forSeq = _.fill(Array(forward), 0).map(function (n, i) { - return { - dir: 'forward', - turnLanes: createSVGLink(metadata.turnLanes.forward[i]), - index: i - }; - }); - var backSeq = _.fill(Array(backward), 0).map(function (n, i) { - return { - dir: 'backward', - turnLanes: createSVGLink(metadata.turnLanes.backward[backward - i - 1]), - index: backward - i - 1 - }; - }); - - if (leftHand) { - return [].concat(forSeq, backSeq); - } - return [].concat(backSeq, forSeq); -} -function createSVGLink(dirArray) { - var directions =_.cloneDeep(dirArray); - // console.log(d.dir); - // directions = metadata.turnLanes[d.dir][d.index]; + seq = seq + .map(turnLanesSeq); - // TODO: fix this vv - if (!directions) return ''; - var dir = directions.sort(function (a, b) { - return a.charCodeAt(0) - b.charCodeAt(0); - }); - dir = dir.join('-'); - if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; + return seq; +} - return dir; - } +function createSVGLink(directions) { + if (!directions || !_.isArray(directions)) return ''; + var dir = _.cloneDeep(directions).sort(function (a, b) { + // lane icons are sorted in lexical order + return a.charCodeAt(0) - b.charCodeAt(0); + }); + dir = dir.join('-'); + if (dir.indexOf('unknown') > -1 || dir.length === 0) return 'unknown'; -window.getLayoutSeq = getLayoutSeq; \ No newline at end of file + return dir; +} diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 486a33034e2..de5ef4fb608 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -335,11 +335,11 @@ export function uiFieldLanes(field, context) { tag['turn:lanes:forward'] = undefined; tag['turn:lanes:backward'] = undefined; } else { - tag.lanes = (metadata.forward + metadata.backward) + ''; + tag.lanes = (metadata.forward + metadata.backward) + ''; //TODO: add bothways tag['lanes:forward'] = metadata.forward + ''; tag['lanes:backward'] = metadata.backward + ''; - + tag['turn:lanes'] = undefined; tag['turn:lanes:forward'] = formPipes(metadata.turnLanes.forward, metadata.forward, 'none'); tag['turn:lanes:backward'] = formPipes(metadata.turnLanes.backward, metadata.backward, 'none'); @@ -349,6 +349,8 @@ export function uiFieldLanes(field, context) { console.log('turn:lane ===',tag['turn:lanes']); console.log('turn:lane;forward ==',tag['turn:lanes:forward']); console.log('turn:lane;backward ==',tag['turn:lanes:backward']); + + // TODO: need to prune unwanted things, and lane:forward,backward would be zero if oneway dispatch.call('change', this, tag); } diff --git a/test/spec/osm/lanes.js b/test/spec/osm/lanes.js index c704af8bace..99a22b21bbe 100644 --- a/test/spec/osm/lanes.js +++ b/test/spec/osm/lanes.js @@ -1,217 +1,217 @@ -describe.only('iD.Lanes', function() { +describe.only('iD.Lanes', function () { - describe('default lane tags', function() { + describe('default lane tags', function () { - describe('motorway', function() { + describe('motorway', function () { - it('returns 2 lanes for highway=motorway', function() { - expect(iD.Way({tags: { highway: 'motorway' }}).lanes().metadata.count, 'motorway lanes') + it('returns 2 lanes for highway=motorway', function () { + expect(iD.Way({ tags: { highway: 'motorway' } }).lanes().metadata.count, 'motorway lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'motorway', oneway: 'yes' }}).lanes().metadata.count, 'motorway lanes') + expect(iD.Way({ tags: { highway: 'motorway', oneway: 'yes' } }).lanes().metadata.count, 'motorway lanes') .to.eql(2); }); - it('returns 4 lanes for highway=motorway and oneway=no', function() { - expect(iD.Way({tags: { highway: 'motorway', oneway: 'no' }}).lanes().metadata.count, 'motorway lanes') + it('returns 4 lanes for highway=motorway and oneway=no', function () { + expect(iD.Way({ tags: { highway: 'motorway', oneway: 'no' } }).lanes().metadata.count, 'motorway lanes') .to.eql(4); }); - it('returns 1 lane for highway=motorway_link', function() { - expect(iD.Way({tags: { highway: 'motorway_link' }}).lanes().metadata.count, 'motorway_link lanes') + it('returns 1 lane for highway=motorway_link', function () { + expect(iD.Way({ tags: { highway: 'motorway_link' } }).lanes().metadata.count, 'motorway_link lanes') .to.eql(1); - expect(iD.Way({tags: { highway: 'motorway_link', oneway: 'yes' }}).lanes().metadata.count, 'motorway_link lanes') + expect(iD.Way({ tags: { highway: 'motorway_link', oneway: 'yes' } }).lanes().metadata.count, 'motorway_link lanes') .to.eql(1); }); - it('returns 2 lanes for highway=motorway_link and oneway=no', function() { - expect(iD.Way({tags: { highway: 'motorway_link', oneway: 'no' }}).lanes().metadata.count, 'motorway_link lanes') + it('returns 2 lanes for highway=motorway_link and oneway=no', function () { + expect(iD.Way({ tags: { highway: 'motorway_link', oneway: 'no' } }).lanes().metadata.count, 'motorway_link lanes') .to.eql(2); }); }); - describe('trunk', function() { + describe('trunk', function () { - it('returns 4 lanes for highway=trunk', function() { - expect(iD.Way({tags: { highway: 'trunk' }}).lanes().metadata.count, 'trunk lanes') + it('returns 4 lanes for highway=trunk', function () { + expect(iD.Way({ tags: { highway: 'trunk' } }).lanes().metadata.count, 'trunk lanes') .to.eql(4); - expect(iD.Way({tags: { highway: 'trunk', oneway: 'no' }}).lanes().metadata.count, 'trunk lanes') + expect(iD.Way({ tags: { highway: 'trunk', oneway: 'no' } }).lanes().metadata.count, 'trunk lanes') .to.eql(4); }); - it('returns 2 lanes for highway=trunk and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', oneway: 'yes' }}).lanes().metadata.count, 'trunk lanes') + it('returns 2 lanes for highway=trunk and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'trunk', oneway: 'yes' } }).lanes().metadata.count, 'trunk lanes') .to.eql(2); }); - it('returns 2 lanes for highway=trunk_link', function() { - expect(iD.Way({tags: { highway: 'trunk_link' }}).lanes().metadata.count, 'trunk_link lanes') + it('returns 2 lanes for highway=trunk_link', function () { + expect(iD.Way({ tags: { highway: 'trunk_link' } }).lanes().metadata.count, 'trunk_link lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'trunk_link', oneway: 'no' }}).lanes().metadata.count, 'trunk_link lanes') + expect(iD.Way({ tags: { highway: 'trunk_link', oneway: 'no' } }).lanes().metadata.count, 'trunk_link lanes') .to.eql(2); }); - it('returns 1 lane for highway=trunk_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk_link', oneway: 'yes' }}).lanes().metadata.count, 'trunk_link lanes') + it('returns 1 lane for highway=trunk_link and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'trunk_link', oneway: 'yes' } }).lanes().metadata.count, 'trunk_link lanes') .to.eql(1); }); }); - describe('primary', function() { + describe('primary', function () { - it('returns 2 lanes for highway=primary', function() { - expect(iD.Way({tags: { highway: 'primary' }}).lanes().metadata.count, 'primary lanes') + it('returns 2 lanes for highway=primary', function () { + expect(iD.Way({ tags: { highway: 'primary' } }).lanes().metadata.count, 'primary lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'primary', oneway: 'no' }}).lanes().metadata.count, 'primary lanes') + expect(iD.Way({ tags: { highway: 'primary', oneway: 'no' } }).lanes().metadata.count, 'primary lanes') .to.eql(2); }); - it('returns 1 lane for highway=primary and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'primary', oneway: 'yes' }}).lanes().metadata.count, 'primary lanes') + it('returns 1 lane for highway=primary and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'primary', oneway: 'yes' } }).lanes().metadata.count, 'primary lanes') .to.eql(1); }); - it('returns 2 lanes for highway=primary_link', function() { - expect(iD.Way({tags: { highway: 'primary_link' }}).lanes().metadata.count, 'primary lanes') + it('returns 2 lanes for highway=primary_link', function () { + expect(iD.Way({ tags: { highway: 'primary_link' } }).lanes().metadata.count, 'primary lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'primary_link', oneway: 'no' }}).lanes().metadata.count, 'primary lanes') + expect(iD.Way({ tags: { highway: 'primary_link', oneway: 'no' } }).lanes().metadata.count, 'primary lanes') .to.eql(2); }); - it('returns 1 lane for highway=primary_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'primary_link', oneway: 'yes' }}).lanes().metadata.count, 'primary lanes') + it('returns 1 lane for highway=primary_link and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'primary_link', oneway: 'yes' } }).lanes().metadata.count, 'primary lanes') .to.eql(1); }); }); - describe('seconday', function() { + describe('seconday', function () { - it('returns 2 lanes for highway=secondary', function() { - expect(iD.Way({tags: { highway: 'secondary' }}).lanes().metadata.count, 'secondary lanes') + it('returns 2 lanes for highway=secondary', function () { + expect(iD.Way({ tags: { highway: 'secondary' } }).lanes().metadata.count, 'secondary lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'secondary', oneway: 'no' }}).lanes().metadata.count, 'secondary lanes') + expect(iD.Way({ tags: { highway: 'secondary', oneway: 'no' } }).lanes().metadata.count, 'secondary lanes') .to.eql(2); }); - it('returns 1 lane for highway=secondary and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'secondary', oneway: 'yes' }}).lanes().metadata.count, 'secondary lanes') + it('returns 1 lane for highway=secondary and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'secondary', oneway: 'yes' } }).lanes().metadata.count, 'secondary lanes') .to.eql(1); }); - it('returns 2 lane for highway=secondary_link', function() { - expect(iD.Way({tags: { highway: 'secondary_link' }}).lanes().metadata.count, 'secondary_link lanes') + it('returns 2 lane for highway=secondary_link', function () { + expect(iD.Way({ tags: { highway: 'secondary_link' } }).lanes().metadata.count, 'secondary_link lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'secondary_link', oneway: 'no' }}).lanes().metadata.count, 'secondary_link lanes') + expect(iD.Way({ tags: { highway: 'secondary_link', oneway: 'no' } }).lanes().metadata.count, 'secondary_link lanes') .to.eql(2); }); - it('returns 1 lane for highway=secondary_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'secondary_link', oneway: 'yes' }}).lanes().metadata.count, 'secondary_link lanes') + it('returns 1 lane for highway=secondary_link and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'secondary_link', oneway: 'yes' } }).lanes().metadata.count, 'secondary_link lanes') .to.eql(1); }); }); - describe('tertiary', function() { + describe('tertiary', function () { - it('returns 2 lanes for highway=tertiary', function() { - expect(iD.Way({tags: { highway: 'tertiary' }}).lanes().metadata.count, 'tertiary lanes') + it('returns 2 lanes for highway=tertiary', function () { + expect(iD.Way({ tags: { highway: 'tertiary' } }).lanes().metadata.count, 'tertiary lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'tertiary', oneway: 'no' }}).lanes().metadata.count, 'tertiary lanes') + expect(iD.Way({ tags: { highway: 'tertiary', oneway: 'no' } }).lanes().metadata.count, 'tertiary lanes') .to.eql(2); }); - it('returns 1 lane for highway=tertiary and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'tertiary', oneway: 'yes' }}).lanes().metadata.count, 'tertiary lanes') + it('returns 1 lane for highway=tertiary and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'tertiary', oneway: 'yes' } }).lanes().metadata.count, 'tertiary lanes') .to.eql(1); }); - it('returns 2 lane for highway=tertiary_link', function() { - expect(iD.Way({tags: { highway: 'tertiary_link' }}).lanes().metadata.count, 'tertiary_link lanes') + it('returns 2 lane for highway=tertiary_link', function () { + expect(iD.Way({ tags: { highway: 'tertiary_link' } }).lanes().metadata.count, 'tertiary_link lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'tertiary_link', oneway: 'no' }}).lanes().metadata.count, 'tertiary_link lanes') + expect(iD.Way({ tags: { highway: 'tertiary_link', oneway: 'no' } }).lanes().metadata.count, 'tertiary_link lanes') .to.eql(2); }); - it('returns 1 lane for highway=tertiary_link and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'tertiary_link', oneway: 'yes' }}).lanes().metadata.count, 'tertiary_link lanes') + it('returns 1 lane for highway=tertiary_link and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'tertiary_link', oneway: 'yes' } }).lanes().metadata.count, 'tertiary_link lanes') .to.eql(1); }); }); - describe('residential', function() { + describe('residential', function () { - it('returns 2 lanes for highway=residential', function() { - expect(iD.Way({tags: { highway: 'residential' }}).lanes().metadata.count, 'residential lanes') + it('returns 2 lanes for highway=residential', function () { + expect(iD.Way({ tags: { highway: 'residential' } }).lanes().metadata.count, 'residential lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'residential', oneway: 'no' }}).lanes().metadata.count, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', oneway: 'no' } }).lanes().metadata.count, 'residential lanes') .to.eql(2); }); - it('returns 1 lane for highway=residential and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'residential', oneway: 'yes' }}).lanes().metadata.count, 'residential lanes') + it('returns 1 lane for highway=residential and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'residential', oneway: 'yes' } }).lanes().metadata.count, 'residential lanes') .to.eql(1); }); }); - describe('service', function() { + describe('service', function () { - it('returns 2 lanes for highway=service', function() { - expect(iD.Way({tags: { highway: 'service' }}).lanes().metadata.count, 'service lanes') + it('returns 2 lanes for highway=service', function () { + expect(iD.Way({ tags: { highway: 'service' } }).lanes().metadata.count, 'service lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'service', oneway: 'no' }}).lanes().metadata.count, 'service lanes') + expect(iD.Way({ tags: { highway: 'service', oneway: 'no' } }).lanes().metadata.count, 'service lanes') .to.eql(2); }); - it('returns 1 lane for highway=service and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'service', oneway: 'yes' }}).lanes().metadata.count, 'service lanes') + it('returns 1 lane for highway=service and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'service', oneway: 'yes' } }).lanes().metadata.count, 'service lanes') .to.eql(1); }); }); - describe('track', function() { + describe('track', function () { - it('returns 2 lanes for highway=track', function() { - expect(iD.Way({tags: { highway: 'track' }}).lanes().metadata.count, 'track lanes') + it('returns 2 lanes for highway=track', function () { + expect(iD.Way({ tags: { highway: 'track' } }).lanes().metadata.count, 'track lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'track', oneway: 'no' }}).lanes().metadata.count, 'track lanes') + expect(iD.Way({ tags: { highway: 'track', oneway: 'no' } }).lanes().metadata.count, 'track lanes') .to.eql(2); }); - it('returns 1 lane for highway=track and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'track', oneway: 'yes' }}).lanes().metadata.count, 'track lanes') + it('returns 1 lane for highway=track and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'track', oneway: 'yes' } }).lanes().metadata.count, 'track lanes') .to.eql(1); }); }); - describe('path', function() { + describe('path', function () { - it('returns 2 lanes for highway=path', function() { - expect(iD.Way({tags: { highway: 'path' }}).lanes().metadata.count, 'path lanes') + it('returns 2 lanes for highway=path', function () { + expect(iD.Way({ tags: { highway: 'path' } }).lanes().metadata.count, 'path lanes') .to.eql(2); - expect(iD.Way({tags: { highway: 'path', oneway: 'no' }}).lanes().metadata.count, 'path lanes') + expect(iD.Way({ tags: { highway: 'path', oneway: 'no' } }).lanes().metadata.count, 'path lanes') .to.eql(2); }); - it('returns 1 lane for highway=path and oneway=yes', function() { - expect(iD.Way({tags: { highway: 'path', oneway: 'yes' }}).lanes().metadata.count, 'path lanes') + it('returns 1 lane for highway=path and oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'path', oneway: 'yes' } }).lanes().metadata.count, 'path lanes') .to.eql(1); }); }); }); - describe('oneway tags', function() { - it('returns correctly oneway when tagged as oneway', function() { - expect(iD.Way({tags: { highway: 'residential', oneway: 'yes' }}).lanes().metadata.oneway, 'residential lanes') + describe('oneway tags', function () { + it('returns correctly oneway when tagged as oneway', function () { + expect(iD.Way({ tags: { highway: 'residential', oneway: 'yes' } }).lanes().metadata.oneway, 'residential lanes') .to.be.true; - expect(iD.Way({tags: { highway: 'residential', oneway: 'no' }}).lanes().metadata.oneway, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', oneway: 'no' } }).lanes().metadata.oneway, 'residential lanes') .to.be.false; }); }); - describe('lane direction', function() { + describe('lane direction', function () { - it('returns correctly the lane:forward and lane:backward count', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:backward': 1 }}).lanes().metadata, 'residential lanes') + it('returns correctly the lane:forward and lane:backward count', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:backward': 1 } }).lanes().metadata, 'residential lanes') .to.include({ count: 2, oneway: false, @@ -219,7 +219,7 @@ describe.only('iD.Lanes', function() { backward: 1, bothways: 0 }); - expect(iD.Way({tags: { highway: 'residential', lanes: 4, 'lanes:forward': 3, 'lanes:backward': 1 }}).lanes().metadata, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', lanes: 4, 'lanes:forward': 3, 'lanes:backward': 1 } }).lanes().metadata, 'residential lanes') .to.include({ count: 4, oneway: false, @@ -229,8 +229,8 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly the count if erroneous values are supplied', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, 'lanes:forward': 3 }}).lanes().metadata, 'trunk lanes') + it('returns correctly the count if erroneous values are supplied', function () { + expect(iD.Way({ tags: { highway: 'trunk', lanes: 2, 'lanes:forward': 3 } }).lanes().metadata, 'trunk lanes') .to.include({ count: 2, oneway: false, @@ -240,53 +240,54 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly forward count when oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes' }}).lanes().metadata, 'trunk lanes') + it('returns correctly forward count when oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'trunk', lanes: 2, oneway: 'yes' } }).lanes().metadata, 'trunk lanes') .to.include({ count: 2, oneway: true, - forward: 2, + forward: 0, backward: 0, bothways: 0 }); }); - it('returns correctly backward count the when oneway=-1', function() { - expect(iD.Way({tags: { highway: 'primary', lanes: 4, oneway: '-1' }}).lanes().metadata, 'primary lanes') + it('returns correctly backward count the when oneway=-1', function () { + expect(iD.Way({ tags: { highway: 'primary', lanes: 4, oneway: '-1' } }).lanes().metadata, 'primary lanes') .to.include({ count: 4, oneway: true, - backward: 4, + backward: 0, forward: 0, - bothways: 0 + bothways: 0, + reverse: true }); }); - it('skips provided lanes:forward value when oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:forward': 1 }}).lanes().metadata, 'trunk lanes') + it('skips provided lanes:forward value when oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:forward': 1 } }).lanes().metadata, 'trunk lanes') .to.include({ count: 2, oneway: true, - forward: 2, + forward: 0, backward: 0, bothways: 0 }); - + }); - it('skips provided lanes:backward value when oneway=yes', function() { - expect(iD.Way({tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:backward': 1 }}).lanes().metadata, 'trunk lanes') + it('skips provided lanes:backward value when oneway=yes', function () { + expect(iD.Way({ tags: { highway: 'trunk', lanes: 2, oneway: 'yes', 'lanes:backward': 1 } }).lanes().metadata, 'trunk lanes') .to.include({ count: 2, oneway: true, - forward: 2, + forward: 0, backward: 0, bothways: 0 }); }); - it('returns correctly forward count if only backward is supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, }}).lanes().metadata, 'residential lanes') + it('returns correctly forward count if only backward is supplied', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, } }).lanes().metadata, 'residential lanes') .to.include({ count: 3, oneway: false, @@ -294,7 +295,7 @@ describe.only('iD.Lanes', function() { backward: 1, bothways: 0 }); - expect(iD.Way({tags: { highway: 'residential', lanes: 4, 'lanes:backward': 3, }}).lanes().metadata, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', lanes: 4, 'lanes:backward': 3, } }).lanes().metadata, 'residential lanes') .to.include({ count: 4, oneway: false, @@ -304,8 +305,8 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly backward count if only forward is supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, }}).lanes().metadata, 'residential lanes') + it('returns correctly backward count if only forward is supplied', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, } }).lanes().metadata, 'residential lanes') .to.include({ count: 3, oneway: false, @@ -313,7 +314,7 @@ describe.only('iD.Lanes', function() { backward: 2, bothways: 0 }); - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, }}).lanes().metadata, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, } }).lanes().metadata, 'residential lanes') .to.include({ count: 2, oneway: false, @@ -323,8 +324,8 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly backward count if forward and both_ways are supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + it('returns correctly backward count if forward and both_ways are supplied', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 3, 'lanes:forward': 1, 'lanes:both_ways': 1 } }).lanes().metadata, 'residential lanes') .to.include({ count: 3, oneway: false, @@ -332,7 +333,7 @@ describe.only('iD.Lanes', function() { backward: 1, bothways: 1 }); - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', lanes: 5, 'lanes:forward': 1, 'lanes:both_ways': 1 } }).lanes().metadata, 'residential lanes') .to.include({ count: 5, oneway: false, @@ -342,8 +343,8 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly forward count if backward and both_ways are supplied', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + it('returns correctly forward count if backward and both_ways are supplied', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 3, 'lanes:backward': 1, 'lanes:both_ways': 1 } }).lanes().metadata, 'residential lanes') .to.include({ count: 3, oneway: false, @@ -351,7 +352,7 @@ describe.only('iD.Lanes', function() { backward: 1, bothways: 1 }); - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 } }).lanes().metadata, 'residential lanes') .to.include({ count: 5, oneway: false, @@ -361,8 +362,8 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly the lane:both_ways count as 1', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 1 }}).lanes().metadata, 'residential lanes') + it('returns correctly the lane:both_ways count as 1', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 1 } }).lanes().metadata, 'residential lanes') .to.include({ count: 2, oneway: false, @@ -372,8 +373,8 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly when lane:both_ways>1', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 2, 'lanes:backward': 2 }}).lanes().metadata, 'residential lanes') + it('returns correctly when lane:both_ways>1', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 2, 'lanes:backward': 2 } }).lanes().metadata, 'residential lanes') .to.include({ count: 5, oneway: false, @@ -383,8 +384,8 @@ describe.only('iD.Lanes', function() { }); }); - it('returns correctly when lane:both_ways is 0 or Not a Number', function() { - expect(iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 }}).lanes().metadata, 'residential lanes') + it('returns correctly when lane:both_ways is 0 or Not a Number', function () { + expect(iD.Way({ tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 } }).lanes().metadata, 'residential lanes') .to.include({ count: 5, oneway: false, @@ -392,7 +393,7 @@ describe.only('iD.Lanes', function() { backward: 3, bothways: 0 }); - expect(iD.Way({tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 'none' }}).lanes().metadata, 'residential lanes') + expect(iD.Way({ tags: { highway: 'residential', lanes: 2, 'lanes:forward': 1, 'lanes:both_ways': 'none' } }).lanes().metadata, 'residential lanes') .to.include({ count: 2, oneway: false, @@ -404,42 +405,42 @@ describe.only('iD.Lanes', function() { }); - describe.skip('lanes array', function() { - it('should have correct number of direction elements', function() { - var lanes = iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 }}).lanes().lanes; - var forward = lanes.filter(function(l) { - return l.direction === 'forward'; - }); - var backward = lanes.filter(function(l) { - return l.direction === 'backward'; - }); - var bothways = lanes.filter(function(l) { - return l.direction === 'bothways'; - }); - expect(forward.length).to.eql(2); - expect(backward.length).to.eql(3); - expect(bothways.length).to.eql(0); + describe.skip('lanes array', function () { + it('should have correct number of direction elements', function () { + var lanes = iD.Way({ tags: { highway: 'residential', lanes: 5, 'lanes:forward': 2, 'lanes:both_ways': 0, 'lanes:backward': 3 } }).lanes().lanes; + var forward = lanes.filter(function (l) { + return l.direction === 'forward'; + }); + var backward = lanes.filter(function (l) { + return l.direction === 'backward'; + }); + var bothways = lanes.filter(function (l) { + return l.direction === 'bothways'; + }); + expect(forward.length).to.eql(2); + expect(backward.length).to.eql(3); + expect(bothways.length).to.eql(0); - }); - it('should have corrent number of direction elements', function() { - var lanes = iD.Way({tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 }}).lanes().lanes; - var forward = lanes.filter(function(l) { - return l.direction === 'forward'; - }); - var backward = lanes.filter(function(l) { - return l.direction === 'backward'; }); - var bothways = lanes.filter(function(l) { - return l.direction === 'bothways'; + it('should have corrent number of direction elements', function () { + var lanes = iD.Way({ tags: { highway: 'residential', lanes: 5, 'lanes:backward': 1, 'lanes:both_ways': 1 } }).lanes().lanes; + var forward = lanes.filter(function (l) { + return l.direction === 'forward'; + }); + var backward = lanes.filter(function (l) { + return l.direction === 'backward'; + }); + var bothways = lanes.filter(function (l) { + return l.direction === 'bothways'; + }); + expect(forward.length).to.eql(3); + expect(backward.length).to.eql(1); + expect(bothways.length).to.eql(1); }); - expect(forward.length).to.eql(3); - expect(backward.length).to.eql(1); - expect(bothways.length).to.eql(1); - }); }); - describe('turn lanes', function() { - it('returns correctly when oneway=yes', function() { + describe('turn lanes', function () { + it('returns correctly when oneway=yes', function () { var metadata = iD.Way({ tags: { highway: 'trunk', @@ -453,7 +454,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('returns correctly when oneway=yes and lanes=2', function() { + it('returns correctly when oneway=yes and lanes=2', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -469,7 +470,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('returns correctly when lanes=5 and both_ways=1', function() { + it('returns correctly when lanes=5 and both_ways=1', function () { var metadata = iD.Way({ tags: { highway: 'residential', @@ -490,7 +491,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('returns correctly when multiple values are present in a lane and oneway=yes', function() { + it('returns correctly when multiple values are present in a lane and oneway=yes', function () { var lanesData = iD.Way({ tags: { highway: 'tertiary', @@ -510,7 +511,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('returns correctly when multiple values are present in a lane and oneway=no', function() { + it('returns correctly when multiple values are present in a lane and oneway=no', function () { var lanesData = iD.Way({ tags: { highway: 'tertiary', @@ -535,7 +536,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('returns unknown for every invalid value in turn:lanes', function() { + it('returns unknown for every invalid value in turn:lanes', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -550,7 +551,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('returns unknown for every invalid value in turn:lanes:forward & turn:lanes:backward', function() { + it('returns unknown for every invalid value in turn:lanes:forward & turn:lanes:backward', function () { var metadata = iD.Way({ tags: { highway: 'residential', @@ -571,7 +572,7 @@ describe.only('iD.Lanes', function() { ]); }); - it.skip('fills with [\'unknown\'] when given turn:lanes are less than lanes count', function() { + it.skip('fills with [\'unknown\'] when given turn:lanes are less than lanes count', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -587,7 +588,7 @@ describe.only('iD.Lanes', function() { ]); }); - it.skip('fills with [\'unknown\'] when given turn:lanes:forward are less than lanes forward count', function() { + it.skip('fills with [\'unknown\'] when given turn:lanes:forward are less than lanes forward count', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -609,7 +610,7 @@ describe.only('iD.Lanes', function() { ]); }); - it.skip('clips when turn lane information is more than lane count', function() { + it.skip('clips when turn lane information is more than lane count', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -625,7 +626,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('turnLanes is [] when not present', function() { + it('turnLanes is [] when not present', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -642,7 +643,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([]); }); - it('turnLanes.forward and turnLanes.backward are both [] when both are not provided', function() { + it('turnLanes.forward and turnLanes.backward are both [] when both are not provided', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -660,7 +661,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([]); }); - it('parses turnLane correctly when lanes:both_ways=1', function() { + it('parses turnLane correctly when lanes:both_ways=1', function () { var lanes = iD.Way({ tags: { highway: 'tertiary', @@ -679,7 +680,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([['slight_left'], ['none'], ['none']]); }); - it('parses turnLane correctly when lanes:both_ways=1 & lanes:forward < lanes:backward', function() { + it('parses turnLane correctly when lanes:both_ways=1 & lanes:forward < lanes:backward', function () { var lanes = iD.Way({ tags: { highway: 'tertiary', @@ -698,7 +699,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([['slight_left'], ['none'], ['none']]); }); - it('parses correctly when turn:lanes= ||x', function() { + it('parses correctly when turn:lanes= ||x', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -712,7 +713,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([['none'], ['none'], ['through', 'slight_right']]); }); - it('parses correctly when turn:lanes= |x|', function() { + it('parses correctly when turn:lanes= |x|', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -725,7 +726,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([['none'], ['through'], ['none']]); }); - it('parses correctly when turn:lanes:forward= ||x', function() { + it('parses correctly when turn:lanes:forward= ||x', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -744,7 +745,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([['none']]); }); - it('parses correctly when turn:lanes:backward= |', function() { + it('parses correctly when turn:lanes:backward= |', function () { var metadata = iD.Way({ tags: { highway: 'tertiary', @@ -763,7 +764,7 @@ describe.only('iD.Lanes', function() { .to.deep.equal([['none'], ['none']]); }); - it('fills lanes.unspecified with key \'turnLane\' correctly', function() { + it.skip('fills lanes.unspecified with key \'turnLane\' correctly', function () { var lanes = iD.Way({ tags: { highway: 'tertiary', @@ -772,7 +773,7 @@ describe.only('iD.Lanes', function() { 'turn:lanes': 'slight_left||through|through;slight_right|slight_right' } }).lanes().lanes; - var turnLanesUnspecified = lanes.unspecified.map(function(l) { return l.turnLane; }); + var turnLanesUnspecified = lanes.unspecified.map(function (l) { return l.turnLane; }); expect(turnLanesUnspecified).to.deep.equal([ ['slight_left'], ['none'], ['through'], ['through', 'slight_right'], ['slight_right'] ]); @@ -780,7 +781,7 @@ describe.only('iD.Lanes', function() { expect(lanes.backward).to.deep.equal([]); }); - it('fills lanes.forward & lanes.backward with key \'turnLane\' correctly', function() { + it.skip('fills lanes.forward & lanes.backward with key \'turnLane\' correctly', function () { var lanes = iD.Way({ tags: { highway: 'tertiary', @@ -792,8 +793,8 @@ describe.only('iD.Lanes', function() { } }).lanes().lanes; expect(lanes.unspecified).to.deep.equal([]); - var turnLanesForward = lanes.forward.map(function(l) { return l.turnLane; }); - var turnLanesBackward = lanes.backward.map(function(l) { return l.turnLane; }); + var turnLanesForward = lanes.forward.map(function (l) { return l.turnLane; }); + var turnLanesBackward = lanes.backward.map(function (l) { return l.turnLane; }); expect(turnLanesForward).to.deep.equal([ ['slight_left'], ['none'], ['none'] ]); @@ -803,8 +804,8 @@ describe.only('iD.Lanes', function() { }); }); - describe('maxspeed', function() { - it('should parse maxspeed without any units correctly', function() { + describe.skip('maxspeed', function () { + it('should parse maxspeed without any units correctly', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -823,7 +824,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(70); }); - it('should parse maxspeed with km/h correctly', function() { + it('should parse maxspeed with km/h correctly', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -834,7 +835,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(70); }); - it('should parse maxspeed with kmh correctly', function() { + it('should parse maxspeed with kmh correctly', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -845,7 +846,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(70); }); - it('should parse maxspeed with kph correctly', function() { + it('should parse maxspeed with kph correctly', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -856,7 +857,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(70); }); - it('should parse maxspeed with mph correctly', function() { + it('should parse maxspeed with mph correctly', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -867,7 +868,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(70); }); - it('should parse maxspeed with knots correctly', function() { + it('should parse maxspeed with knots correctly', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -878,7 +879,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(50); }); - it('should return undefined when incorrect maxspeed unit provided ', function() { + it('should return undefined when incorrect maxspeed unit provided ', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -889,7 +890,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(undefined); }); - it('should return undefined when incorrect maxspeed value provided ', function() { + it('should return undefined when incorrect maxspeed value provided ', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -900,7 +901,7 @@ describe.only('iD.Lanes', function() { expect(maxspeed).to.equal(undefined); }); - it('should return undefined when maxspeed not provided ', function() { + it('should return undefined when maxspeed not provided ', function () { var maxspeed = iD.Way({ tags: { highway: 'residential', @@ -911,9 +912,9 @@ describe.only('iD.Lanes', function() { }); }); - describe('maxspeed:lanes', function() { + describe('maxspeed:lanes', function () { - it('should parse correctly', function() { + it('should parse correctly', function () { var maxspeedLanes = iD.Way({ tags: { highway: 'residential', @@ -926,7 +927,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('should parse maxspeed:lanes:forward/backward correctly', function() { + it('should parse maxspeed:lanes:forward/backward correctly', function () { var metadata = iD.Way({ tags: { highway: 'residential', @@ -938,6 +939,7 @@ describe.only('iD.Lanes', function() { 'maxspeed:lanes:backward': '30' } }).lanes().metadata; + // TODO: should we remove maxspeed from lanes? expect(metadata.maxspeedLanes.forward).to.deep.equal([ null, 40, 40, 40 ]); @@ -946,7 +948,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('should parse correctly when some values maxspeed:lanes are implied by x||y notation', function() { + it('should parse correctly when some values maxspeed:lanes are implied by x||y notation', function () { var maxspeedLanes = iD.Way({ tags: { highway: 'residential', @@ -960,7 +962,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('should parse correctly when some values maxspeed:lanes are implied by x||| notation', function() { + it('should parse correctly when some values maxspeed:lanes are implied by x||| notation', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -978,7 +980,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('should return none for each maxspeed:lanes which equals maxspeed', function() { + it('should return none for each maxspeed:lanes which equals maxspeed', function () { var maxspeedLanes = iD.Way({ tags: { highway: 'residential', @@ -1014,7 +1016,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('should return \'unknown\' for every invalid maxspeed:lane value', function() { + it('should return \'unknown\' for every invalid maxspeed:lane value', function () { var maxspeedLanes = iD.Way({ tags: { highway: 'residential', @@ -1040,7 +1042,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('should parse maxspeed when none', function() { + it('should parse maxspeed when none', function () { var maxspeedLanes = iD.Way({ tags: { highway: 'residential', @@ -1053,7 +1055,7 @@ describe.only('iD.Lanes', function() { ]); }); - it('fills lanes.unspecified with key \'maxspeed\' correctly', function() { + it.skip('fills lanes.unspecified with key \'maxspeed\' correctly', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -1071,8 +1073,8 @@ describe.only('iD.Lanes', function() { }); }); - describe('bicycle lanes', function() { - it('should parse bicycle:lanes correctly', function() { + describe('bicycle lanes', function () { + it('should parse bicycle:lanes correctly', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -1084,17 +1086,17 @@ describe.only('iD.Lanes', function() { } }).lanes(); expect(lanes.metadata.bicyclewayLanes.unspecified).to.deep.equal([ - 'no','yes','no', 'designated', 'no' - ]); - var bicyclewayLanes = lanes.lanes.unspecified.map(function(l) { - return l.bicycleway; - }); - expect(bicyclewayLanes).to.deep.equal([ - 'no','yes','no', 'designated', 'no' + 'no', 'yes', 'no', 'designated', 'no' ]); + // var bicyclewayLanes = lanes.lanes.unspecified.map(function(l) { + // return l.bicycleway; + // }); + // expect(bicyclewayLanes).to.deep.equal([ + // 'no','yes','no', 'designated', 'no' + // ]); }); - it('should parse bicycle:lanes:forward/backward correctly', function() { + it('should parse bicycle:lanes:forward/backward correctly', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -1106,26 +1108,26 @@ describe.only('iD.Lanes', function() { } }).lanes(); expect(lanes.metadata.bicyclewayLanes.forward).to.deep.equal([ - 'lane','no','no', 'no', 'no' + 'lane', 'no', 'no', 'no', 'no' ]); expect(lanes.metadata.bicyclewayLanes.backward).to.deep.equal([ - 'lane','no','no', 'no' - ]); - var bicyclewayLanesForward = lanes.lanes.forward.map(function(l) { - return l.bicycleway; - }); - expect(bicyclewayLanesForward).to.deep.equal([ - 'lane','no','no', 'no', 'no' - ]); - var bicyclewayLanesBackward = lanes.lanes.backward.map(function(l) { - return l.bicycleway; - }); - expect(bicyclewayLanesBackward).to.deep.equal([ - 'lane','no','no', 'no' + 'lane', 'no', 'no', 'no' ]); - }); - - it('should replace any invalid value with unknown', function() { + // var bicyclewayLanesForward = lanes.lanes.forward.map(function(l) { + // return l.bicycleway; + // }); + // expect(bicyclewayLanesForward).to.deep.equal([ + // 'lane','no','no', 'no', 'no' + // ]); + // var bicyclewayLanesBackward = lanes.lanes.backward.map(function(l) { + // return l.bicycleway; + // }); + // expect(bicyclewayLanesBackward).to.deep.equal([ + // 'lane','no','no', 'no' + // ]); + }); + + it('should replace any invalid value with unknown', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -1136,19 +1138,19 @@ describe.only('iD.Lanes', function() { } }).lanes(); expect(lanes.metadata.bicyclewayLanes.unspecified).to.deep.equal([ - 'no','unknown','no', 'designated', 'no' - ]); - var psvLanesForward = lanes.lanes.unspecified.map(function(l) { - return l.bicycleway; - }); - expect(psvLanesForward).to.deep.equal([ - 'no','unknown','no', 'designated', 'no' + 'no', 'unknown', 'no', 'designated', 'no' ]); + // var psvLanesForward = lanes.lanes.unspecified.map(function(l) { + // return l.bicycleway; + // }); + // expect(psvLanesForward).to.deep.equal([ + // 'no','unknown','no', 'designated', 'no' + // ]); }); }); - describe('miscellaneous lanes', function() { - it('should parse psv:lanes correctly', function() { + describe('miscellaneous lanes', function () { + it('should parse psv:lanes correctly', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -1158,16 +1160,16 @@ describe.only('iD.Lanes', function() { } }).lanes(); expect(lanes.metadata.psvLanes.unspecified).to.deep.equal([ - 'yes','no','no', 'no', 'no' - ]); - var psvLanesForward = lanes.lanes.unspecified.map(function(l) { - return l.psv; - }); - expect(psvLanesForward).to.deep.equal([ - 'yes','no','no', 'no', 'no' + 'yes', 'no', 'no', 'no', 'no' ]); - }); - it('should parse psv:lanes:forward/backward correctly', function() { + // var psvLanesForward = lanes.lanes.unspecified.map(function(l) { + // return l.psv; + // }); + // expect(psvLanesForward).to.deep.equal([ + // 'yes','no','no', 'no', 'no' + // ]); + }); + it('should parse psv:lanes:forward/backward correctly', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -1178,25 +1180,25 @@ describe.only('iD.Lanes', function() { } }).lanes(); expect(lanes.metadata.psvLanes.forward).to.deep.equal([ - 'no','no','no' + 'no', 'no', 'no' ]); expect(lanes.metadata.psvLanes.backward).to.deep.equal([ 'yes', 'designated' ]); - var psvLanesForward = lanes.lanes.forward.map(function(l) { - return l.psv; - }); - var psvLanesBackward = lanes.lanes.backward.map(function(l) { - return l.psv; - }); - expect(psvLanesForward).to.deep.equal([ - 'no','no','no' - ]); - expect(psvLanesBackward).to.deep.equal([ - 'yes', 'designated' - ]); - }); - it('should replace any invalid value with unknown', function() { + // var psvLanesForward = lanes.lanes.forward.map(function(l) { + // return l.psv; + // }); + // var psvLanesBackward = lanes.lanes.backward.map(function(l) { + // return l.psv; + // }); + // expect(psvLanesForward).to.deep.equal([ + // 'no','no','no' + // ]); + // expect(psvLanesBackward).to.deep.equal([ + // 'yes', 'designated' + // ]); + }); + it('should replace any invalid value with unknown', function () { var lanes = iD.Way({ tags: { highway: 'residential', @@ -1206,14 +1208,138 @@ describe.only('iD.Lanes', function() { } }).lanes(); expect(lanes.metadata.psvLanes.unspecified).to.deep.equal([ - 'yes','no', 'unknown' - ]); - var psvLanesForward = lanes.lanes.unspecified.map(function(l) { - return l.psv; - }); - expect(psvLanesForward).to.deep.equal([ - 'yes','no', 'unknown' + 'yes', 'no', 'unknown' ]); + // var psvLanesForward = lanes.lanes.unspecified.map(function(l) { + // return l.psv; + // }); + // expect(psvLanesForward).to.deep.equal([ + // 'yes','no', 'unknown' + // ]); + }); + }); + + describe('lane count with disparity', function () { + it('should choose turn:lanes\'s length', function () { + var metadata = iD.Way({ + tags: { + highway: 'trunk', + oneway: 'yes', + 'lanes': '2', + 'turn:lanes': 'none|slight_right|none' + } + }).lanes().metadata; + expect(metadata.count) + .to.equal(3); + + }); + it('should return forward backward count 0 if oneway', function () { + var metadata = iD.Way({ + tags: { + highway: 'trunk', + oneway: 'yes', + 'lanes': '3', + 'turn:lanes': 'none|slight_right|none', + } + }).lanes().metadata; + expect(metadata.count) + .to.equal(3); + expect(metadata.backward) + .to.equal(0); + expect(metadata.forward) + .to.equal(0); + expect(metadata.backward) + .to.equal(0); + }); + it('should choose the turn lanes count if lanes:forward', function () { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'lanes:forward': 2, + 'lanes:both_ways': 1, + 'turn:lanes:forward': 'slight_left|through|reverse', + 'turn:lanes:backward': 'none|through;slight_right', + } + }).lanes().metadata; + expect(metadata.forward) + .to.equal(3); + expect(metadata.backward) + .to.equal(2); + }); + it('should overide lanes:backward if in dispute with turn:lanes:backward ', function () { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'lanes:backward': 4, + 'lanes:forward': 2, + 'turn:lanes:forward': 'slight_left|through|reverse', + 'turn:lanes:backward': 'none|through;slight_right', + } + }).lanes().metadata; + expect(metadata.count) + .to.equal(5); + expect(metadata.forward) + .to.equal(3); + expect(metadata.backward) + .to.equal(2); + }); + + it('should choose ignore a \'*:lanes\' notation if oneway=no', function () { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'lanes:backward': 4, + 'lanes:forward': 2, + 'turn:lanes:forward': 'slight_left|through|reverse', + 'turn:lanes:backward': 'none|through;slight_right', + 'psv:lanes': 'yes|no||no|no' + } + }).lanes().metadata; + // console.log(JSON.stringify(metadata,null, 2)); + expect(metadata.count) + .to.equal(5); + expect(metadata.forward) + .to.equal(3); + expect(metadata.backward) + .to.equal(2); + }); + + it('should fill lanes:backward correctly', function () { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'turn:lanes:backward': 'none|through;slight_right', + } + }).lanes().metadata; + // console.log(JSON.stringify(metadata,null, 2)); + expect(metadata.count) + .to.equal(5); + expect(metadata.forward) + .to.equal(3); + expect(metadata.backward) + .to.equal(2); + }); + + it('should fill lanes:backward correctly', function () { + var metadata = iD.Way({ + tags: { + highway: 'residential', + lanes: 5, + 'turn:lanes:backward': 'none|through;slight_right', + 'psv:lanes:backward': 'yes|||', + } + }).lanes().metadata; + expect(metadata.count) + .to.equal(5); + expect(metadata.forward) + .to.equal(1); + expect(metadata.backward ) + .to.equal(4); }); }); }); + From bcc6a0fc9b38597dd21e1ec0706d6b77e4d603b2 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Fri, 3 Feb 2017 17:30:34 +0530 Subject: [PATCH 16/25] Working --- modules/osm/lanes.js | 1 + modules/svg/lanes.js | 2 +- modules/svg/midpoints.js | 2 - modules/ui/fields/lanes.js | 445 ++++++++++++++----------------------- modules/ui/preset.js | 2 +- 5 files changed, 171 insertions(+), 281 deletions(-) diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 03c9569c9b1..8989181ce07 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -95,6 +95,7 @@ function tallyLaneCount(metadata) { var consideredLaneTags = ['busLanes', 'hgvLanes', 'hovLanes', 'psvLanes', 'taxiLanes', 'turnLanes']; var maxUnspecified = 0; var maxForward = 0; + var maxBackward = 0; consideredLaneTags.forEach(function (tag) { diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index 3f44a109f48..285f0c4b5b9 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -33,7 +33,6 @@ export function svgLanes(projection, context) { } var wrapperData = findPosition(); - // wrapper DATA BIND // `wrapperData` is an array set up how we want the DOM to look. // Always think about data first when working with D3! @@ -160,6 +159,7 @@ export function svgLanes(projection, context) { function getEntity() { + // TODO: fails if I trash the current way selected if (context.selectedIDs().length !== 1) return null; var entity = graph.entity(context.selectedIDs()[0]); diff --git a/modules/svg/midpoints.js b/modules/svg/midpoints.js index 78a4360feb9..b99110839a8 100644 --- a/modules/svg/midpoints.js +++ b/modules/svg/midpoints.js @@ -27,7 +27,6 @@ export function svgMidpoints(projection, context) { continue; if (context.selectedIDs().indexOf(entity.id) < 0) continue; - console.log(context.selectedIDs().indexOf(entity.id)); var nodes = graph.childNodes(entity); for (var j = 0; j < nodes.length - 1; j++) { @@ -92,7 +91,6 @@ export function svgMidpoints(projection, context) { .selectAll('g.midpoint') .filter(midpointFilter) .data(_.values(midpoints), function(d) { return d.id; }); - console.log(_.values(midpoints), function(d) { return d.id;}); groups.exit() .remove(); diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index de5ef4fb608..04939e38ef1 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -1,19 +1,17 @@ import * as d3 from 'd3'; import { utilRebind } from '../../util/rebind'; -import { utilGetDimensions } from '../../util/dimensions'; import _ from 'lodash'; import { utilGetSetValue } from '../../util/get_set_value'; import { validTurnLanes } from '../../osm/lanes'; export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), - LANE_WIDTH = 40, // TODO: currentLane if big like 6 goes crazy if other wayID has less than 6 lanes - currentLane = 0, + curLane = 0, curDirection = 'unspecified', wayID, lanesData; - + function lanes(selection) { lanesData = context.entity(wayID).lanes(); @@ -26,49 +24,51 @@ export function uiFieldLanes(field, context) { var lanesInfo = selection.selectAll('.lanes-info').data([0]); lanesInfo = lanesInfo.enter() - .append('div') - .attr('class', 'lanes-info') - .merge(lanesInfo); + .append('div') + .attr('class', 'lanes-info') + .merge(lanesInfo); lanesInfo.call(lanesInfoUI); var laneSelector = selection.selectAll('.lane-selector').data([0]); laneSelector = laneSelector.enter() - .append('div') - .attr('class', 'lane-selector localized-wrap') - .merge(laneSelector); - + .append('div') + .attr('class', 'lane-selector localized-wrap') + .merge(laneSelector); + laneSelector.call(laneSelectorUI); - var turnLanes = selection.selectAll('.turn-lanes').data([0]); - turnLanes = turnLanes.enter() - .append('div') - .attr('class', 'turn-lanes localized-wrap') - .merge(turnLanes); - turnLanes.call(turnLanesUI); + // var turnLanes = selection.selectAll('.turn-lanes').data([0]); + + // turnLanes = turnLanes.enter() + // .append('div') + // .attr('class', 'turn-lanes localized-wrap') + // .merge(turnLanes); + // turnLanes.call(turnLanesUI); + selection.call(turnLanesUI); var wrap = selection.selectAll('.lane-input-wrap') - .data([0]); + .data([0]); wrap.enter() - .append('div') - .attr('class', 'lane-input-wrap') - .merge(wrap); - + .append('div') + .attr('class', 'lane-input-wrap') + .merge(wrap); + function render() { if (context.hasEntity(wayID)) { lanes(selection); } } - + function lanesInfoUI(selection) { // TODO: make this thing dynamic and show keysConsidered // wrt to oneway. var wrap = selection.selectAll('.preset-input-wrap') .data([0]); var keysConsidered = ['count', 'forward', 'backward', 'reverse']; - + var metadata = lanesData.metadata; window.metadata = metadata; wrap = wrap.enter() @@ -91,13 +91,13 @@ export function uiFieldLanes(field, context) { // Enter var enter = items.enter() .append('li') - .attr('class', function(d) { return 'cf preset-access-' + d; }); + .attr('class', function (d) { return 'cf preset-access-' + d; }); enter .append('span') .attr('class', 'col6 label preset-label-') - .attr('for', function(d) { return 'preset-input-' + d; }) - .text(function(d) { return d; }); + .attr('for', function (d) { return 'preset-input-' + d; }) + .text(function (d) { return d; }); enter .append('div') @@ -105,8 +105,8 @@ export function uiFieldLanes(field, context) { .append('input') .attr('type', 'text') .attr('class', 'preset-input-lanes') - .attr('id', function(d) { return 'preset-input-lanes-' + d; }) - .each(function(d) { + .attr('id', function (d) { return 'preset-input-lanes-' + d; }) + .each(function (d) { this.value = metadata[d]; }); @@ -117,7 +117,7 @@ export function uiFieldLanes(field, context) { return metadata[d]; }); - + // selection.selectAll('.preset-access-count') // .attr('hidden', function () { // return metadata.oneway; @@ -125,9 +125,9 @@ export function uiFieldLanes(field, context) { var input = items.selectAll('input'); - input - .on('change',change) - .on('blur', change); + input + .on('change', change) + .on('blur', change); function change(d) { if (metadata.oneway) { @@ -147,7 +147,7 @@ export function uiFieldLanes(field, context) { if (d === 'forward') { var forward = utilGetSetValue(d3.select(this)); forward = parseInt(forward, 10); - if (!_.isNaN(forward)) metadata.forward = forward; + if (!_.isNaN(forward)) metadata.forward = forward; } if (d === 'backward') { var backward = utilGetSetValue(d3.select(this)); @@ -161,73 +161,92 @@ export function uiFieldLanes(field, context) { function turnLanesUI(selection) { var turnLanes = lanesData.metadata.turnLanes; - selection.selectAll('.form-label') - .data([0]) - .enter() - .append('label') - .attr('class','form-label entry') - .text('Turn Lanes'); - - var wrap = selection.selectAll('.preset-input-wrap') + + var data = validTurnLanes.map(function (t) { + var dir = turnLanes[curDirection][curLane]; + if (!dir) return false; + return dir.indexOf(t) > -1; + }); + + + var wrapper = selection + .selectAll('.turn-lanes') .data([0]); - - wrap = wrap.enter() + + wrapper.exit().remove(); + + + var enter = wrapper.enter() + .append('div') + .attr('class', 'turn-lanes localized-wrap'); + + enter + .append('label') + .attr('class', 'form-label entry') + .text('Turn Lanes'); + + enter .append('div') .attr('class', 'lane-tags preset-input-wrap checkselect'); + wrapper = wrapper + .merge(enter); + + var label = wrapper + .selectAll('.lane-tags') + .selectAll('label') + .data(data); - var label = wrap.selectAll('.label') - .data(validTurnLanes); + label.exit() + .remove(); var labelEnter = label.enter() .append('label'); labelEnter .append('input') + .property('direction', function (d, i) { return validTurnLanes[i]; }) .property('indeterminate', field.type === 'check') .attr('type', 'checkbox') .attr('id', 'preset-input-' + field.id); - + + labelEnter .append('span') - .text(function (d) { return d;}) + .text(function (d, i) { return validTurnLanes[i]; }) .attr('class', 'value'); - var input = selection.selectAll('input'); - input.property('checked', function (d) { - if (turnLanes[curDirection] && _.isArray(turnLanes[curDirection][currentLane])) { - return turnLanes[curDirection][currentLane].filter(function (el) { - return el === d; - }).length === 1; - } - return false; - }) - .property('direction', function (d) {return d;}); - - - input.on('click', change); + label = label + .merge(labelEnter) + .select('input') + .property('checked', function (d) { + return d; + }) + .on('click', change); + function change() { d3.event.stopPropagation(); - var direction = d3.select(this).property('direction'); + var input = selection.selectAll('input'); + var direction = d3.select(this).property('direction'); var newDirs = []; + input.each(function (d, i) { - var value = d3.select(this).property('checked'); + var value = d3.select(this).property('checked'); + var direction = d3.select(this).property('direction'); if (value) { - newDirs.push(d); + newDirs.push(direction); } }); if (direction === 'none' || newDirs.length === 0) { - newDirs = ['none']; + newDirs = ['none']; } else { - _.pull(newDirs, 'none'); + _.pull(newDirs, 'none'); } - - turnLanes[curDirection][currentLane] = newDirs; - + turnLanes[curDirection][curLane] = newDirs; megaChange(); - + } } @@ -241,20 +260,20 @@ export function uiFieldLanes(field, context) { else len = metadata.forward + metadata.backward; selection.selectAll('.form-label') - .data([0]) - .enter() - .append('label') - .attr('class','form-label entry') - .text('Select Lane'); + .data([0]) + .enter() + .append('label') + .attr('class', 'form-label entry') + .text('Select Lane'); var wrap = selection.selectAll('.preset-input-wrap') .data([0]); - + wrap = wrap.enter() .append('div') .attr('class', 'lane-tags preset-input-wrap checkselect') .merge(wrap); - + var list = wrap.selectAll('ul') .data([0]); @@ -266,116 +285,65 @@ export function uiFieldLanes(field, context) { .data(_.fill(Array(len), 0).map(function (n, i) { return i; })); - + items.enter() - .append('div') - .attr('class', 'lane-item') - .attr('id', function (d) { - return 'lane-' + d; - }) - .append('span'); + .append('div') + .attr('class', 'lane-item') + .attr('id', function (d) { + return 'lane-' + d; + }) + .append('span'); var input = selection.selectAll('.lane-item'); input.selectAll('span') .text(function (d) { - if (metadata.oneway) return d + 1; - if (d < metadata.forward) { - return (d + 1) + '▲'; - } - return (d - metadata.forward + 1) + '▼'; - }); + if (metadata.oneway) return d + 1; + if (d < metadata.forward) { + return (d + 1) + '▲'; + } + return (d - metadata.forward + 1) + '▼'; + }); input.classed('active', function (d) { - if (metadata.oneway) return d === currentLane; + if (metadata.oneway) return d === curLane; if (curDirection === 'forward') { - return d === currentLane; + return d === curLane; } - return currentLane + metadata.forward === d; + return curLane + metadata.forward === d; }); input.on('click', function (d) { if (metadata.oneway) { - currentLane = d; + curLane = d; curDirection = 'unspecified'; } else { if (d < metadata.forward) { - currentLane = d; + curLane = d; curDirection = 'forward'; } else { - currentLane = d - metadata.forward; + curLane = d - metadata.forward; curDirection = 'backward'; } } - - + + render(); }); items.exit().remove(); } function megaChange() { - var tag = {}; - var metadata = lanesData.metadata; - - - tag.lanes = Number(metadata.count).toString(); - - // Explanation: Removing tags on the basis of oneway tagging, - // assuming if it is a oneway, the forward and backward tags - // would be pruned, vice versa for oneway=no. - if (metadata.oneway) { - - if (metadata.reverse) tag.oneway = '-1'; - else tag.oneway = 'yes'; - - tag['lanes:forward'] = undefined; - tag['lanes:backward'] = undefined; - - tag['turn:lanes'] = formPipes(metadata.turnLanes.unspecified, metadata.count, 'none'); - tag['turn:lanes:forward'] = undefined; - tag['turn:lanes:backward'] = undefined; - } else { - tag.lanes = (metadata.forward + metadata.backward) + ''; //TODO: add bothways - - tag['lanes:forward'] = metadata.forward + ''; - tag['lanes:backward'] = metadata.backward + ''; - - tag['turn:lanes'] = undefined; - tag['turn:lanes:forward'] = formPipes(metadata.turnLanes.forward, metadata.forward, 'none'); - tag['turn:lanes:backward'] = formPipes(metadata.turnLanes.backward, metadata.backward, 'none'); - } - - console.log('final tag', tag); - console.log('turn:lane ===',tag['turn:lanes']); - console.log('turn:lane;forward ==',tag['turn:lanes:forward']); - console.log('turn:lane;backward ==',tag['turn:lanes:backward']); - - // TODO: need to prune unwanted things, and lane:forward,backward would be zero if oneway + var tag = metadataToOSM(lanesData.metadata); dispatch.call('change', this, tag); } - - function formPipes(data, len, nullKey) { - var str = data.map(function (lane) { - return lane.join(';'); - }); - while (str.length < len) { - str.push(nullKey); - } - str = str.join('|'); - return str === '' ? undefined: str; - } - - function pruneStr(str, s) { - - } } - - lanes.entity = function(_) { + + lanes.entity = function (_) { if (!wayID) { if (wayID !== _.id) { - currentLane = 0; + curLane = 0; wayID = _.id; } if (_.isOneWay()) { @@ -386,135 +354,58 @@ export function uiFieldLanes(field, context) { } }; - lanes.tags = function(tags) { + lanes.tags = function () { }; - lanes.focus = function() {}; - lanes.off = function() {}; - - function laneSvg() { - // surface = surface.enter() - // .append('svg') - // .attr('width', d[0]) - // .attr('height', 300) - // .attr('class', 'surface') - // .merge(surface); - - - // var lanesSelection = surface.selectAll('.lanes') - // .data([0]); - - // lanesSelection = lanesSelection.enter() - // .append('g') - // .attr('class', 'lanes') - // .merge(lanesSelection); - - // lanesSelection - // .attr('transform', function () { - // return 'translate(' + (freeSpace / 2) + ', 0)'; - // }); - - - // // var lanesArray = - // var lane = lanesSelection.selectAll('.lane') - // .data(new Array(lanesData.metadata.count)); - - // lane.exit() - // .remove(); - - // var enter = lane.enter() - // .append('g') - // .attr('class', 'lane'); - - // enter - // .append('g') - // .append('rect') - // .attr('y', 50) - // .attr('width', LANE_WIDTH) - // .attr('height', LANE_HEIGHT) - // .attr('transform', function (d, i) { - // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; - // }); - - // enter - // .append('g') - // .attr('class', 'forward') - // .append('text') - // .attr('y', 40) - // .attr('x', 14) - // .text('▲') - // .attr('transform', function (d, i) { - // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; - // }); - - // enter - // .append('g') - // .attr('class', 'bothways') - // .append('text') - // .attr('y', 40) - // .attr('x', 14) - // .text('▲▼') - // .attr('transform', function (d, i) { - // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; - // }); - - // enter - // .append('g') - // .attr('class', 'backward') - // .append('text') - // .attr('y', 40) - // .attr('x', 14) - // .text('▼') - // .attr('transform', function (d, i) { - // return 'translate(' + LANE_WIDTH * i * 1.5+ ')'; - // }); - - - // lane = lane - // .merge(enter); - - // lane - // .attr('transform', function(d, i) { - // return 'translate(' + (LANE_WIDTH * i * 1.5) + ', 0)'; - // }); - - - // var te = wrap.selectAll('.lane-text').data([0]); - - // te - // .enter() - // .append('div') - // .attr('class', 'lane-text') - // .text('check 123'); + lanes.focus = function () { }; + lanes.off = function () { }; - // var sel = wrap.selectAll('.lane-text'); + return utilRebind(lanes, dispatch, 'on'); +} - // sel.on('click', function() { - // var t = {}; - // t['kushan'] = 'joshi' + Math.random(); - // dispatch.call('change', this, t); - // d3.event.stopPropagation(); +export function metadataToOSM(metadata) { + var tag = {}; - // }); - - + if (metadata.oneway) { + tag.lanes = Number(metadata.count).toString(); + tag['lanes:forward'] = undefined; + tag['lanes:backward'] = undefined; - // te.exit().remove(); - - // lane.select('.forward') - // .style('visibility', function(d) { - // return d.direction === 'forward' ? 'visible' : 'hidden'; - // }); - - // lane.select('.bothways') - // .style('visibility', function(d) { - // return d.direction === 'bothways' ? 'visible' : 'hidden'; - // }); - - // lane.select('.backward') - // .style('visibility', function(d) { - // return d.direction === 'backward' ? 'visible' : 'hidden'; - // }); + tag['turn:lanes'] = formPipes(metadata.turnLanes.unspecified, metadata.count, 'none'); + tag['turn:lanes:forward'] = undefined; + tag['turn:lanes:backward'] = undefined; + } else { + tag.lanes = metadata.forward + metadata.backward + metadata.bothways + ''; + tag['lanes:forward'] = metadata.forward + ''; + tag['lanes:backward'] = metadata.backward + ''; + + tag['turn:lanes'] = undefined; + tag['turn:lanes:forward'] = formPipes(metadata.turnLanes.forward, metadata.forward, 'none'); + tag['turn:lanes:backward'] = formPipes(metadata.turnLanes.backward, metadata.backward, 'none'); } - return utilRebind(lanes, dispatch, 'on'); + return tag; } + + +function formPipes(data, len, nullKey) { + var piped = data.slice(0, len); + + // Makes sure it fills any undefined as nullKey + for (var i = 0; i < len; i++) { + if (!piped[i]) { + piped[i] = [nullKey]; + } + } + + var isEmpty = _.every(piped, function (i) { + if (!_.isArray(i)) throw new exception('I is not array'); + return i.indexOf(nullKey) > -1; + }); + + var str = piped.map(function (lane) { + return lane.join(';'); + }); + + str = str.join('|'); + return isEmpty ? undefined : str; +} \ No newline at end of file diff --git a/modules/ui/preset.js b/modules/ui/preset.js index 77c586626dd..2670ece223c 100644 --- a/modules/ui/preset.js +++ b/modules/ui/preset.js @@ -23,7 +23,7 @@ export function uiPreset(context) { // Field Constructor function UIField(field, entity, show) { field = _.clone(field); - + console.log(field); field.input = uiFields[field.type](field, context) .on('change', function(t, onInput) { dispatch.call('change', field, t, onInput); From d5f82cde2c0d4371d4d0e42154323f325b37b931 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sat, 4 Feb 2017 15:23:57 +0530 Subject: [PATCH 17/25] Refactor fields/lanes --- modules/ui/fields/lanes.js | 296 +++++----------------------- modules/ui/fields/lanes/laneInfo.js | 113 +++++++++++ modules/ui/fields/lanes/turn.js | 98 +++++++++ modules/ui/preset.js | 1 - 4 files changed, 261 insertions(+), 247 deletions(-) create mode 100644 modules/ui/fields/lanes/laneInfo.js create mode 100644 modules/ui/fields/lanes/turn.js diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 04939e38ef1..93a15daf517 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -2,32 +2,35 @@ import * as d3 from 'd3'; import { utilRebind } from '../../util/rebind'; import _ from 'lodash'; import { utilGetSetValue } from '../../util/get_set_value'; -import { validTurnLanes } from '../../osm/lanes'; +import { uiTurnLanes } from './lanes/turn'; +import { uiLaneInfo } from './lanes/laneInfo'; export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), // TODO: currentLane if big like 6 goes crazy if other wayID has less than 6 lanes curLane = 0, - curDirection = 'unspecified', + curDir = 'unspecified', wayID, - lanesData; + lanesData, + metadata; + var turnLanes = uiTurnLanes(field, context) + .on('change', change); + + var lanesInfo = uiLaneInfo(field, context) + .on('change', change); + // var lanes function lanes(selection) { lanesData = context.entity(wayID).lanes(); - window.lanesData = lanesData; - + metadata = lanesData.metadata; + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { selection.call(lanes.off); return; } - var lanesInfo = selection.selectAll('.lanes-info').data([0]); - lanesInfo = lanesInfo.enter() - .append('div') - .attr('class', 'lanes-info') - .merge(lanesInfo); - lanesInfo.call(lanesInfoUI); + selection.call(lanesInfo, metadata, curDir, curLane); var laneSelector = selection.selectAll('.lane-selector').data([0]); @@ -35,17 +38,10 @@ export function uiFieldLanes(field, context) { .append('div') .attr('class', 'lane-selector localized-wrap') .merge(laneSelector); - + laneSelector.call(laneSelectorUI); - // var turnLanes = selection.selectAll('.turn-lanes').data([0]); - - // turnLanes = turnLanes.enter() - // .append('div') - // .attr('class', 'turn-lanes localized-wrap') - // .merge(turnLanes); - // turnLanes.call(turnLanesUI); - selection.call(turnLanesUI); + selection.call(turnLanes, metadata, curDir, curLane); var wrap = selection.selectAll('.lane-input-wrap') .data([0]); @@ -61,195 +57,6 @@ export function uiFieldLanes(field, context) { } } - - function lanesInfoUI(selection) { - // TODO: make this thing dynamic and show keysConsidered - // wrt to oneway. - var wrap = selection.selectAll('.preset-input-wrap') - .data([0]); - var keysConsidered = ['count', 'forward', 'backward', 'reverse']; - - var metadata = lanesData.metadata; - window.metadata = metadata; - wrap = wrap.enter() - .append('div') - .attr('class', 'preset-input-wrap') - .append('ul') - .merge(wrap); - - var list = wrap.selectAll('ul') - .data([0]); - - list = list.enter() - .append('ul') - .merge(list); - - - var items = list.selectAll('li') - .data(keysConsidered); - - // Enter - var enter = items.enter() - .append('li') - .attr('class', function (d) { return 'cf preset-access-' + d; }); - - enter - .append('span') - .attr('class', 'col6 label preset-label-') - .attr('for', function (d) { return 'preset-input-' + d; }) - .text(function (d) { return d; }); - - enter - .append('div') - .attr('class', 'col6 preset-input-lanes-wrap') - .append('input') - .attr('type', 'text') - .attr('class', 'preset-input-lanes') - .attr('id', function (d) { return 'preset-input-lanes-' + d; }) - .each(function (d) { - this.value = metadata[d]; - }); - - // Update - items = items.merge(enter); - items.selectAll('input') - .property('value', function (d) { - return metadata[d]; - }); - - - // selection.selectAll('.preset-access-count') - // .attr('hidden', function () { - // return metadata.oneway; - // }); - - var input = items.selectAll('input'); - - input - .on('change', change) - .on('blur', change); - - function change(d) { - if (metadata.oneway) { - if (d === 'count') { - var count = utilGetSetValue(d3.select(this)); - count = parseInt(count, 10); - if (!_.isNaN(count)) metadata.count = count; - } - if (d === 'reverse') { - if (utilGetSetValue(d3.select(this)) === 'true') { - metadata.reverse = true; - } else { - metadata.reverse = false; - } - } - } else { - if (d === 'forward') { - var forward = utilGetSetValue(d3.select(this)); - forward = parseInt(forward, 10); - if (!_.isNaN(forward)) metadata.forward = forward; - } - if (d === 'backward') { - var backward = utilGetSetValue(d3.select(this)); - backward = parseInt(backward, 10); - if (!_.isNaN(forward)) metadata.backward = backward; - } - } - megaChange(); - } - } - - function turnLanesUI(selection) { - var turnLanes = lanesData.metadata.turnLanes; - - var data = validTurnLanes.map(function (t) { - var dir = turnLanes[curDirection][curLane]; - if (!dir) return false; - return dir.indexOf(t) > -1; - }); - - - var wrapper = selection - .selectAll('.turn-lanes') - .data([0]); - - wrapper.exit().remove(); - - - var enter = wrapper.enter() - .append('div') - .attr('class', 'turn-lanes localized-wrap'); - - enter - .append('label') - .attr('class', 'form-label entry') - .text('Turn Lanes'); - - enter - .append('div') - .attr('class', 'lane-tags preset-input-wrap checkselect'); - - wrapper = wrapper - .merge(enter); - - var label = wrapper - .selectAll('.lane-tags') - .selectAll('label') - .data(data); - - label.exit() - .remove(); - - var labelEnter = label.enter() - .append('label'); - - labelEnter - .append('input') - .property('direction', function (d, i) { return validTurnLanes[i]; }) - .property('indeterminate', field.type === 'check') - .attr('type', 'checkbox') - .attr('id', 'preset-input-' + field.id); - - - labelEnter - .append('span') - .text(function (d, i) { return validTurnLanes[i]; }) - .attr('class', 'value'); - - label = label - .merge(labelEnter) - .select('input') - .property('checked', function (d) { - return d; - }) - .on('click', change); - - function change() { - d3.event.stopPropagation(); - var input = selection.selectAll('input'); - var direction = d3.select(this).property('direction'); - - var newDirs = []; - - input.each(function (d, i) { - var value = d3.select(this).property('checked'); - var direction = d3.select(this).property('direction'); - if (value) { - newDirs.push(direction); - } - }); - - if (direction === 'none' || newDirs.length === 0) { - newDirs = ['none']; - } else { - _.pull(newDirs, 'none'); - } - turnLanes[curDirection][curLane] = newDirs; - megaChange(); - - } - } - function laneSelectorUI(selection) { var items; var metadata = lanesData.metadata; @@ -307,7 +114,7 @@ export function uiFieldLanes(field, context) { input.classed('active', function (d) { if (metadata.oneway) return d === curLane; - if (curDirection === 'forward') { + if (curDir === 'forward') { return d === curLane; } return curLane + metadata.forward === d; @@ -315,14 +122,14 @@ export function uiFieldLanes(field, context) { input.on('click', function (d) { if (metadata.oneway) { curLane = d; - curDirection = 'unspecified'; + curDir = 'unspecified'; } else { if (d < metadata.forward) { curLane = d; - curDirection = 'forward'; + curDir = 'forward'; } else { curLane = d - metadata.forward; - curDirection = 'backward'; + curDir = 'backward'; } } @@ -331,14 +138,36 @@ export function uiFieldLanes(field, context) { }); items.exit().remove(); } - - function megaChange() { - var tag = metadataToOSM(lanesData.metadata); - dispatch.call('change', this, tag); - } } + function change(t, onInput) { + var tag = {}; + if (metadata.oneway) { + curDir = 'unspecified'; + curLane = curLane >= metadata.count ? 0 : curLane; + + tag.lanes = Number(metadata.count).toString(); + + tag['lanes:forward'] = undefined; + tag['lanes:backward'] = undefined; + + tag['turn:lanes'] = formPipes(metadata.turnLanes.unspecified, metadata.count, 'none'); + tag['turn:lanes:forward'] = undefined; + tag['turn:lanes:backward'] = undefined; + } else { + curLane = curLane >= metadata[curDir] ? 0 : curLane; + + tag.lanes = metadata.forward + metadata.backward + metadata.bothways + ''; + tag['lanes:forward'] = metadata.forward + ''; + tag['lanes:backward'] = metadata.backward + ''; + + tag['turn:lanes'] = undefined; + tag['turn:lanes:forward'] = formPipes(metadata.turnLanes.forward, metadata.forward, 'none'); + tag['turn:lanes:backward'] = formPipes(metadata.turnLanes.backward, metadata.backward, 'none'); + } + dispatch.call('change', this, tag, onInput); + } lanes.entity = function (_) { if (!wayID) { @@ -347,9 +176,9 @@ export function uiFieldLanes(field, context) { wayID = _.id; } if (_.isOneWay()) { - curDirection = 'unspecified'; + curDir = 'unspecified'; } else { - curDirection = 'forward'; + curDir = 'forward'; } } }; @@ -362,32 +191,7 @@ export function uiFieldLanes(field, context) { return utilRebind(lanes, dispatch, 'on'); } -export function metadataToOSM(metadata) { - var tag = {}; - - if (metadata.oneway) { - tag.lanes = Number(metadata.count).toString(); - - tag['lanes:forward'] = undefined; - tag['lanes:backward'] = undefined; - - tag['turn:lanes'] = formPipes(metadata.turnLanes.unspecified, metadata.count, 'none'); - tag['turn:lanes:forward'] = undefined; - tag['turn:lanes:backward'] = undefined; - } else { - tag.lanes = metadata.forward + metadata.backward + metadata.bothways + ''; - tag['lanes:forward'] = metadata.forward + ''; - tag['lanes:backward'] = metadata.backward + ''; - - tag['turn:lanes'] = undefined; - tag['turn:lanes:forward'] = formPipes(metadata.turnLanes.forward, metadata.forward, 'none'); - tag['turn:lanes:backward'] = formPipes(metadata.turnLanes.backward, metadata.backward, 'none'); - } - return tag; -} - - -function formPipes(data, len, nullKey) { +export function formPipes(data, len, nullKey) { var piped = data.slice(0, len); // Makes sure it fills any undefined as nullKey diff --git a/modules/ui/fields/lanes/laneInfo.js b/modules/ui/fields/lanes/laneInfo.js new file mode 100644 index 00000000000..e5f4fcc23b1 --- /dev/null +++ b/modules/ui/fields/lanes/laneInfo.js @@ -0,0 +1,113 @@ +import * as d3 from 'd3'; +import { validTurnLanes } from '../../../osm/lanes'; +import { utilRebind } from '../../../util/rebind'; +import { formPipes } from '../lanes'; +import { utilGetSetValue } from '../../../util/get_set_value'; + +import _ from 'lodash'; + +export function uiLaneInfo() { + var dispatch = d3.dispatch('change'); + function laneInfo(selection, metadata, curDirection, curLane) { + // TODO: make this thing dynamic and show keysConsidered + // wrt to oneway. + var keysConsidered = ['count', 'forward', 'backward', 'reverse']; + var s = selection.selectAll('.lanes-info').data([0]); + s = s.enter() + .append('div') + .attr('class', 'lanes-info') + .merge(s); + + var wrap = s.selectAll('.preset-input-wrap') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'preset-input-wrap') + .append('ul') + .merge(wrap); + + var list = wrap.selectAll('ul') + .data([0]); + + list = list.enter() + .append('ul') + .merge(list); + + + var items = list.selectAll('li') + .data(keysConsidered); + + // Enter + var enter = items.enter() + .append('li') + .attr('class', function (d) { return 'cf preset-access-' + d; }); + + enter + .append('span') + .attr('class', 'col6 label preset-label-') + .attr('for', function (d) { return 'preset-input-' + d; }) + .text(function (d) { return d; }); + + enter + .append('div') + .attr('class', 'col6 preset-input-lanes-wrap') + .append('input') + .attr('type', 'text') + .attr('class', 'preset-input-lanes') + .attr('id', function (d) { return 'preset-input-lanes-' + d; }) + .each(function (d) { + this.value = metadata[d]; + }); + + // Update + items = items.merge(enter); + items.selectAll('input') + .property('value', function (d) { + return metadata[d]; + }); + + var input = items.selectAll('input'); + + input + .on('change', change) + .on('blur', change); + + function change(d) { + var tag = {}; + if (metadata.oneway) { + if (d === 'count') { + var count = utilGetSetValue(d3.select(this)); + count = parseInt(count, 10); + if (!_.isNaN(count)) metadata.count = count; + } + if (d === 'reverse') { + if (utilGetSetValue(d3.select(this)) === 'true') { + metadata.reverse = true; + } else { + metadata.reverse = false; + } + } + + tag.lanes = Number(metadata.count).toString(); + tag['lanes:forward'] = undefined; + tag['lanes:backward'] = undefined; + + } else { + if (d === 'forward') { + var forward = utilGetSetValue(d3.select(this)); + forward = parseInt(forward, 10); + if (!_.isNaN(forward)) metadata.forward = forward; + } + if (d === 'backward') { + var backward = utilGetSetValue(d3.select(this)); + backward = parseInt(backward, 10); + if (!_.isNaN(backward)) metadata.backward = backward; + } + } + + dispatch.call('change', this); + } + } + return utilRebind(laneInfo, dispatch, 'on'); +} \ No newline at end of file diff --git a/modules/ui/fields/lanes/turn.js b/modules/ui/fields/lanes/turn.js new file mode 100644 index 00000000000..66542a37fe3 --- /dev/null +++ b/modules/ui/fields/lanes/turn.js @@ -0,0 +1,98 @@ +import * as d3 from 'd3'; +import { validTurnLanes } from '../../../osm/lanes'; +import { utilRebind } from '../../../util/rebind'; +import { formPipes } from '../lanes'; +import _ from 'lodash'; + +export function uiTurnLanes(field) { + var dispatch = d3.dispatch('change'); + + function turnLanes(selection, metadata, curDirection, curLane) { + var turnLanesData = metadata.turnLanes; + var valid = validTurnLanes.map(function (t) { + var dir = turnLanesData[curDirection][curLane]; + if (!dir) return false; + return dir.indexOf(t) > -1; + }); + + var wrapper = selection + .selectAll('.turn-lanes') + .data([0]); + + wrapper.exit().remove(); + + var enter = wrapper.enter() + .append('div') + .attr('class', 'turn-lanes localized-wrap'); + + enter + .append('label') + .attr('class', 'form-label entry') + .text('Turn Lanes'); + + enter + .append('div') + .attr('class', 'lane-tags preset-input-wrap checkselect'); + + wrapper = wrapper + .merge(enter); + + var label = wrapper + .selectAll('.lane-tags') + .selectAll('label') + .data(valid); + + label.exit() + .remove(); + + var labelEnter = label.enter() + .append('label'); + + labelEnter + .append('input') + .property('direction', function (d, i) { return validTurnLanes[i]; }) + .property('indeterminate', field.type === 'check') + .attr('type', 'checkbox') + .attr('id', 'preset-input-' + field.id); + + + labelEnter + .append('span') + .text(function (d, i) { return validTurnLanes[i]; }) + .attr('class', 'value'); + + label = label + .merge(labelEnter) + .select('input') + .property('checked', function (d) { + return d; + }) + .on('click', change); + + function change() { + d3.event.stopPropagation(); + var input = selection.selectAll('input'); + var direction = d3.select(this).property('direction'); + var newDirs = []; + + input.each(function (d, i) { + var value = d3.select(this).property('checked'); + var direction = d3.select(this).property('direction'); + if (value) { + newDirs.push(direction); + } + }); + + if (direction === 'none' || newDirs.length === 0) { + newDirs = ['none']; + } else { + _.pull(newDirs, 'none'); + } + + turnLanesData[curDirection][curLane] = newDirs; + + dispatch.call('change', this); + } + } + return utilRebind(turnLanes, dispatch, 'on'); +} \ No newline at end of file diff --git a/modules/ui/preset.js b/modules/ui/preset.js index 2670ece223c..12b9a26af1a 100644 --- a/modules/ui/preset.js +++ b/modules/ui/preset.js @@ -23,7 +23,6 @@ export function uiPreset(context) { // Field Constructor function UIField(field, entity, show) { field = _.clone(field); - console.log(field); field.input = uiFields[field.type](field, context) .on('change', function(t, onInput) { dispatch.call('change', field, t, onInput); From b8c837de540998c7bfd86ce1af05848e610a0a9c Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sat, 4 Feb 2017 22:05:14 +0530 Subject: [PATCH 18/25] lanes info styling --- css/app.css | 26 +++++- modules/ui/fields/lanes/laneInfo.js | 128 +++++++++++++++++----------- 2 files changed, 100 insertions(+), 54 deletions(-) diff --git a/css/app.css b/css/app.css index 2f9456b59e0..397132c74eb 100644 --- a/css/app.css +++ b/css/app.css @@ -1227,22 +1227,40 @@ button.save.has-count .count::before { } -.form-field-lanes .lanes-info { - margin-bottom: 10px; +.form-field-lanes .lanes-info input { + min-height: 30px; } + /*.form-field-lanes input { + border: 1px solid #ccc; + min-height: 30px; + border-top: 0; + border-radius: 0 0 4px 4px; + overflow: hidden; + }*/ -.form-field-lanes .preset-input-wrap input { + +.form-field-lanes .lane-field { border-radius: 0; border-width: 0; border-bottom-width: 1px; + padding: 5px 10px; + width: 100%; + border: 1px solid #CCC; + border-top: 0 } + +.form-field-lanes .preset-input-lanes-wrap input { + border-top: 0 +} + +/* .form-field-lanes .preset-input-lanes-wrap input { border-radius: 0; border-width: 0; border-left-width: 1px; } - +*/ .form-field-lanes .preset-input-wrap li { border-bottom: 1px solid #CCC; } diff --git a/modules/ui/fields/lanes/laneInfo.js b/modules/ui/fields/lanes/laneInfo.js index e5f4fcc23b1..8cc10fd0266 100644 --- a/modules/ui/fields/lanes/laneInfo.js +++ b/modules/ui/fields/lanes/laneInfo.js @@ -12,66 +12,94 @@ export function uiLaneInfo() { // TODO: make this thing dynamic and show keysConsidered // wrt to oneway. var keysConsidered = ['count', 'forward', 'backward', 'reverse']; - var s = selection.selectAll('.lanes-info').data([0]); - s = s.enter() - .append('div') - .attr('class', 'lanes-info') - .merge(s); - - var wrap = s.selectAll('.preset-input-wrap') + var wrapper = selection + .selectAll('.lanes-info') .data([0]); - wrap = wrap.enter() + var enter = wrapper.enter() .append('div') - .attr('class', 'preset-input-wrap') - .append('ul') - .merge(wrap); - - var list = wrap.selectAll('ul') - .data([0]); + .attr('class', 'lanes-info lanes-wrap preset-input-wrap'); - list = list.enter() - .append('ul') - .merge(list); - - - var items = list.selectAll('li') - .data(keysConsidered); - - // Enter - var enter = items.enter() - .append('li') - .attr('class', function (d) { return 'cf preset-access-' + d; }); + var input = enter + .append('input') + .attr('class', 'lanes-info') + .attr('type', 'lane-count') + .attr('id', '23231') + .attr('placeholder', 'Lane count'); - enter - .append('span') - .attr('class', 'col6 label preset-label-') - .attr('for', function (d) { return 'preset-input-' + d; }) - .text(function (d) { return d; }); + input + .on('input', change(true)) + .on('blur', change()) + .on('change', change()); - enter + var spinControl = enter .append('div') - .attr('class', 'col6 preset-input-lanes-wrap') - .append('input') - .attr('type', 'text') - .attr('class', 'preset-input-lanes') - .attr('id', function (d) { return 'preset-input-lanes-' + d; }) - .each(function (d) { - this.value = metadata[d]; + .attr('class', 'spin-control'); + + // var enter = spinControl.enter() + // .append('div') + // .attr('class', 'spin-control'); + + spinControl + .append('button') + .datum(1) + .attr('class', 'increment') + .attr('tabindex', -1); + + spinControl + .append('button') + .datum(-1) + .attr('class', 'decrement') + .attr('tabindex', -1); + + // spinControl = spinControl + // .merge(enter); + + spinControl.selectAll('button') + .on('click', function (d) { + d3.event.preventDefault(); + var num = parseInt(input.node().value || 0, 10); + if (!isNaN(num)) input.node().value = num + d; + change()(); }); - // Update - items = items.merge(enter); - items.selectAll('input') - .property('value', function (d) { - return metadata[d]; - }); - - var input = items.selectAll('input'); + // var items = list.selectAll('li') + // .data(keysConsidered); - input - .on('change', change) - .on('blur', change); + // Enter + // var enter = items.enter() + // .append('li') + // .attr('class', function (d) { return 'cf preset-access-' + d; }); + + // enter + // .append('span') + // .attr('class', 'col6 label preset-label-') + // .attr('for', function (d) { return 'preset-input-' + d; }) + // .text(function (d) { return d; }); + + // enter + // .append('div') + // .attr('class', 'col6 preset-input-lanes-wrap') + // .append('input') + // .attr('type', 'text') + // .attr('class', 'preset-input-lanes') + // .attr('id', function (d) { return 'preset-input-lanes-' + d; }) + // .each(function (d) { + // this.value = metadata[d]; + // }); + + // // Update + // items = items.merge(enter); + // items.selectAll('input') + // .property('value', function (d) { + // return metadata[d]; + // }); + + // var input = items.selectAll('input'); + + // input + // .on('change', change) + // .on('blur', change); function change(d) { var tag = {}; From 711f9f1bac9fa842d74857bb56bbc2eebe9ba13a Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sun, 5 Feb 2017 11:49:31 +0530 Subject: [PATCH 19/25] styling lanesInfo --- css/app.css | 3 ++ modules/ui/fields/lanes/laneInfo.js | 51 ++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/css/app.css b/css/app.css index 397132c74eb..423157bc32f 100644 --- a/css/app.css +++ b/css/app.css @@ -1229,6 +1229,9 @@ button.save.has-count .count::before { .form-field-lanes .lanes-info input { min-height: 30px; + padding: 5px 10px; + width: 100%; + border: 0; } /*.form-field-lanes input { border: 1px solid #ccc; diff --git a/modules/ui/fields/lanes/laneInfo.js b/modules/ui/fields/lanes/laneInfo.js index 8cc10fd0266..9b54321fdff 100644 --- a/modules/ui/fields/lanes/laneInfo.js +++ b/modules/ui/fields/lanes/laneInfo.js @@ -11,7 +11,6 @@ export function uiLaneInfo() { function laneInfo(selection, metadata, curDirection, curLane) { // TODO: make this thing dynamic and show keysConsidered // wrt to oneway. - var keysConsidered = ['count', 'forward', 'backward', 'reverse']; var wrapper = selection .selectAll('.lanes-info') .data([0]); @@ -20,47 +19,67 @@ export function uiLaneInfo() { .append('div') .attr('class', 'lanes-info lanes-wrap preset-input-wrap'); - var input = enter + wrapper = wrapper + .merge(enter); + + var input = wrapper.selectAll('.lanes-info') + .data([0]); + + input.exit() + .remove(); + + input = input.enter() .append('input') .attr('class', 'lanes-info') .attr('type', 'lane-count') - .attr('id', '23231') - .attr('placeholder', 'Lane count'); - + .attr('placeholder', 'Lane count') + .merge(input); + input - .on('input', change(true)) - .on('blur', change()) - .on('change', change()); + .on('input', change) + .on('blur', change) + .on('change', change); + // .each(() => this.value = metadata.count); + + input.node().value = metadata.count; - var spinControl = enter + var spinControl = wrapper.selectAll('.spin-control') + .data([0]); + + + var senter = spinControl.enter() .append('div') .attr('class', 'spin-control'); - + + // console.log(wrapper.selectAll('input').node().value(metadata.count)); + // utilGetSetValue(wrapper.selectAll('input'), metadata.count || ''); // var enter = spinControl.enter() // .append('div') // .attr('class', 'spin-control'); - spinControl + senter .append('button') .datum(1) .attr('class', 'increment') .attr('tabindex', -1); - spinControl + senter .append('button') .datum(-1) .attr('class', 'decrement') .attr('tabindex', -1); - // spinControl = spinControl - // .merge(enter); + spinControl = spinControl + .merge(senter); spinControl.selectAll('button') .on('click', function (d) { d3.event.preventDefault(); + console.log(input.node().value); var num = parseInt(input.node().value || 0, 10); if (!isNaN(num)) input.node().value = num + d; - change()(); + + change(); }); // var items = list.selectAll('li') @@ -103,6 +122,8 @@ export function uiLaneInfo() { function change(d) { var tag = {}; + metadata.count = utilGetSetValue(selection.selectAll('input')); + if (metadata.oneway) { if (d === 'count') { var count = utilGetSetValue(d3.select(this)); From 5189ea0f16161e68642b5ea41a8e1c2e8cb7318e Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sun, 5 Feb 2017 13:14:59 +0530 Subject: [PATCH 20/25] lanes info styling part 2 --- css/app.css | 8 ----- modules/ui/fields/lanes/laneInfo.js | 48 +++++++++++++++++++---------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/css/app.css b/css/app.css index 423157bc32f..04b68e87977 100644 --- a/css/app.css +++ b/css/app.css @@ -1233,14 +1233,6 @@ button.save.has-count .count::before { width: 100%; border: 0; } - /*.form-field-lanes input { - border: 1px solid #ccc; - min-height: 30px; - border-top: 0; - border-radius: 0 0 4px 4px; - overflow: hidden; - }*/ - .form-field-lanes .lane-field { border-radius: 0; diff --git a/modules/ui/fields/lanes/laneInfo.js b/modules/ui/fields/lanes/laneInfo.js index 9b54321fdff..cf667a67ee5 100644 --- a/modules/ui/fields/lanes/laneInfo.js +++ b/modules/ui/fields/lanes/laneInfo.js @@ -11,6 +11,8 @@ export function uiLaneInfo() { function laneInfo(selection, metadata, curDirection, curLane) { // TODO: make this thing dynamic and show keysConsidered // wrt to oneway. + var oneway = metadata.oneway; + var wrapper = selection .selectAll('.lanes-info') .data([0]); @@ -21,9 +23,19 @@ export function uiLaneInfo() { wrapper = wrapper .merge(enter); - + var input = wrapper.selectAll('.lanes-info') - .data([0]); + .data(oneway ? [{ + text: 'Lanes', + dir: 'oneway' + }] : [{ + text: 'Forward Lanes', + dir: 'forward' + }, + { + text: 'Backward Lanes', + dir: 'backward' + }]); input.exit() .remove(); @@ -32,45 +44,49 @@ export function uiLaneInfo() { .append('input') .attr('class', 'lanes-info') .attr('type', 'lane-count') - .attr('placeholder', 'Lane count') .merge(input); input + .attr('placeholder', function (d) { + return d.text; + }) + .attr('direction', function (d) { + return d.dir; + }) .on('input', change) .on('blur', change) - .on('change', change); - // .each(() => this.value = metadata.count); + .on('change', change) + .each(function (d) { + if (d.dir === 'oneway') { + this.value = metadata.count; + return; + } + this.value = metadata[d.dir]; + }); - input.node().value = metadata.count; var spinControl = wrapper.selectAll('.spin-control') .data([0]); - var senter = spinControl.enter() + var spinEnter = spinControl.enter() .append('div') .attr('class', 'spin-control'); - // console.log(wrapper.selectAll('input').node().value(metadata.count)); - // utilGetSetValue(wrapper.selectAll('input'), metadata.count || ''); - // var enter = spinControl.enter() - // .append('div') - // .attr('class', 'spin-control'); - - senter + spinEnter .append('button') .datum(1) .attr('class', 'increment') .attr('tabindex', -1); - senter + spinEnter .append('button') .datum(-1) .attr('class', 'decrement') .attr('tabindex', -1); spinControl = spinControl - .merge(senter); + .merge(spinEnter); spinControl.selectAll('button') .on('click', function (d) { From 399633fc41ceba3e3f24eb5c9eb85b892042c710 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sun, 5 Feb 2017 15:07:21 +0530 Subject: [PATCH 21/25] lane selector UI --- css/app.css | 20 +- modules/ui/fields/lanes/laneInfo.js | 314 ++++++++++++++++++---------- temp.js | 113 ++++++++++ 3 files changed, 330 insertions(+), 117 deletions(-) create mode 100644 temp.js diff --git a/css/app.css b/css/app.css index 04b68e87977..2a87cf72e5f 100644 --- a/css/app.css +++ b/css/app.css @@ -1231,9 +1231,14 @@ button.save.has-count .count::before { min-height: 30px; padding: 5px 10px; width: 100%; - border: 0; + border-radius: 0; + border-width: 0; + border-left-width: 1px; +} +.form-field-lanes .spin-control button{ + height: 30px; + right: 0; } - .form-field-lanes .lane-field { border-radius: 0; border-width: 0; @@ -1244,10 +1249,11 @@ button.save.has-count .count::before { border-top: 0 } - +/* .form-field-lanes .preset-input-lanes-wrap input { - border-top: 0 -} + border-top: 0; + border-left-width: 1px; +}*/ /* .form-field-lanes .preset-input-lanes-wrap input { @@ -1281,7 +1287,6 @@ button.save.has-count .count::before { display: flex; flex-grow: 1; border-right: 1px solid #ccc; - border-bottom: 0; flex-direction: column; align-items: center; background-color: white; @@ -1303,7 +1308,7 @@ button.save.has-count .count::before { .lane-selector .lane-item span { height: 30px; - padding: 5px 10px 5px 10px; + line-height: 30px; } .lane-tags > label { @@ -3353,7 +3358,6 @@ img.tile-removing { /* Scrollbars ----------------------------------------------------- */ - ::-webkit-scrollbar { height: 20px; overflow: visible; diff --git a/modules/ui/fields/lanes/laneInfo.js b/modules/ui/fields/lanes/laneInfo.js index cf667a67ee5..6a37fc33a74 100644 --- a/modules/ui/fields/lanes/laneInfo.js +++ b/modules/ui/fields/lanes/laneInfo.js @@ -9,67 +9,49 @@ import _ from 'lodash'; export function uiLaneInfo() { var dispatch = d3.dispatch('change'); function laneInfo(selection, metadata, curDirection, curLane) { - // TODO: make this thing dynamic and show keysConsidered - // wrt to oneway. - var oneway = metadata.oneway; + var s = selection.selectAll('.lanes-info').data([0]); + s = s.enter() + .append('div') + .attr('class', 'lanes-info') + .merge(s); - var wrapper = selection - .selectAll('.lanes-info') + var wrap = s.selectAll('.preset-input-wrap') .data([0]); - var enter = wrapper.enter() + wrap = wrap.enter() .append('div') - .attr('class', 'lanes-info lanes-wrap preset-input-wrap'); - - wrapper = wrapper - .merge(enter); - - var input = wrapper.selectAll('.lanes-info') - .data(oneway ? [{ - text: 'Lanes', - dir: 'oneway' - }] : [{ - text: 'Forward Lanes', - dir: 'forward' - }, - { - text: 'Backward Lanes', - dir: 'backward' - }]); - - input.exit() - .remove(); - - input = input.enter() - .append('input') - .attr('class', 'lanes-info') - .attr('type', 'lane-count') - .merge(input); - - input - .attr('placeholder', function (d) { - return d.text; - }) - .attr('direction', function (d) { - return d.dir; - }) - .on('input', change) - .on('blur', change) - .on('change', change) - .each(function (d) { - if (d.dir === 'oneway') { - this.value = metadata.count; - return; - } - this.value = metadata[d.dir]; - }); - + .attr('class', 'preset-input-wrap') + .merge(wrap); - var spinControl = wrapper.selectAll('.spin-control') + var list = wrap.selectAll('ul') .data([0]); + list = list.enter() + .append('ul') + .merge(list); - var spinEnter = spinControl.enter() + + var items = list.selectAll('li') + .data(metadata.oneway ? ['count'] : ['forward', 'backward']); + + items.exit().remove(); + // Enter + var enter = items.enter() + .append('li') + .attr('class', 'cf preset-lanes-info'); + + enter + .append('span') + .attr('class', 'col6 label preset-label-') + .text(function (d) { return d; }); + + enter + .append('div') + .attr('class', 'col6 preset-input-lanes-wrap') + .append('input') + .attr('type', 'text'); + + var spinEnter = enter .append('div') .attr('class', 'spin-control'); @@ -85,74 +67,43 @@ export function uiLaneInfo() { .attr('class', 'decrement') .attr('tabindex', -1); - spinControl = spinControl - .merge(spinEnter); - spinControl.selectAll('button') + + // Update + items = items.merge(enter); + + items.select('span') + .text(function (d) { return d; }); + + items.select('input') + .each(function (d) { + this.value = metadata[d]; + }) + .attr('name', function (d) { + return d; + }) + .on('change', change) + .on('blur', change); + + items.selectAll('button') .on('click', function (d) { d3.event.preventDefault(); - console.log(input.node().value); - var num = parseInt(input.node().value || 0, 10); - if (!isNaN(num)) input.node().value = num + d; - - change(); + var el = d3.select(this.parentNode.parentNode); + el = el.selectAll('input'); + var num = parseInt(el.node().value || 0, 10); + if (!isNaN(num) && num + d > 0 && num + d < 11) + el.node().value = num + d; + change.call(el.node(), el.attr('name')); }); - // var items = list.selectAll('li') - // .data(keysConsidered); - - // Enter - // var enter = items.enter() - // .append('li') - // .attr('class', function (d) { return 'cf preset-access-' + d; }); - - // enter - // .append('span') - // .attr('class', 'col6 label preset-label-') - // .attr('for', function (d) { return 'preset-input-' + d; }) - // .text(function (d) { return d; }); - - // enter - // .append('div') - // .attr('class', 'col6 preset-input-lanes-wrap') - // .append('input') - // .attr('type', 'text') - // .attr('class', 'preset-input-lanes') - // .attr('id', function (d) { return 'preset-input-lanes-' + d; }) - // .each(function (d) { - // this.value = metadata[d]; - // }); - - // // Update - // items = items.merge(enter); - // items.selectAll('input') - // .property('value', function (d) { - // return metadata[d]; - // }); - - // var input = items.selectAll('input'); - - // input - // .on('change', change) - // .on('blur', change); - function change(d) { var tag = {}; - metadata.count = utilGetSetValue(selection.selectAll('input')); - if (metadata.oneway) { if (d === 'count') { var count = utilGetSetValue(d3.select(this)); count = parseInt(count, 10); if (!_.isNaN(count)) metadata.count = count; } - if (d === 'reverse') { - if (utilGetSetValue(d3.select(this)) === 'true') { - metadata.reverse = true; - } else { - metadata.reverse = false; - } - } tag.lanes = Number(metadata.count).toString(); tag['lanes:forward'] = undefined; @@ -175,4 +126,149 @@ export function uiLaneInfo() { } } return utilRebind(laneInfo, dispatch, 'on'); -} \ No newline at end of file +} +// import * as d3 from 'd3'; +// import { validTurnLanes } from '../../../osm/lanes'; +// import { utilRebind } from '../../../util/rebind'; +// import { formPipes } from '../lanes'; +// import { utilGetSetValue } from '../../../util/get_set_value'; + +// import _ from 'lodash'; + +// export function uiLaneInfo() { +// var dispatch = d3.dispatch('change'); +// function laneInfo(selection, metadata, curDirection, curLane) { +// // TODO: make this thing dynamic and show keysConsidered +// // wrt to oneway. +// var oneway = metadata.oneway; + +// var wrapper = selection +// .selectAll('.lanes-info') +// .data([0]); + +// var enter = wrapper.enter() +// .append('div') +// .attr('class', 'lanes-info lanes-wrap preset-input-wrap'); + +// wrapper = wrapper +// .merge(enter); + +// var input = wrapper.selectAll('.lanes-info') +// .data(oneway ? [{ +// text: 'Lanes', +// dir: 'oneway' +// }] : [{ +// text: 'Forward Lanes', +// dir: 'forward' +// }, +// { +// text: 'Backward Lanes', +// dir: 'backward' +// }]); + +// input.exit() +// .remove(); + +// input = input.enter() +// .append('input') +// .attr('class', 'lanes-info') +// .attr('type', 'lane-count') +// .merge(input); + +// input +// .attr('placeholder', function (d) { +// return d.text; +// }) +// .attr('direction', function (d) { +// return d.dir; +// }) +// .on('input', change) +// .on('blur', change) +// .on('change', change) +// .each(function (d) { +// if (d.dir === 'oneway') { +// this.value = metadata.count; +// return; +// } +// this.value = metadata[d.dir]; +// }); + + +// // var items = list.selectAll('li') +// // .data(keysConsidered); + +// // Enter +// // var enter = items.enter() +// // .append('li') +// // .attr('class', function (d) { return 'cf preset-access-' + d; }); + +// // enter +// // .append('span') +// // .attr('class', 'col6 label preset-label-') +// // .attr('for', function (d) { return 'preset-input-' + d; }) +// // .text(function (d) { return d; }); + +// // enter +// // .append('div') +// // .attr('class', 'col6 preset-input-lanes-wrap') +// // .append('input') +// // .attr('type', 'text') +// // .attr('class', 'preset-input-lanes') +// // .attr('id', function (d) { return 'preset-input-lanes-' + d; }) +// // .each(function (d) { +// // this.value = metadata[d]; +// // }); + +// // // Update +// // items = items.merge(enter); +// // items.selectAll('input') +// // .property('value', function (d) { +// // return metadata[d]; +// // }); + +// // var input = items.selectAll('input'); + +// // input +// // .on('change', change) +// // .on('blur', change); + +// function change(d) { +// var tag = {}; +// metadata.count = utilGetSetValue(selection.selectAll('input')); + +// if (metadata.oneway) { +// if (d === 'count') { +// var count = utilGetSetValue(d3.select(this)); +// count = parseInt(count, 10); +// if (!_.isNaN(count)) metadata.count = count; +// } +// if (d === 'reverse') { +// if (utilGetSetValue(d3.select(this)) === 'true') { +// metadata.reverse = true; +// } else { +// metadata.reverse = false; +// } +// } + +// tag.lanes = Number(metadata.count).toString(); +// tag['lanes:forward'] = undefined; +// tag['lanes:backward'] = undefined; + +// } else { +// if (d === 'forward') { +// var forward = utilGetSetValue(d3.select(this)); +// forward = parseInt(forward, 10); +// if (!_.isNaN(forward)) metadata.forward = forward; +// } +// if (d === 'backward') { +// var backward = utilGetSetValue(d3.select(this)); +// backward = parseInt(backward, 10); +// if (!_.isNaN(backward)) metadata.backward = backward; +// } +// } + +// dispatch.call('change', this); +// } +// } +// return utilRebind(laneInfo, dispatch, 'on'); +// } \ No newline at end of file diff --git a/temp.js b/temp.js new file mode 100644 index 00000000000..e5f4fcc23b1 --- /dev/null +++ b/temp.js @@ -0,0 +1,113 @@ +import * as d3 from 'd3'; +import { validTurnLanes } from '../../../osm/lanes'; +import { utilRebind } from '../../../util/rebind'; +import { formPipes } from '../lanes'; +import { utilGetSetValue } from '../../../util/get_set_value'; + +import _ from 'lodash'; + +export function uiLaneInfo() { + var dispatch = d3.dispatch('change'); + function laneInfo(selection, metadata, curDirection, curLane) { + // TODO: make this thing dynamic and show keysConsidered + // wrt to oneway. + var keysConsidered = ['count', 'forward', 'backward', 'reverse']; + var s = selection.selectAll('.lanes-info').data([0]); + s = s.enter() + .append('div') + .attr('class', 'lanes-info') + .merge(s); + + var wrap = s.selectAll('.preset-input-wrap') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'preset-input-wrap') + .append('ul') + .merge(wrap); + + var list = wrap.selectAll('ul') + .data([0]); + + list = list.enter() + .append('ul') + .merge(list); + + + var items = list.selectAll('li') + .data(keysConsidered); + + // Enter + var enter = items.enter() + .append('li') + .attr('class', function (d) { return 'cf preset-access-' + d; }); + + enter + .append('span') + .attr('class', 'col6 label preset-label-') + .attr('for', function (d) { return 'preset-input-' + d; }) + .text(function (d) { return d; }); + + enter + .append('div') + .attr('class', 'col6 preset-input-lanes-wrap') + .append('input') + .attr('type', 'text') + .attr('class', 'preset-input-lanes') + .attr('id', function (d) { return 'preset-input-lanes-' + d; }) + .each(function (d) { + this.value = metadata[d]; + }); + + // Update + items = items.merge(enter); + items.selectAll('input') + .property('value', function (d) { + return metadata[d]; + }); + + var input = items.selectAll('input'); + + input + .on('change', change) + .on('blur', change); + + function change(d) { + var tag = {}; + if (metadata.oneway) { + if (d === 'count') { + var count = utilGetSetValue(d3.select(this)); + count = parseInt(count, 10); + if (!_.isNaN(count)) metadata.count = count; + } + if (d === 'reverse') { + if (utilGetSetValue(d3.select(this)) === 'true') { + metadata.reverse = true; + } else { + metadata.reverse = false; + } + } + + tag.lanes = Number(metadata.count).toString(); + tag['lanes:forward'] = undefined; + tag['lanes:backward'] = undefined; + + } else { + if (d === 'forward') { + var forward = utilGetSetValue(d3.select(this)); + forward = parseInt(forward, 10); + if (!_.isNaN(forward)) metadata.forward = forward; + } + if (d === 'backward') { + var backward = utilGetSetValue(d3.select(this)); + backward = parseInt(backward, 10); + if (!_.isNaN(backward)) metadata.backward = backward; + } + } + + dispatch.call('change', this); + } + } + return utilRebind(laneInfo, dispatch, 'on'); +} \ No newline at end of file From eb759ee8ff68ec406a842f633d7221970c4b46f1 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sun, 5 Feb 2017 21:49:27 +0530 Subject: [PATCH 22/25] UI cleanup --- css/app.css | 46 +++++++-- modules/osm/lanes.js | 4 +- modules/ui/fields/lanes.js | 49 ++++++---- modules/ui/fields/lanes/laneInfo.js | 145 ---------------------------- modules/ui/fields/lanes/turn.js | 115 ++++++++++++---------- 5 files changed, 131 insertions(+), 228 deletions(-) diff --git a/css/app.css b/css/app.css index 2a87cf72e5f..36119c7d363 100644 --- a/css/app.css +++ b/css/app.css @@ -1282,7 +1282,7 @@ button.save.has-count .count::before { } -.lane-selector .lane-item { +.lane-selector .lane-index { cursor: pointer; display: flex; flex-grow: 1; @@ -1292,35 +1292,63 @@ button.save.has-count .count::before { background-color: white; } -.lane-selector .lane-item.active { +.lane-selector .lane-index.active { background-color: #E8EBFF; } -.lane-selector .lane-item.active:hover { +.lane-selector .lane-index.active:hover { background-color: #E8EBFF; } -.lane-selector .lane-item:hover { +.lane-selector .lane-index:hover { background-color: #ececec; } -.lane-selector .lane-item:last-child { +.lane-selector .lane-index.has-data { + color: #7092ff; +} + +.lane-selector .lane-index:last-child { border-right: 0; } -.lane-selector .lane-item span { +.lane-selector .lane-index span { height: 30px; line-height: 30px; } -.lane-tags > label { +.turn-lane-tags .turn-lanes-direction { position: relative; padding: 5px 10px; - display: block; height: 30px; background-color: white; color: #7092FF; cursor: pointer; } +.turn-lane-tags .turn-lanes-direction.active { + background-color: #E8EBFF; +} +.turn-lane-tags .turn-lanes-direction.active:hover { + background-color: #E8EBFF; +} +.turn-lane-tags .turn-lanes-direction.left-border { + border-left: 1px solid #ccc +} +.turn-lane-tags .turn-lanes-direction:hover { + background-color: #ececec; +} +.turn-lanes-directions .direction { + width: 50px; +} +.turn-lane-tags .turn-lanes-direction:nth-last-child(2) { + border-bottom: 0; + border-bottom-left-radius: 4px; +} +.turn-lane-tags .turn-lanes-direction:last-child { + border-bottom: 0; + border-bottom-right-radius: 4px; +} + +/* .lane-tags > label:hover { background-color: #ececec; } @@ -1342,7 +1370,7 @@ button.save.has-count .count::before { .lane-tags > label.active { background: #E8EBFF; -} +}*/ /*lane-visualizer*/ diff --git a/modules/osm/lanes.js b/modules/osm/lanes.js index 8989181ce07..b60195fc758 100644 --- a/modules/osm/lanes.js +++ b/modules/osm/lanes.js @@ -1,8 +1,8 @@ import _ from 'lodash'; export var validTurnLanes = [ - 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', - 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' + 'left', 'right', 'slight_left', 'slight_right', 'sharp_left', + 'sharp_right', 'merge_to_left', 'merge_to_right','reverse', 'through', 'none' ]; export function osmLanes(entity) { diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 93a15daf517..30cce898b63 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -19,12 +19,11 @@ export function uiFieldLanes(field, context) { var lanesInfo = uiLaneInfo(field, context) .on('change', change); - // var lanes function lanes(selection) { lanesData = context.entity(wayID).lanes(); metadata = lanesData.metadata; - + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { selection.call(lanes.off); return; @@ -38,7 +37,7 @@ export function uiFieldLanes(field, context) { .append('div') .attr('class', 'lane-selector localized-wrap') .merge(laneSelector); - + laneSelector.call(laneSelectorUI); selection.call(turnLanes, metadata, curDir, curLane); @@ -60,11 +59,7 @@ export function uiFieldLanes(field, context) { function laneSelectorUI(selection) { var items; var metadata = lanesData.metadata; - var oneway = metadata.oneway; var len = metadata.count; - // TODO: clean up this mess of vvvvv - if (oneway) len = metadata.count; - else len = metadata.forward + metadata.backward; selection.selectAll('.form-label') .data([0]) @@ -88,20 +83,20 @@ export function uiFieldLanes(field, context) { .append('ul') .merge(list); - items = list.selectAll('.lane-item') + items = list.selectAll('.lane-index') .data(_.fill(Array(len), 0).map(function (n, i) { return i; })); items.enter() .append('div') - .attr('class', 'lane-item') + .attr('class', 'lane-index') .attr('id', function (d) { return 'lane-' + d; }) .append('span'); - var input = selection.selectAll('.lane-item'); + var input = selection.selectAll('.lane-index'); input.selectAll('span') .text(function (d) { @@ -112,13 +107,24 @@ export function uiFieldLanes(field, context) { return (d - metadata.forward + 1) + '▼'; }); - input.classed('active', function (d) { - if (metadata.oneway) return d === curLane; - if (curDir === 'forward') { - return d === curLane; - } - return curLane + metadata.forward === d; - }); + input + .classed('active', function (d) { + if (metadata.oneway) return d === curLane; + if (curDir === 'forward') { + return d === curLane; + } + return curLane + metadata.forward === d; + }) + .classed('has-data', function (d) { + if (metadata.oneway) { + return hasData('unspecified', d); + } + if (d < metadata.forward) { + return hasData('forward', d); + } + return hasData('backward', d - metadata.forward); + }); + input.on('click', function (d) { if (metadata.oneway) { curLane = d; @@ -140,6 +146,12 @@ export function uiFieldLanes(field, context) { } } + function hasData(direction, lane) { + var data = metadata.turnLanes[direction][lane]; + if (data) { + return data.indexOf('none') === -1; + } + } function change(t, onInput) { var tag = {}; @@ -157,7 +169,7 @@ export function uiFieldLanes(field, context) { tag['turn:lanes:backward'] = undefined; } else { curLane = curLane >= metadata[curDir] ? 0 : curLane; - + tag.lanes = metadata.forward + metadata.backward + metadata.bothways + ''; tag['lanes:forward'] = metadata.forward + ''; tag['lanes:backward'] = metadata.backward + ''; @@ -202,7 +214,6 @@ export function formPipes(data, len, nullKey) { } var isEmpty = _.every(piped, function (i) { - if (!_.isArray(i)) throw new exception('I is not array'); return i.indexOf(nullKey) > -1; }); diff --git a/modules/ui/fields/lanes/laneInfo.js b/modules/ui/fields/lanes/laneInfo.js index 6a37fc33a74..4ef1cdd95bd 100644 --- a/modules/ui/fields/lanes/laneInfo.js +++ b/modules/ui/fields/lanes/laneInfo.js @@ -127,148 +127,3 @@ export function uiLaneInfo() { } return utilRebind(laneInfo, dispatch, 'on'); } -// import * as d3 from 'd3'; -// import { validTurnLanes } from '../../../osm/lanes'; -// import { utilRebind } from '../../../util/rebind'; -// import { formPipes } from '../lanes'; -// import { utilGetSetValue } from '../../../util/get_set_value'; - -// import _ from 'lodash'; - -// export function uiLaneInfo() { -// var dispatch = d3.dispatch('change'); -// function laneInfo(selection, metadata, curDirection, curLane) { -// // TODO: make this thing dynamic and show keysConsidered -// // wrt to oneway. -// var oneway = metadata.oneway; - -// var wrapper = selection -// .selectAll('.lanes-info') -// .data([0]); - -// var enter = wrapper.enter() -// .append('div') -// .attr('class', 'lanes-info lanes-wrap preset-input-wrap'); - -// wrapper = wrapper -// .merge(enter); - -// var input = wrapper.selectAll('.lanes-info') -// .data(oneway ? [{ -// text: 'Lanes', -// dir: 'oneway' -// }] : [{ -// text: 'Forward Lanes', -// dir: 'forward' -// }, -// { -// text: 'Backward Lanes', -// dir: 'backward' -// }]); - -// input.exit() -// .remove(); - -// input = input.enter() -// .append('input') -// .attr('class', 'lanes-info') -// .attr('type', 'lane-count') -// .merge(input); - -// input -// .attr('placeholder', function (d) { -// return d.text; -// }) -// .attr('direction', function (d) { -// return d.dir; -// }) -// .on('input', change) -// .on('blur', change) -// .on('change', change) -// .each(function (d) { -// if (d.dir === 'oneway') { -// this.value = metadata.count; -// return; -// } -// this.value = metadata[d.dir]; -// }); - - -// // var items = list.selectAll('li') -// // .data(keysConsidered); - -// // Enter -// // var enter = items.enter() -// // .append('li') -// // .attr('class', function (d) { return 'cf preset-access-' + d; }); - -// // enter -// // .append('span') -// // .attr('class', 'col6 label preset-label-') -// // .attr('for', function (d) { return 'preset-input-' + d; }) -// // .text(function (d) { return d; }); - -// // enter -// // .append('div') -// // .attr('class', 'col6 preset-input-lanes-wrap') -// // .append('input') -// // .attr('type', 'text') -// // .attr('class', 'preset-input-lanes') -// // .attr('id', function (d) { return 'preset-input-lanes-' + d; }) -// // .each(function (d) { -// // this.value = metadata[d]; -// // }); - -// // // Update -// // items = items.merge(enter); -// // items.selectAll('input') -// // .property('value', function (d) { -// // return metadata[d]; -// // }); - -// // var input = items.selectAll('input'); - -// // input -// // .on('change', change) -// // .on('blur', change); - -// function change(d) { -// var tag = {}; -// metadata.count = utilGetSetValue(selection.selectAll('input')); - -// if (metadata.oneway) { -// if (d === 'count') { -// var count = utilGetSetValue(d3.select(this)); -// count = parseInt(count, 10); -// if (!_.isNaN(count)) metadata.count = count; -// } -// if (d === 'reverse') { -// if (utilGetSetValue(d3.select(this)) === 'true') { -// metadata.reverse = true; -// } else { -// metadata.reverse = false; -// } -// } - -// tag.lanes = Number(metadata.count).toString(); -// tag['lanes:forward'] = undefined; -// tag['lanes:backward'] = undefined; - -// } else { -// if (d === 'forward') { -// var forward = utilGetSetValue(d3.select(this)); -// forward = parseInt(forward, 10); -// if (!_.isNaN(forward)) metadata.forward = forward; -// } -// if (d === 'backward') { -// var backward = utilGetSetValue(d3.select(this)); -// backward = parseInt(backward, 10); -// if (!_.isNaN(backward)) metadata.backward = backward; -// } -// } - -// dispatch.call('change', this); -// } -// } -// return utilRebind(laneInfo, dispatch, 'on'); -// } \ No newline at end of file diff --git a/modules/ui/fields/lanes/turn.js b/modules/ui/fields/lanes/turn.js index 66542a37fe3..7a026da25dc 100644 --- a/modules/ui/fields/lanes/turn.js +++ b/modules/ui/fields/lanes/turn.js @@ -1,23 +1,28 @@ import * as d3 from 'd3'; -import { validTurnLanes } from '../../../osm/lanes'; import { utilRebind } from '../../../util/rebind'; -import { formPipes } from '../lanes'; import _ from 'lodash'; +import { svgIcon } from '../../../svg/index'; -export function uiTurnLanes(field) { +var validTurnLanes = [ + 'left', 'right', 'slight_left', 'slight_right', 'sharp_left', + 'sharp_right', 'merge_to_left', 'merge_to_right', 'reverse', 'through' +]; + +export function uiTurnLanes() { var dispatch = d3.dispatch('change'); function turnLanes(selection, metadata, curDirection, curLane) { var turnLanesData = metadata.turnLanes; - var valid = validTurnLanes.map(function (t) { - var dir = turnLanesData[curDirection][curLane]; - if (!dir) return false; - return dir.indexOf(t) > -1; - }); - + var valid = validTurnLanes.map(function (d) { + var directions = turnLanesData[curDirection][curLane]; + return { + dir: d, + active: directions && directions.indexOf(d) > -1 + }; + }); var wrapper = selection .selectAll('.turn-lanes') - .data([0]); + .data([curLane]); wrapper.exit().remove(); @@ -25,72 +30,76 @@ export function uiTurnLanes(field) { .append('div') .attr('class', 'turn-lanes localized-wrap'); - enter + var label = enter .append('label') .attr('class', 'form-label entry') .text('Turn Lanes'); - enter + var buttonWrap = label .append('div') - .attr('class', 'lane-tags preset-input-wrap checkselect'); + .attr('class', 'form-label-button-wrap'); + + buttonWrap.append('button') + .attr('class', 'remove-icon') + .attr('tabindex', -1) + .call(svgIcon('#operation-delete')); + + enter + .append('ul') + .attr('class', 'turn-lane-tags preset-input-wrap checkselect'); wrapper = wrapper .merge(enter); - var label = wrapper - .selectAll('.lane-tags') - .selectAll('label') - .data(valid); - label.exit() - .remove(); + // Updatoe + // wrapper + // .selectAll('.form-label') + // .text(function (d) { + // return 'Turn Lane #' + (curLane + 1); + // }); + + wrapper.selectAll('.remove-icon') + .on('click', remove); - var labelEnter = label.enter() - .append('label'); + var dirWrapper = wrapper.selectAll('ul') + .selectAll('.turn-lanes-direction') + .data(valid); - labelEnter - .append('input') - .property('direction', function (d, i) { return validTurnLanes[i]; }) - .property('indeterminate', field.type === 'check') - .attr('type', 'checkbox') - .attr('id', 'preset-input-' + field.id); + dirWrapper.exit().remove(); + var row = dirWrapper + .enter() + .append('li') + .attr('class', 'label col6 turn-lanes-direction') + .text(function (d, i) { return d.dir; }); - labelEnter - .append('span') - .text(function (d, i) { return validTurnLanes[i]; }) - .attr('class', 'value'); + dirWrapper = dirWrapper + .merge(row); - label = label - .merge(labelEnter) - .select('input') - .property('checked', function (d) { - return d; + // Update + dirWrapper + .classed('active', function (d) { return d.active; }) + .classed('left-border', function(d, i) { + return i%2 === 1; }) .on('click', change); - function change() { + function remove() { + d3.event.stopPropagation(); + turnLanesData[curDirection][curLane] = ['none']; + dispatch.call('change', this); + } + function change(d) { d3.event.stopPropagation(); - var input = selection.selectAll('input'); - var direction = d3.select(this).property('direction'); var newDirs = []; - - input.each(function (d, i) { - var value = d3.select(this).property('checked'); - var direction = d3.select(this).property('direction'); - if (value) { - newDirs.push(direction); - } + d.active = !d.active; + valid.forEach(function (v) { + if (v.active) newDirs.push(v.dir); }); - if (direction === 'none' || newDirs.length === 0) { - newDirs = ['none']; - } else { - _.pull(newDirs, 'none'); - } - + if (newDirs.length === 0) newDirs = ['none']; turnLanesData[curDirection][curLane] = newDirs; - dispatch.call('change', this); } } From 512468a57f508bf3c38d8a2a82f61c57ae39bb4e Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sun, 5 Feb 2017 22:16:18 +0530 Subject: [PATCH 23/25] svg lanes ui cleanup --- css/app.css | 39 ++++++++------------------------------- modules/svg/lanes.js | 41 ++++++++++++++++++++--------------------- 2 files changed, 28 insertions(+), 52 deletions(-) diff --git a/css/app.css b/css/app.css index 36119c7d363..30925ce7d79 100644 --- a/css/app.css +++ b/css/app.css @@ -1303,7 +1303,7 @@ button.save.has-count .count::before { } .lane-selector .lane-index.has-data { - color: #7092ff; + font-weight: bold; } .lane-selector .lane-index:last-child { @@ -1347,39 +1347,16 @@ button.save.has-count .count::before { border-bottom-right-radius: 4px; } - -/* -.lane-tags > label:hover { - background-color: #ececec; -} - -.lane-tags > label:not(:last-child) { - border-bottom: 1px solid #ccc; -} - -.lane-tags > label:last-child { - border-radius: 0 0 3px 3px; -} - -.lane-tags label > span { - display: block; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +.lanes-svg-wrapper .lanes-circle { + stroke-width: 2; + stroke: #000; + fill-opacity: 0.75; + fill: #fff; } -.lane-tags > label.active { - background: #E8EBFF; -}*/ - -/*lane-visualizer*/ - -.lane-visualizer-background { - fill: #000; - fill-opacity: 0.5; +.lanes-svg-wrapper .lanes-circle.backward { + stroke-dasharray: 3 5; } - - /* preset form multicombo */ .form-field-multicombo { diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index 285f0c4b5b9..3d82cbe9ff2 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -23,7 +23,7 @@ export function svgLanes(projection, context) { var driveLeft; var layoutSeq = []; var iconWidth = 40; - var zoomLimit = zoom >= 18; + var zoomLimit = zoom >= 19.5; // TODO: on removing map features svgLanes stays there if (entity) { @@ -40,7 +40,7 @@ export function svgLanes(projection, context) { // Each DOM node bound to data will have a special __data__ property // you can see it in Chrome developer tools var wrapper = selection.selectAll('.layer-hit') - .selectAll('.lanes-wrapper') + .selectAll('.lanes-svg-wrapper') .data(wrapperData && zoomLimit ? [wrapperData] : []); // wrapper EXIT @@ -54,11 +54,9 @@ export function svgLanes(projection, context) { // the normal thing to do here is create the missing DOM nodes var enter = wrapper.enter() .insert('g', ':first-child') - .attr('class', 'lanes-wrapper'); + .attr('class', 'lanes-svg-wrapper'); + - enter - .append('rect') - .attr('class', 'lanes-background'); // enter.append('polygon') // .attr('points', '-3,4 5,0 -3,-4') @@ -82,12 +80,12 @@ export function svgLanes(projection, context) { return 'translate(' + p[0] + ',' + p[1] + ') rotate(' + ang + ')'; }); - wrapper.selectAll('.lanes-background') - .attr('transform', function () { - return 'translate(' + metadata.count * iconWidth / (-2) + ', 0)'; - }) - .attr('width', function () { return metadata.count * iconWidth; }) - .attr('height', function () { return iconWidth; }); + // wrapper.selectAll('.lanes-background') + // .attr('transform', function () { + // return 'translate(' + metadata.count * iconWidth / (-2) + ', 0)'; + // }) + // .attr('width', function () { return metadata.count * iconWidth; }) + // .attr('height', function () { return iconWidth; }); @@ -136,15 +134,16 @@ export function svgLanes(projection, context) { // situation, it's the behavior we want, so we can style the circle based on `d.dir`. // `select` propagates __data__ to children, `selectAll` does not. lanes.select('.lanes-circle') - .style('fill', function (d) { - switch (d.dir) { - case 'forward': - return '#dfffdf'; - case 'backward': - return '#ffd8d8'; - default: - return '#d8d8d8'; - } + .classed('backward', function (d) { + return d.dir === 'backward'; + // switch (d.dir) { + // case 'forward': + // return '#dfffdf'; + // case 'backward': + // return '#ffd8d8'; + // default: + // return '#d8d8d8'; + // } }); From b84323f2b5897adec80202b5cf2f5eb44b52f692 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sun, 5 Feb 2017 22:26:33 +0530 Subject: [PATCH 24/25] lanesInfo cleanup --- modules/ui/fields/lanes.js | 4 +--- modules/ui/fields/lanes/laneInfo.js | 7 ++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 30cce898b63..1a1a1557122 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -1,13 +1,11 @@ import * as d3 from 'd3'; -import { utilRebind } from '../../util/rebind'; import _ from 'lodash'; -import { utilGetSetValue } from '../../util/get_set_value'; +import { utilRebind } from '../../util/rebind'; import { uiTurnLanes } from './lanes/turn'; import { uiLaneInfo } from './lanes/laneInfo'; export function uiFieldLanes(field, context) { var dispatch = d3.dispatch('change'), - // TODO: currentLane if big like 6 goes crazy if other wayID has less than 6 lanes curLane = 0, curDir = 'unspecified', wayID, diff --git a/modules/ui/fields/lanes/laneInfo.js b/modules/ui/fields/lanes/laneInfo.js index 4ef1cdd95bd..9d886b0bc53 100644 --- a/modules/ui/fields/lanes/laneInfo.js +++ b/modules/ui/fields/lanes/laneInfo.js @@ -1,14 +1,12 @@ import * as d3 from 'd3'; -import { validTurnLanes } from '../../../osm/lanes'; import { utilRebind } from '../../../util/rebind'; -import { formPipes } from '../lanes'; import { utilGetSetValue } from '../../../util/get_set_value'; import _ from 'lodash'; export function uiLaneInfo() { var dispatch = d3.dispatch('change'); - function laneInfo(selection, metadata, curDirection, curLane) { + function laneInfo(selection, metadata) { var s = selection.selectAll('.lanes-info').data([0]); s = s.enter() .append('div') @@ -35,6 +33,7 @@ export function uiLaneInfo() { .data(metadata.oneway ? ['count'] : ['forward', 'backward']); items.exit().remove(); + // Enter var enter = items.enter() .append('li') @@ -67,8 +66,6 @@ export function uiLaneInfo() { .attr('class', 'decrement') .attr('tabindex', -1); - - // Update items = items.merge(enter); From 8014de2efb581f5890dd51559cfecd13e6fd80ef Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Sun, 5 Feb 2017 22:44:57 +0530 Subject: [PATCH 25/25] css cleanup --- .gitignore | 1 + .vscode/launch.json | 19 ------ .vscode/settings.json | 3 - css/app.css | 22 +------ data/core.yaml | 12 ++++ dist/locales/en.json | 14 ++++ modules/geo/geo.js | 6 +- modules/geo/index.js | 1 - modules/modes/select.js | 4 +- modules/renderer/map.js | 5 +- modules/svg/lanes.js | 27 ++++---- modules/svg/midpoints.js | 2 + modules/ui/entity_editor.js | 1 + modules/ui/fields/lanes/turn.js | 13 +--- modules/ui/map_in_map.js | 49 ++++++-------- modules/ui/preset.js | 1 + modules/ui/radial_menu.js | 1 + temp.js | 113 -------------------------------- test/spec/osm/lanes.js | 2 +- 19 files changed, 74 insertions(+), 222 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/settings.json delete mode 100644 temp.js diff --git a/.gitignore b/.gitignore index c2925d6a7de..4335eac6196 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ dist/mapillary-js/ node_modules/ npm-debug.log transifex.auth +.vscode # autogenerated symlinks land.html diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 064c90f7b3c..00000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Chrome against localhost", - "type": "chrome", - "request": "launch", - "url": "http://localhost:8080/#background=Bing&id=w26283961&map=18.71/12.98164/77.63874", - "webRoot": "${workspaceRoot}" - }, - { - "name": "Attach to Chrome", - "type": "chrome", - "request": "attach", - "port": 9222, - "webRoot": "${workspaceRoot}" - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0c4a6693fab..00000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "vsicons.presets.angular": false -} \ No newline at end of file diff --git a/css/app.css b/css/app.css index 30925ce7d79..8d3aa4855e3 100644 --- a/css/app.css +++ b/css/app.css @@ -1223,14 +1223,10 @@ button.save.has-count .count::before { /* lanes */ -.form-field-lanes { - -} - .form-field-lanes .lanes-info input { min-height: 30px; - padding: 5px 10px; - width: 100%; + /*padding: 5px 10px; + width: 100%;*/ border-radius: 0; border-width: 0; border-left-width: 1px; @@ -1249,19 +1245,6 @@ button.save.has-count .count::before { border-top: 0 } -/* -.form-field-lanes .preset-input-lanes-wrap input { - border-top: 0; - border-left-width: 1px; -}*/ - -/* -.form-field-lanes .preset-input-lanes-wrap input { - border-radius: 0; - border-width: 0; - border-left-width: 1px; -} -*/ .form-field-lanes .preset-input-wrap li { border-bottom: 1px solid #CCC; } @@ -1279,7 +1262,6 @@ button.save.has-count .count::before { .lane-selector ul { display: flex; flex-direction: row; - } .lane-selector .lane-index { diff --git a/data/core.yaml b/data/core.yaml index 3aa09cd67e0..6f4e51e421c 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -838,3 +838,15 @@ en: help: "You can replay this walkthrough or view more documentation by clicking the {button} Help button." save: "Don't forget to regularly save your changes!" start: "Start mapping!" + lanes: + turn: + left: "Left" + right: "Right" + slight_left: "Slight left" + slight_right: "Slight right" + sharp_left: "Sharp left" + sharp_right: "Sharp right" + merge_to_left: "Merge left" + merge_to_right: "Merge right" + reverse: "Reverse" + through: "Through" \ No newline at end of file diff --git a/dist/locales/en.json b/dist/locales/en.json index 04eac3a490b..afb7eea9e25 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -697,6 +697,20 @@ "start": "Start mapping!" } }, + "lanes": { + "turn": { + "left": "Left", + "right": "Right", + "slight_left": "Slight left", + "slight_right": "Slight right", + "sharp_left": "Sharp left", + "sharp_right": "Sharp right", + "merge_to_left": "Merge left", + "merge_to_right": "Merge right", + "reverse": "Reverse", + "through": "Through" + } + }, "presets": { "categories": { "category-barrier": { diff --git a/modules/geo/geo.js b/modules/geo/geo.js index f0c78dda0ec..096c0a18e0d 100644 --- a/modules/geo/geo.js +++ b/modules/geo/geo.js @@ -11,11 +11,6 @@ export function geoInterp(p1, p2, t) { p1[1] + (p2[1] - p1[1]) * t]; } -// returns a point which divides the line joining p1, p2 -// in the ratio m:n -export function geoDivideSegment(p1, p2, m, n) { - return [(p2[0]*n + p2[0]*m)/(m+n), (p2[1]*n + p2[1]*m)/(m+n)]; -} // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product. // Returns a positive value, if OAB makes a counter-clockwise turn, @@ -108,6 +103,7 @@ export function geoAngle(a, b, projection) { return Math.atan2(b[1] - a[1], b[0] - a[0]); } + // Rotate all points counterclockwise around a pivot point by given angle export function geoRotate(points, angle, around) { return points.map(function(point) { diff --git a/modules/geo/index.js b/modules/geo/index.js index 8e060f7f787..5e37b16fe04 100644 --- a/modules/geo/index.js +++ b/modules/geo/index.js @@ -21,4 +21,3 @@ export { geoPointInPolygon } from './geo.js'; export { geoPolygonContainsPolygon } from './geo.js'; export { geoPolygonIntersectsPolygon } from './geo.js'; export { geoSphericalDistance } from './geo.js'; -export { geoDivideSegment } from './geo.js'; \ No newline at end of file diff --git a/modules/modes/select.js b/modules/modes/select.js index d87c76c984c..27032262e84 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -146,9 +146,9 @@ export function modeSelect(context, selectedIDs) { function positionMenu() { - var entity = singular(); if (suppressMenu || !radialMenu) { return; } + var entity = singular(); if (entity && context.geometry(entity.id) === 'relation') { suppressMenu = true; } else if (entity && entity.type === 'node') { @@ -158,12 +158,10 @@ export function modeSelect(context, selectedIDs) { viewport = geoExtent(context.projection.clipExtent()).polygon(); if (geoPointInPolygon(point, viewport)) { radialMenu.center(point); - } else { suppressMenu = true; } } - console.log(entity); } diff --git a/modules/renderer/map.js b/modules/renderer/map.js index 9b8794fc016..91a146d71c6 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -63,7 +63,6 @@ export function rendererMap(context) { function map(selection) { - _selection = selection; context @@ -162,7 +161,7 @@ export function rendererMap(context) { surface.selectAll('.data-layer-osm') .call(drawVertices, graph, all, filter, map.extent(), map.zoom()) .call(drawMidpoints, graph, all, filter, map.trimmedExtent()) - .call(drawLanes, graph, all, filter, map.trimmedExtent(), map.zoom(), map.center()); + .call(drawLanes, graph, all, filter, dimensions, map.zoom(), map.center()); dispatch.call('drawn', this, {full: false}); } }); @@ -255,7 +254,7 @@ export function rendererMap(context) { .call(drawLines, graph, data, filter) .call(drawAreas, graph, data, filter) .call(drawMidpoints, graph, data, filter, map.trimmedExtent()) - .call(drawLanes, graph, data, filter, map.trimmedExtent(), map.zoom(), map.center()) + .call(drawLanes, graph, data, filter, dimensions, map.zoom(), map.center()) .call(drawLabels, graph, data, filter, dimensions, !difference && !extent) .call(drawPoints, graph, data, filter); diff --git a/modules/svg/lanes.js b/modules/svg/lanes.js index 3d82cbe9ff2..42366795077 100644 --- a/modules/svg/lanes.js +++ b/modules/svg/lanes.js @@ -1,8 +1,5 @@ import _ from 'lodash'; -import { - svgPointTransform, - svgTagClasses -} from './index'; + import { geoAngle, @@ -12,12 +9,11 @@ import { } from '../geo/index'; import { getLayoutSeq } from '../osm/lanes'; import { dataDriveLeft } from '../../data'; -import { geoPointInPolygon } from '../geo'; +import { geoPointInPolygon, geoExtent } from '../geo'; export function svgLanes(projection, context) { - - return function drawLanes(selection, graph, entities, filter, extent, zoom, mapCenter) { + return function drawLanes(selection, graph, entities, filter, dimensions, zoom, mapCenter) { var entity = getEntity(); var metadata; var driveLeft; @@ -57,12 +53,6 @@ export function svgLanes(projection, context) { .attr('class', 'lanes-svg-wrapper'); - - // enter.append('polygon') - // .attr('points', '-3,4 5,0 -3,-4') - // .attr('class', 'fill'); - - // wrapper UPDATE // update selection runs every time for all the matched DOM elements. // `merge` brings in the nodes that were just entered @@ -119,7 +109,7 @@ export function svgLanes(projection, context) { lanes .attr('transform', function (d, i) { - var transform = 'translate(' + [iconWidth / 2 + i * iconWidth - metadata.count * iconWidth / (2) , (iconWidth / 2)] + ')'; + var transform = 'translate(' + [iconWidth / 2 + i * iconWidth - metadata.count * iconWidth / (2), (iconWidth / 2)] + ')'; transform += d.turnLanes.indexOf('right') > -1 ? ' scale(-1, 1)' : ''; if (d.dir === 'backward') { transform += ' rotate(180)'; } return transform; @@ -168,8 +158,14 @@ export function svgLanes(projection, context) { return entity; } + function trimmedExtent() { + var headerY = 60, footerY = 30, pad = 90; + return new geoExtent(projection.invert([pad, dimensions[1] - footerY - pad]), + projection.invert([dimensions[0] - pad, headerY + pad])); + } function findPosition() { + var extent = trimmedExtent(); var loc; if (!entity) return; @@ -180,7 +176,7 @@ export function svgLanes(projection, context) { var a = nodes[j - 1]; var b = nodes[j]; if (geoEuclideanDistance(projection(a.loc), projection(b.loc)) > 40) { - var point = geoInterp(a.loc, b.loc, 0.75); + var point = geoInterp(a.loc, b.loc, 0.77); if (extent.intersects(point)) { loc = point; } else { @@ -206,3 +202,4 @@ export function svgLanes(projection, context) { }; } + diff --git a/modules/svg/midpoints.js b/modules/svg/midpoints.js index b99110839a8..a64b922254a 100644 --- a/modules/svg/midpoints.js +++ b/modules/svg/midpoints.js @@ -27,6 +27,7 @@ export function svgMidpoints(projection, context) { continue; if (context.selectedIDs().indexOf(entity.id) < 0) continue; + var nodes = graph.childNodes(entity); for (var j = 0; j < nodes.length - 1; j++) { @@ -91,6 +92,7 @@ export function svgMidpoints(projection, context) { .selectAll('g.midpoint') .filter(midpointFilter) .data(_.values(midpoints), function(d) { return d.id; }); + groups.exit() .remove(); diff --git a/modules/ui/entity_editor.js b/modules/ui/entity_editor.js index ffe2b871650..e5fc7bb9070 100644 --- a/modules/ui/entity_editor.js +++ b/modules/ui/entity_editor.js @@ -180,6 +180,7 @@ export function uiEntityEditor(context) { var blacklist = ['description', 'note', 'fixme']; if (_.some(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v; + var cleaned = v.split(';') .map(function(s) { return s.trim(); }) .join(keepSpaces(k) ? '; ' : ';'); diff --git a/modules/ui/fields/lanes/turn.js b/modules/ui/fields/lanes/turn.js index 7a026da25dc..4da9cf22ebf 100644 --- a/modules/ui/fields/lanes/turn.js +++ b/modules/ui/fields/lanes/turn.js @@ -1,7 +1,7 @@ import * as d3 from 'd3'; import { utilRebind } from '../../../util/rebind'; -import _ from 'lodash'; import { svgIcon } from '../../../svg/index'; +import { t } from '../../../util/locale'; var validTurnLanes = [ 'left', 'right', 'slight_left', 'slight_right', 'sharp_left', @@ -48,17 +48,10 @@ export function uiTurnLanes() { .append('ul') .attr('class', 'turn-lane-tags preset-input-wrap checkselect'); + // Update wrapper = wrapper .merge(enter); - - // Updatoe - // wrapper - // .selectAll('.form-label') - // .text(function (d) { - // return 'Turn Lane #' + (curLane + 1); - // }); - wrapper.selectAll('.remove-icon') .on('click', remove); @@ -72,7 +65,7 @@ export function uiTurnLanes() { .enter() .append('li') .attr('class', 'label col6 turn-lanes-direction') - .text(function (d, i) { return d.dir; }); + .text(function (d) { return t('lanes.turn.' + d.dir); }); dirWrapper = dirWrapper .merge(row); diff --git a/modules/ui/map_in_map.js b/modules/ui/map_in_map.js index 5b19007da6d..1b383914511 100644 --- a/modules/ui/map_in_map.js +++ b/modules/ui/map_in_map.js @@ -10,8 +10,8 @@ import { utilGetDimensions } from '../util/dimensions'; var TAU = 2 * Math.PI; function ztok(z) { return 256 * Math.pow(2, z) / TAU; } function ktoz(k) { return Math.log(k * TAU) / Math.LN2 - 8; } -function vecSub(a, b) { return [ a[0] - b[0], a[1] - b[1] ]; } -function vecScale(a, b) { return [ a[0] * b, a[1] * b ]; } +function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; } +function vecScale(a, b) { return [a[0] * b, a[1] * b]; } export function uiMapInMap(context) { @@ -209,7 +209,7 @@ export function uiMapInMap(context) { var overlays = overlay .selectAll('div') - .data(activeOverlayLayers, function(d) { return d.source().name(); }); + .data(activeOverlayLayers, function (d) { return d.source().name(); }); overlays.exit() .remove(); @@ -217,7 +217,7 @@ export function uiMapInMap(context) { overlays = overlays.enter() .append('div') .merge(overlays) - .each(function(layer) { d3.select(this).call(layer); }); + .each(function (layer) { d3.select(this).call(layer); }); var dataLayers = tiles @@ -237,43 +237,34 @@ export function uiMapInMap(context) { // redraw viewport bounding box if (gesture !== 'pan') { - // var getPath = d3.geoPath(projection), - var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] }; + var getPath = d3.geoPath(projection), + bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] }; viewport = wrap.selectAll('.map-in-map-viewport') .data([0]); viewport = viewport.enter() - .append('canvas') - // .attr('class', 'map-in-map-viewport') - .attr('height', 300) - .attr('width', 300) + .append('svg') + .attr('class', 'map-in-map-viewport') .merge(viewport); - var path2 = viewport.selectAll('.map-in-map-bbox') + var path = viewport.selectAll('.map-in-map-bbox') .data([bbox]); - var ctx = d3.select('canvas').node().getContext('2d'); - var path = d3.geoPath() - .projection(projection) - .context(ctx); - ctx.beginPath(); - path(bbox); - ctx.stroke(); - // path.enter() - // .append('path') - // .attr('class', 'map-in-map-bbox') - // .merge(path) - // .attr('d', getPath) - // .classed('thick', function(d) { return getPath.area(d) < 30; }); + path.enter() + .append('path') + .attr('class', 'map-in-map-bbox') + .merge(path) + .attr('d', getPath) + .classed('thick', function (d) { return getPath.area(d) < 30; }); } } function queueRedraw() { clearTimeout(timeoutId); - timeoutId = setTimeout(function() { redraw(); }, 750); + timeoutId = setTimeout(function () { redraw(); }, 750); } @@ -293,7 +284,7 @@ export function uiMapInMap(context) { .transition() .duration(200) .style('opacity', '0') - .on('end', function() { + .on('end', function () { selection.selectAll('.map-in-map') .style('display', 'none'); }); @@ -304,7 +295,7 @@ export function uiMapInMap(context) { .transition() .duration(200) .style('opacity', '1') - .on('end', function() { + .on('end', function () { redraw(); }); } @@ -325,7 +316,7 @@ export function uiMapInMap(context) { .merge(wrap); context.map() - .on('drawn.map-in-map', function(drawn) { + .on('drawn.map-in-map', function (drawn) { if (drawn.full === true) { redraw(); } @@ -341,4 +332,4 @@ export function uiMapInMap(context) { } return map_in_map; -} +} \ No newline at end of file diff --git a/modules/ui/preset.js b/modules/ui/preset.js index 12b9a26af1a..77c586626dd 100644 --- a/modules/ui/preset.js +++ b/modules/ui/preset.js @@ -23,6 +23,7 @@ export function uiPreset(context) { // Field Constructor function UIField(field, entity, show) { field = _.clone(field); + field.input = uiFields[field.type](field, context) .on('change', function(t, onInput) { dispatch.call('change', field, t, onInput); diff --git a/modules/ui/radial_menu.js b/modules/ui/radial_menu.js index 1abc0ebff20..8c62abb5b25 100644 --- a/modules/ui/radial_menu.js +++ b/modules/ui/radial_menu.js @@ -8,6 +8,7 @@ export function uiRadialMenu(context, operations) { center = [0, 0], tooltip; + var radialMenu = function(selection) { if (!operations.length) return; diff --git a/temp.js b/temp.js deleted file mode 100644 index e5f4fcc23b1..00000000000 --- a/temp.js +++ /dev/null @@ -1,113 +0,0 @@ -import * as d3 from 'd3'; -import { validTurnLanes } from '../../../osm/lanes'; -import { utilRebind } from '../../../util/rebind'; -import { formPipes } from '../lanes'; -import { utilGetSetValue } from '../../../util/get_set_value'; - -import _ from 'lodash'; - -export function uiLaneInfo() { - var dispatch = d3.dispatch('change'); - function laneInfo(selection, metadata, curDirection, curLane) { - // TODO: make this thing dynamic and show keysConsidered - // wrt to oneway. - var keysConsidered = ['count', 'forward', 'backward', 'reverse']; - var s = selection.selectAll('.lanes-info').data([0]); - s = s.enter() - .append('div') - .attr('class', 'lanes-info') - .merge(s); - - var wrap = s.selectAll('.preset-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'preset-input-wrap') - .append('ul') - .merge(wrap); - - var list = wrap.selectAll('ul') - .data([0]); - - list = list.enter() - .append('ul') - .merge(list); - - - var items = list.selectAll('li') - .data(keysConsidered); - - // Enter - var enter = items.enter() - .append('li') - .attr('class', function (d) { return 'cf preset-access-' + d; }); - - enter - .append('span') - .attr('class', 'col6 label preset-label-') - .attr('for', function (d) { return 'preset-input-' + d; }) - .text(function (d) { return d; }); - - enter - .append('div') - .attr('class', 'col6 preset-input-lanes-wrap') - .append('input') - .attr('type', 'text') - .attr('class', 'preset-input-lanes') - .attr('id', function (d) { return 'preset-input-lanes-' + d; }) - .each(function (d) { - this.value = metadata[d]; - }); - - // Update - items = items.merge(enter); - items.selectAll('input') - .property('value', function (d) { - return metadata[d]; - }); - - var input = items.selectAll('input'); - - input - .on('change', change) - .on('blur', change); - - function change(d) { - var tag = {}; - if (metadata.oneway) { - if (d === 'count') { - var count = utilGetSetValue(d3.select(this)); - count = parseInt(count, 10); - if (!_.isNaN(count)) metadata.count = count; - } - if (d === 'reverse') { - if (utilGetSetValue(d3.select(this)) === 'true') { - metadata.reverse = true; - } else { - metadata.reverse = false; - } - } - - tag.lanes = Number(metadata.count).toString(); - tag['lanes:forward'] = undefined; - tag['lanes:backward'] = undefined; - - } else { - if (d === 'forward') { - var forward = utilGetSetValue(d3.select(this)); - forward = parseInt(forward, 10); - if (!_.isNaN(forward)) metadata.forward = forward; - } - if (d === 'backward') { - var backward = utilGetSetValue(d3.select(this)); - backward = parseInt(backward, 10); - if (!_.isNaN(backward)) metadata.backward = backward; - } - } - - dispatch.call('change', this); - } - } - return utilRebind(laneInfo, dispatch, 'on'); -} \ No newline at end of file diff --git a/test/spec/osm/lanes.js b/test/spec/osm/lanes.js index 99a22b21bbe..289c3afd064 100644 --- a/test/spec/osm/lanes.js +++ b/test/spec/osm/lanes.js @@ -1,4 +1,4 @@ -describe.only('iD.Lanes', function () { +describe('iD.Lanes', function () { describe('default lane tags', function () {