From d4b162266794982ebae2417a3b5f4d5bf219d7e4 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Sat, 14 Jul 2018 19:15:25 -0400 Subject: [PATCH 1/7] moved getTiles and filtering to utils --- dist/locales/en.json | 4 +- modules/lib/d3.geo.tile.js | 93 ----------------- modules/lib/index.js | 1 - modules/renderer/tile_layer.js | 5 +- modules/services/mapillary.js | 46 +-------- modules/services/openstreetcam.js | 65 +++--------- modules/services/osm.js | 30 +----- modules/services/streetside.js | 50 +-------- modules/util/index.js | 1 + modules/util/tile.js | 165 ++++++++++++++++++++++++++++++ package.json | 2 +- 11 files changed, 199 insertions(+), 263 deletions(-) delete mode 100644 modules/lib/d3.geo.tile.js create mode 100644 modules/util/tile.js diff --git a/dist/locales/en.json b/dist/locales/en.json index 8e9018bfb..3931424de 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -6681,7 +6681,7 @@ "attribution": { "text": "Terms & Feedback" }, - "description": "DigitalGlobe-Premium is a mosaic composed of DigitalGlobe basemap with select regions filled with +Vivid or custom area of interest imagery, 50cm resolution or better, and refreshed more frequently with ongoing updates.", + "description": "Premium DigitalGlobe satellite imagery.", "name": "DigitalGlobe Premium Imagery" }, "DigitalGlobe-Premium-vintage": { @@ -6695,7 +6695,7 @@ "attribution": { "text": "Terms & Feedback" }, - "description": "DigitalGlobe-Standard is a curated set of imagery covering 86% of the earth’s landmass, with 30-60cm resolution where available, backfilled by Landsat. Average age is 2.31 years, with some areas updated 2x per year.", + "description": "Standard DigitalGlobe satellite imagery.", "name": "DigitalGlobe Standard Imagery" }, "DigitalGlobe-Standard-vintage": { diff --git a/modules/lib/d3.geo.tile.js b/modules/lib/d3.geo.tile.js deleted file mode 100644 index 3b0f718ef..000000000 --- a/modules/lib/d3.geo.tile.js +++ /dev/null @@ -1,93 +0,0 @@ -import { range as d3_range } from 'd3-array'; - - -export function d3geoTile() { - var _size = [960, 500]; - var _scale = 256; - var _scaleExtent = [0, 20]; - var _translate = [_size[0] / 2, _size[1] / 2]; - var _zoomDelta = 0; - var _margin = 0; - - function bound(val) { - return Math.min(_scaleExtent[1], Math.max(_scaleExtent[0], val)); - } - - function tile() { - var z = Math.max(Math.log(_scale) / Math.LN2 - 8, 0); - var z0 = bound(Math.round(z + _zoomDelta)); - var k = Math.pow(2, z - z0 + 8); - var origin = [ - (_translate[0] - _scale / 2) / k, - (_translate[1] - _scale / 2) / k - ]; - - var cols = d3_range( - Math.max(0, Math.floor(-origin[0]) - _margin), - Math.max(0, Math.ceil(_size[0] / k - origin[0]) + _margin) - ); - var rows = d3_range( - Math.max(0, Math.floor(-origin[1]) - _margin), - Math.max(0, Math.ceil(_size[1] / k - origin[1]) + _margin) - ); - - var tiles = []; - for (var i = 0; i < rows.length; i++) { - var y = rows[i]; - for (var j = 0; j < cols.length; j++) { - var x = cols[j]; - - if (i >= _margin && i <= rows.length - _margin && - j >= _margin && j <= cols.length - _margin) { - tiles.unshift([x, y, z0]); // tiles in view at beginning - } else { - tiles.push([x, y, z0]); // tiles in margin at the end - } - } - } - - tiles.translate = origin; - tiles.scale = k; - - return tiles; - } - - tile.scaleExtent = function(val) { - if (!arguments.length) return _scaleExtent; - _scaleExtent = val; - return tile; - }; - - tile.size = function(val) { - if (!arguments.length) return _size; - _size = val; - return tile; - }; - - tile.scale = function(val) { - if (!arguments.length) return _scale; - _scale = val; - return tile; - }; - - tile.translate = function(val) { - if (!arguments.length) return _translate; - _translate = val; - return tile; - }; - - tile.zoomDelta = function(val) { - if (!arguments.length) return _zoomDelta; - _zoomDelta = +val; - return tile; - }; - - // number to extend the rows/columns beyond those covering the viewport - tile.margin = function(val) { - if (!arguments.length) return _margin; - _margin = +val; - return tile; - }; - - return tile; -} diff --git a/modules/lib/index.js b/modules/lib/index.js index b6ddad54a..bee98416c 100644 --- a/modules/lib/index.js +++ b/modules/lib/index.js @@ -1,3 +1,2 @@ export { d3combobox } from './d3.combobox'; -export { d3geoTile } from './d3.geo.tile'; export { d3keybinding } from './d3.keybinding'; diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 475147e43..d5c59e9b9 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -1,15 +1,14 @@ import { select as d3_select } from 'd3-selection'; import { t } from '../util/locale'; -import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile'; import { geoScaleToZoom, geoVecLength } from '../geo'; -import { utilPrefixCSSProperty } from '../util'; +import { utilPrefixCSSProperty, utilTile } from '../util'; export function rendererTileLayer(context) { var tileSize = 256; var transformProp = utilPrefixCSSProperty('Transform'); - var geotile = d3_geoTile(); + var geotile = utilTile(); var _projection; var _cache = {}; diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index baed207b8..84395e20c 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -18,11 +18,12 @@ import { import rbush from 'rbush'; -import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile'; import { geoExtent } from '../geo'; import { svgDefs } from '../svg'; import { utilDetect } from '../util/detect'; -import { utilQsString, utilRebind } from '../util'; +import { utilQsString, utilRebind, utilTile } from '../util'; + +var geoTile = utilTile(); var apibase = 'https://a.mapillary.com/v3/'; var viewercss = 'mapillary-js/mapillary.min.css'; @@ -79,49 +80,12 @@ function localeTimestamp(s) { } -function getTiles(projection) { - var s = projection.scale() * 2 * Math.PI; - var z = Math.max(Math.log(s) / Math.log(2) - 8, 0); - var ts = 256 * Math.pow(2, z - tileZoom); - var origin = [ - s / 2 - projection.translate()[0], - s / 2 - projection.translate()[1] - ]; - - return d3_geoTile() - .scaleExtent([tileZoom, tileZoom]) - .scale(s) - .size(projection.clipExtent()[1]) - .translate(projection.translate())() - .map(function(tile) { - var x = tile[0] * ts - origin[0]; - var y = tile[1] * ts - origin[1]; - - return { - id: tile.toString(), - xyz: tile, - extent: geoExtent( - projection.invert([x, y + ts]), - projection.invert([x + ts, y]) - ) - }; - }); -} - - function loadTiles(which, url, projection) { var s = projection.scale() * 2 * Math.PI; var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = getTiles(projection).filter(function(t) { - return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]); - }); - - _filter(which.inflight, function(v, k) { - var wanted = _find(tiles, function(tile) { return k === (tile.id + ',0'); }); - if (!wanted) delete which.inflight[k]; - return !wanted; - }).map(abortRequest); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection)); + geoTile.removeInflightRequests(which, tiles, abortRequest, ',0'); tiles.forEach(function(tile) { loadNextTilePage(which, currZoom, url, tile); diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index 14ded2d6b..848ea1fce 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -22,9 +22,9 @@ import { import rbush from 'rbush'; -import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile'; import { geoExtent } from '../geo'; +import { utilTile } from '../util'; import { utilDetect } from '../util/detect'; import { @@ -33,18 +33,19 @@ import { utilSetTransform } from '../util'; +var geoTile = utilTile(); -var apibase = 'https://openstreetcam.org', - maxResults = 1000, - tileZoom = 14, - dispatch = d3_dispatch('loadedImages'), - imgZoom = d3_zoom() - .extent([[0, 0], [320, 240]]) - .translateExtent([[0, 0], [320, 240]]) - .scaleExtent([1, 15]) - .on('zoom', zoomPan), - _oscCache, - _oscSelectedImage; +var apibase = 'https://openstreetcam.org'; +var maxResults = 1000; +var tileZoom = 14; +var dispatch = d3_dispatch('loadedImages'); +var imgZoom = d3_zoom() + .extent([[0, 0], [320, 240]]) + .translateExtent([[0, 0], [320, 240]]) + .scaleExtent([1, 15]) + .on('zoom', zoomPan); +var _oscCache; +var _oscSelectedImage; function abortRequest(i) { @@ -74,48 +75,12 @@ function maxPageAtZoom(z) { } -function getTiles(projection) { - var s = projection.scale() * 2 * Math.PI, - z = Math.max(Math.log(s) / Math.log(2) - 8, 0), - ts = 256 * Math.pow(2, z - tileZoom), - origin = [ - s / 2 - projection.translate()[0], - s / 2 - projection.translate()[1]]; - - return d3_geoTile() - .scaleExtent([tileZoom, tileZoom]) - .scale(s) - .size(projection.clipExtent()[1]) - .translate(projection.translate())() - .map(function(tile) { - var x = tile[0] * ts - origin[0], - y = tile[1] * ts - origin[1]; - - return { - id: tile.toString(), - xyz: tile, - extent: geoExtent( - projection.invert([x, y + ts]), - projection.invert([x + ts, y]) - ) - }; - }); -} - - function loadTiles(which, url, projection) { var s = projection.scale() * 2 * Math.PI, currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = getTiles(projection).filter(function(t) { - return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]); - }); - - _filter(which.inflight, function(v, k) { - var wanted = _find(tiles, function(tile) { return k === (tile.id + ',0'); }); - if (!wanted) delete which.inflight[k]; - return !wanted; - }).map(abortRequest); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection)); + geoTile.removeInflightRequests(which, tiles, abortRequest, ',0'); tiles.forEach(function(tile) { loadNextTilePage(which, currZoom, url, tile); diff --git a/modules/services/osm.js b/modules/services/osm.js index 9ead1f5b0..8f73006ee 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -13,7 +13,6 @@ import { xml as d3_xml } from 'd3-request'; import osmAuth from 'osm-auth'; import { JXON } from '../util/jxon'; -import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile'; import { geoExtent } from '../geo'; import { osmEntity, @@ -22,8 +21,9 @@ import { osmWay } from '../osm'; -import { utilRebind, utilIdleWorker } from '../util'; +import { utilRebind, utilIdleWorker, utilTile } from '../util'; +var geoTile = utilTile(); var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded'); var urlroot = 'https://www.openstreetmap.org'; @@ -581,30 +581,8 @@ export default { s / 2 - projection.translate()[1] ]; - var tiles = d3_geoTile() - .scaleExtent([_tileZoom, _tileZoom]) - .scale(s) - .size(dimensions) - .translate(projection.translate())() - .map(function(tile) { - var x = tile[0] * ts - origin[0]; - var y = tile[1] * ts - origin[1]; - - return { - id: tile.toString(), - extent: geoExtent( - projection.invert([x, y + ts]), - projection.invert([x + ts, y])) - }; - }); - - _filter(_tiles.inflight, function(v, i) { - var wanted = _find(tiles, function(tile) { - return i === tile.id; - }); - if (!wanted) delete _tiles.inflight[i]; - return !wanted; - }).map(abortRequest); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(_tileZoom, projection)); + geoTile.removeInflightRequests(_tiles, tiles, abortRequest); tiles.forEach(function(tile) { var id = tile.id; diff --git a/modules/services/streetside.js b/modules/services/streetside.js index f13470b6c..944221543 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -17,7 +17,6 @@ import { import rbush from 'rbush'; import { t } from '../util/locale'; import { jsonpRequest } from '../util/jsonp_request'; -import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile'; import { geoExtent, @@ -29,10 +28,12 @@ import { } from '../geo'; import { utilDetect } from '../util/detect'; -import { utilQsString, utilRebind } from '../util'; +import { utilQsString, utilRebind, utilTile } from '../util'; import Q from 'q'; +var geoTile = utilTile(); + var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?'; var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/'; var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm'; @@ -85,46 +86,6 @@ function localeTimestamp(s) { return d.toLocaleString(detected.locale, options); } -/** - * getTiles() returns array of d3 geo tiles. - * Using d3.geo.tiles.js from lib, gets tile extents for each grid tile in a grid created from - * an area around (and including) the current map view extents. - */ -function getTiles(projection, margin) { - // s is the current map scale - // z is the 'Level of Detail', or zoom-level, where Level 1 is far from the earth, and Level 23 is close to the ground. - // ts ('tile size') here is the formula for determining the width/height of the map in pixels, but with a modification. - // See 'Ground Resolution and Map Scale': //https://msdn.microsoft.com/en-us/library/bb259689.aspx. - // As used here, by subtracting constant 'tileZoom' from z (the level), you end up with a much smaller value for the tile size (in pixels). - var s = projection.scale() * 2 * Math.PI; - var z = Math.max(Math.log(s) / Math.log(2) - 8, 0); - var ts = 256 * Math.pow(2, z - tileZoom); - var origin = [ - s / 2 - projection.translate()[0], - s / 2 - projection.translate()[1] - ]; - - var tiler = d3_geoTile() - .scaleExtent([tileZoom, tileZoom]) - .scale(s) - .size(projection.clipExtent()[1]) - .translate(projection.translate()) - .margin(margin || 0); // request nearby tiles so we can connect sequences. - - return tiler() - .map(function(tile) { - var x = tile[0] * ts - origin[0]; - var y = tile[1] * ts - origin[1]; - return { - id: tile.toString(), - xyz: tile, - extent: geoExtent( - projection.invert([x, y + ts]), - projection.invert([x + ts, y]) - ) - }; - }); -} /** * loadTiles() wraps the process of generating tiles and then fetching image points for each tile. @@ -133,10 +94,7 @@ function loadTiles(which, url, projection, margin) { var s = projection.scale() * 2 * Math.PI; var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - // breakup the map view into tiles - var tiles = getTiles(projection, margin).filter(function (t) { - return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]); - }); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, margin)); tiles.forEach(function (tile) { loadNextTilePage(which, currZoom, url, tile); diff --git a/modules/util/index.js b/modules/util/index.js index f64856de8..9673bfef5 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -23,5 +23,6 @@ export { utilSessionMutex } from './session_mutex'; export { utilStringQs } from './util'; export { utilSuggestNames } from './suggest_names'; export { utilTagText } from './util'; +export { utilTile } from './tile'; export { utilTriggerEvent } from './trigger_event'; export { utilWrap } from './util'; diff --git a/modules/util/tile.js b/modules/util/tile.js new file mode 100644 index 000000000..78d9a6423 --- /dev/null +++ b/modules/util/tile.js @@ -0,0 +1,165 @@ +import _filter from 'lodash-es/filter'; +import _find from 'lodash-es/find'; +import { range as d3_range } from 'd3-array'; +import { geoExtent } from '../geo'; + + +export function utilTile() { + var _size = [960, 500]; + var _scale = 256; + var _scaleExtent = [0, 20]; + var _translate = [_size[0] / 2, _size[1] / 2]; + var _zoomDelta = 0; + var _margin = 0; + + function bound(val) { + return Math.min(_scaleExtent[1], Math.max(_scaleExtent[0], val)); + } + + function nearNullIsland(x, y, z) { + if (z >= 7) { + var center = Math.pow(2, z - 1); + var width = Math.pow(2, z - 6); + var min = center - (width / 2); + var max = center + (width / 2) - 1; + return x >= min && x <= max && y >= min && y <= max; + } + return false; + } + + function tile() { + var z = Math.max(Math.log(_scale) / Math.LN2 - 8, 0); + var z0 = bound(Math.round(z + _zoomDelta)); + var k = Math.pow(2, z - z0 + 8); + var origin = [ + (_translate[0] - _scale / 2) / k, + (_translate[1] - _scale / 2) / k + ]; + + var cols = d3_range( + Math.max(0, Math.floor(-origin[0]) - _margin), + Math.max(0, Math.ceil(_size[0] / k - origin[0]) + _margin) + ); + var rows = d3_range( + Math.max(0, Math.floor(-origin[1]) - _margin), + Math.max(0, Math.ceil(_size[1] / k - origin[1]) + _margin) + ); + + var tiles = []; + for (var i = 0; i < rows.length; i++) { + var y = rows[i]; + for (var j = 0; j < cols.length; j++) { + var x = cols[j]; + + if (i >= _margin && i <= rows.length - _margin && + j >= _margin && j <= cols.length - _margin) { + tiles.unshift([x, y, z0]); // tiles in view at beginning + } else { + tiles.push([x, y, z0]); // tiles in margin at the end + } + } + } + + tiles.translate = origin; + tiles.scale = k; + + return tiles; + } + + /** + * getTiles() returns array of d3 geo tiles. + * Using d3.geo.tiles.js from lib, gets tile extents for each grid tile in a grid created from + * an area around (and including) the current map view extents. + */ + tile.getTiles = function(tileZoom, projection, margin) { + // s is the current map scale + // z is the 'Level of Detail', or zoom-level, where Level 1 is far from the earth, and Level 23 is close to the ground. + // ts ('tile size') here is the formula for determining the width/height of the map in pixels, but with a modification. + // See 'Ground Resolution and Map Scale': //https://msdn.microsoft.com/en-us/library/bb259689.aspx. + // As used here, by subtracting constant 'tileZoom' from z (the level), you end up with a much smaller value for the tile size (in pixels). + var s = projection.scale() * 2 * Math.PI; + var z = Math.max(Math.log(s) / Math.log(2) - 8, 0); + var ts = 256 * Math.pow(2, z - tileZoom); + var origin = [ + s / 2 - projection.translate()[0], + s / 2 - projection.translate()[1] + ]; + + var tiler = this + .scaleExtent([tileZoom, tileZoom]) + .scale(s) + .size(projection.clipExtent()[1]) + .translate(projection.translate()) + .margin(margin || 0); // request nearby tiles so we can connect sequences. + + return tiler() + .map(function(tile) { + var x = tile[0] * ts - origin[0]; + var y = tile[1] * ts - origin[1]; + return { + id: tile.toString(), + xyz: tile, + extent: geoExtent( + projection.invert([x, y + ts]), + projection.invert([x + ts, y]) + ) + }; + }); + }; + + tile.filterNullIsland = function(tiles) { + return tiles.filter(function(t) { + return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]); + }); + }; + + // remove inflight requests that no longer cover the view.. + tile.removeInflightRequests = function(cache, tiles, callback, modifier) { + return _filter(cache.inflight, function(v, i) { + var wanted = _find(tiles, function(tile) { return i === tile.id + modifier; }); + if (!wanted) { + delete cache.inflight[i]; + } + return !wanted; + }).map(callback); // abort request + }; + + tile.scaleExtent = function(val) { + if (!arguments.length) return _scaleExtent; + _scaleExtent = val; + return tile; + }; + + tile.size = function(val) { + if (!arguments.length) return _size; + _size = val; + return tile; + }; + + tile.scale = function(val) { + if (!arguments.length) return _scale; + _scale = val; + return tile; + }; + + tile.translate = function(val) { + if (!arguments.length) return _translate; + _translate = val; + return tile; + }; + + tile.zoomDelta = function(val) { + if (!arguments.length) return _zoomDelta; + _zoomDelta = +val; + return tile; + }; + + // number to extend the rows/columns beyond those covering the viewport + tile.margin = function(val) { + if (!arguments.length) return _margin; + _margin = +val; + return tile; + }; + + return tile; +} diff --git a/package.json b/package.json index 3f2f448fc..a920390d4 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "json-stringify-pretty-compact": "^1.1.0", "jsonschema": "^1.1.0", "mapillary-js": "2.12.1", - "mapillary_sprite_source": "^1.4.0", + "mapillary_sprite_source": "^1.5.0", "minimist": "^1.2.0", "mocha": "^5.0.0", "mocha-phantomjs-core": "^2.1.0", From 60f66b73e983e184703aab56885f3c82fc5a3ae3 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Wed, 18 Jul 2018 15:16:52 -0400 Subject: [PATCH 2/7] fixed missing declarations. TODO: fix tests --- dist/locales/en.json | 4 ++-- modules/services/osm.js | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dist/locales/en.json b/dist/locales/en.json index f243cb992..e4b2ccb4d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -6706,7 +6706,7 @@ "attribution": { "text": "Terms & Feedback" }, - "description": "Premium DigitalGlobe satellite imagery.", + "description": "DigitalGlobe-Premium is a mosaic composed of DigitalGlobe basemap with select regions filled with +Vivid or custom area of interest imagery, 50cm resolution or better, and refreshed more frequently with ongoing updates.", "name": "DigitalGlobe Premium Imagery" }, "DigitalGlobe-Premium-vintage": { @@ -6720,7 +6720,7 @@ "attribution": { "text": "Terms & Feedback" }, - "description": "Standard DigitalGlobe satellite imagery.", + "description": "DigitalGlobe-Standard is a curated set of imagery covering 86% of the earth’s landmass, with 30-60cm resolution where available, backfilled by Landsat. Average age is 2.31 years, with some areas updated 2x per year.", "name": "DigitalGlobe Standard Imagery" }, "DigitalGlobe-Standard-vintage": { diff --git a/modules/services/osm.js b/modules/services/osm.js index 128fe88d9..71f71562f 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -787,7 +787,10 @@ export default { ]; var tiles = geoTile.filterNullIsland(geoTile.getTiles(_tileZoom, projection)); - geoTile.removeInflightRequests(_tiles, tiles, abortRequest); + + // remove inflight requests that no longer cover the view.. + var hadRequests = !_isEmpty(cache.inflight); + geoTile.removeInflightRequests(cache, tiles, abortRequest); if (hadRequests && !loadingNotes && _isEmpty(cache.inflight)) { dispatch.call('loaded'); // stop the spinner From c563abaf7a46953199301ef9933a4491f4dc1562 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Wed, 18 Jul 2018 15:49:04 -0400 Subject: [PATCH 3/7] pass dimensions to tile --- modules/services/mapillary.js | 2 +- modules/services/openstreetcam.js | 2 +- modules/services/osm.js | 9 +-------- modules/services/streetside.js | 2 +- modules/util/tile.js | 4 ++-- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index 93870f94b..db387fd1f 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -84,7 +84,7 @@ function loadTiles(which, url, projection) { var s = projection.scale() * 2 * Math.PI; var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection)); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, projection.clipExtent()[1], 0)); geoTile.removeInflightRequests(which, tiles, abortRequest, ',0'); tiles.forEach(function(tile) { diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index 98b857670..be2953f16 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -79,7 +79,7 @@ function loadTiles(which, url, projection) { var s = projection.scale() * 2 * Math.PI, currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection)); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, projection.clipExtent()[1], 0)); geoTile.removeInflightRequests(which, tiles, abortRequest, ',0'); tiles.forEach(function(tile) { diff --git a/modules/services/osm.js b/modules/services/osm.js index 71f71562f..67a2e29f2 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -778,15 +778,8 @@ export default { tilezoom = _tileZoom; } - var s = projection.scale() * 2 * Math.PI; - var z = Math.max(Math.log(s) / Math.log(2) - 8, 0); - var ts = 256 * Math.pow(2, z - tilezoom); - var origin = [ - s / 2 - projection.translate()[0], - s / 2 - projection.translate()[1] - ]; - var tiles = geoTile.filterNullIsland(geoTile.getTiles(_tileZoom, projection)); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(_tileZoom, projection, dimensions, 0)); // remove inflight requests that no longer cover the view.. var hadRequests = !_isEmpty(cache.inflight); diff --git a/modules/services/streetside.js b/modules/services/streetside.js index 591a21172..4252c05a8 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -94,7 +94,7 @@ function loadTiles(which, url, projection, margin) { var s = projection.scale() * 2 * Math.PI; var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, margin)); + var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, projection.clipExtent()[1], margin)); tiles.forEach(function (tile) { loadNextTilePage(which, currZoom, url, tile); diff --git a/modules/util/tile.js b/modules/util/tile.js index 78d9a6423..546b4f10a 100644 --- a/modules/util/tile.js +++ b/modules/util/tile.js @@ -71,7 +71,7 @@ export function utilTile() { * Using d3.geo.tiles.js from lib, gets tile extents for each grid tile in a grid created from * an area around (and including) the current map view extents. */ - tile.getTiles = function(tileZoom, projection, margin) { + tile.getTiles = function(tileZoom, projection, dimensions, margin) { // s is the current map scale // z is the 'Level of Detail', or zoom-level, where Level 1 is far from the earth, and Level 23 is close to the ground. // ts ('tile size') here is the formula for determining the width/height of the map in pixels, but with a modification. @@ -88,7 +88,7 @@ export function utilTile() { var tiler = this .scaleExtent([tileZoom, tileZoom]) .scale(s) - .size(projection.clipExtent()[1]) + .size(dimensions) .translate(projection.translate()) .margin(margin || 0); // request nearby tiles so we can connect sequences. From 386d375b7a80cc55c77001900c44c9b58dd51398 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Thu, 19 Jul 2018 10:51:39 -0400 Subject: [PATCH 4/7] fix excessive note tiling and network request --- modules/services/mapillary.js | 5 ++++- modules/services/openstreetcam.js | 5 ++++- modules/services/osm.js | 6 +++--- modules/services/streetside.js | 4 +++- modules/util/tile.js | 22 ++++++++++++++++++---- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index db387fd1f..cdf04aa3b 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -84,7 +84,10 @@ function loadTiles(which, url, projection) { var s = projection.scale() * 2 * Math.PI; var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, projection.clipExtent()[1], 0)); + var dimension = projection.clipExtent()[1]; + var tiles = geoTile.getTiles(projection, dimension, tileZoom, 0); + tiles = geoTile.filterNullIsland(tiles); + geoTile.removeInflightRequests(which, tiles, abortRequest, ',0'); tiles.forEach(function(tile) { diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index be2953f16..73483dddc 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -79,7 +79,10 @@ function loadTiles(which, url, projection) { var s = projection.scale() * 2 * Math.PI, currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, projection.clipExtent()[1], 0)); + var dimension = projection.clipExtent()[1]; + var tiles = geoTile.getTiles(projection, dimension, tileZoom, 0); + tiles = geoTile.filterNullIsland(tiles); + geoTile.removeInflightRequests(which, tiles, abortRequest, ',0'); tiles.forEach(function(tile) { diff --git a/modules/services/osm.js b/modules/services/osm.js index 67a2e29f2..4263662e2 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -778,13 +778,13 @@ export default { tilezoom = _tileZoom; } - - var tiles = geoTile.filterNullIsland(geoTile.getTiles(_tileZoom, projection, dimensions, 0)); + // get tiles + var tiles = geoTile.getTiles(projection, dimensions, tilezoom, 0); + tiles = geoTile.filterNullIsland(tiles); // remove inflight requests that no longer cover the view.. var hadRequests = !_isEmpty(cache.inflight); geoTile.removeInflightRequests(cache, tiles, abortRequest); - if (hadRequests && !loadingNotes && _isEmpty(cache.inflight)) { dispatch.call('loaded'); // stop the spinner } diff --git a/modules/services/streetside.js b/modules/services/streetside.js index 4252c05a8..52ee2c14f 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -94,7 +94,9 @@ function loadTiles(which, url, projection, margin) { var s = projection.scale() * 2 * Math.PI; var currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0)); - var tiles = geoTile.filterNullIsland(geoTile.getTiles(tileZoom, projection, projection.clipExtent()[1], margin)); + var dimension = projection.clipExtent()[1]; + var tiles = geoTile.getTiles(projection, dimension, tileZoom, margin); + tiles = geoTile.filterNullIsland(tiles); tiles.forEach(function (tile) { loadNextTilePage(which, currZoom, url, tile); diff --git a/modules/util/tile.js b/modules/util/tile.js index 546b4f10a..2179525f7 100644 --- a/modules/util/tile.js +++ b/modules/util/tile.js @@ -66,12 +66,14 @@ export function utilTile() { return tiles; } + /** * getTiles() returns array of d3 geo tiles. * Using d3.geo.tiles.js from lib, gets tile extents for each grid tile in a grid created from * an area around (and including) the current map view extents. */ - tile.getTiles = function(tileZoom, projection, dimensions, margin) { + tile.getTiles = function(projection, dimensions, tilezoom, margin) { + // s is the current map scale // z is the 'Level of Detail', or zoom-level, where Level 1 is far from the earth, and Level 23 is close to the ground. // ts ('tile size') here is the formula for determining the width/height of the map in pixels, but with a modification. @@ -79,23 +81,24 @@ export function utilTile() { // As used here, by subtracting constant 'tileZoom' from z (the level), you end up with a much smaller value for the tile size (in pixels). var s = projection.scale() * 2 * Math.PI; var z = Math.max(Math.log(s) / Math.log(2) - 8, 0); - var ts = 256 * Math.pow(2, z - tileZoom); + var ts = 256 * Math.pow(2, z - tilezoom); var origin = [ s / 2 - projection.translate()[0], s / 2 - projection.translate()[1] ]; var tiler = this - .scaleExtent([tileZoom, tileZoom]) + .scaleExtent([tilezoom, tilezoom]) .scale(s) .size(dimensions) .translate(projection.translate()) .margin(margin || 0); // request nearby tiles so we can connect sequences. - return tiler() + var tiles = tiler() .map(function(tile) { var x = tile[0] * ts - origin[0]; var y = tile[1] * ts - origin[1]; + return { id: tile.toString(), xyz: tile, @@ -105,14 +108,18 @@ export function utilTile() { ) }; }); + + return tiles; }; + tile.filterNullIsland = function(tiles) { return tiles.filter(function(t) { return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]); }); }; + // remove inflight requests that no longer cover the view.. tile.removeInflightRequests = function(cache, tiles, callback, modifier) { return _filter(cache.inflight, function(v, i) { @@ -124,36 +131,42 @@ export function utilTile() { }).map(callback); // abort request }; + tile.scaleExtent = function(val) { if (!arguments.length) return _scaleExtent; _scaleExtent = val; return tile; }; + tile.size = function(val) { if (!arguments.length) return _size; _size = val; return tile; }; + tile.scale = function(val) { if (!arguments.length) return _scale; _scale = val; return tile; }; + tile.translate = function(val) { if (!arguments.length) return _translate; _translate = val; return tile; }; + tile.zoomDelta = function(val) { if (!arguments.length) return _zoomDelta; _zoomDelta = +val; return tile; }; + // number to extend the rows/columns beyond those covering the viewport tile.margin = function(val) { if (!arguments.length) return _margin; @@ -161,5 +174,6 @@ export function utilTile() { return tile; }; + return tile; } From bf9b19359ae24d297ac2837e8eacfa21660608a6 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 20 Jul 2018 21:41:44 -0400 Subject: [PATCH 5/7] WIP drag note --- modules/behavior/drag.js | 7 +- modules/behavior/draw.js | 1 - modules/behavior/hash.js | 2 + modules/modes/add_note.js | 3 +- modules/modes/browse.js | 4 +- modules/modes/drag_note.js | 254 +++++++++++++++++++++++++++++++++++ modules/modes/index.js | 1 + modules/modes/select_note.js | 3 + modules/osm/note.js | 4 + modules/renderer/map.js | 8 +- modules/services/osm.js | 36 +++-- modules/svg/notes.js | 2 + modules/ui/modes.js | 4 +- modules/ui/note_editor.js | 6 +- 14 files changed, 315 insertions(+), 20 deletions(-) create mode 100644 modules/modes/drag_note.js diff --git a/modules/behavior/drag.js b/modules/behavior/drag.js index f32c952ce..9735b77ae 100644 --- a/modules/behavior/drag.js +++ b/modules/behavior/drag.js @@ -9,6 +9,8 @@ import { touches as d3_touches } from 'd3-selection'; +import { osmNote } from '../osm'; + import { utilRebind } from '../util/rebind'; import { @@ -162,7 +164,10 @@ export function behaviorDrag() { var target = d3_event.target; for (; target && target !== root; target = target.parentNode) { var datum = target.__data__; - var entity = datum && datum.properties && datum.properties.entity; + + var entity = datum instanceof osmNote ? + datum : datum && datum.properties && datum.properties.entity; + if (entity && target[matchesSelector](_selector)) { return dragstart.call(target, entity); } diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index 80b158d71..21d17d2f5 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -138,7 +138,6 @@ export function behaviorDraw(context) { return; } } - dispatch.call('click', this, context.map().mouseCoordinates(), d); } diff --git a/modules/behavior/hash.js b/modules/behavior/hash.js index 2aa76ff2b..cb073c40a 100644 --- a/modules/behavior/hash.js +++ b/modules/behavior/hash.js @@ -51,6 +51,8 @@ export function behaviorHash(context) { var newParams = {}; delete q.id; + console.log('TAH - hash: context.selectedIDs()', context.selectedIDs()); + console.log('TAH - hash: context.selectedNoteID()', context.selectedNoteID()); var selected = context.selectedIDs().filter(function(id) { return !context.entity(id).isNew(); }); diff --git a/modules/modes/add_note.js b/modules/modes/add_note.js index e44a2f763..f0ab36571 100644 --- a/modules/modes/add_note.js +++ b/modules/modes/add_note.js @@ -28,6 +28,7 @@ export function modeAddNote(context) { function add(loc) { var note = osmNote({ + id: -1, loc: loc, status: 'open', comments: {}, @@ -35,8 +36,6 @@ export function modeAddNote(context) { }); services.osm.replaceNote(note); - dispatch.call('change'); - context .selectedNoteID(note.id) diff --git a/modules/modes/browse.js b/modules/modes/browse.js index 5ba0e7fee..72484df62 100644 --- a/modules/modes/browse.js +++ b/modules/modes/browse.js @@ -8,6 +8,7 @@ import { } from '../behavior'; import { modeDragNode } from './drag_node'; +import { modeDragNote } from './drag_note'; export function modeBrowse(context) { @@ -23,7 +24,8 @@ export function modeBrowse(context) { behaviorHover(context).on('hover', context.ui().sidebar.hover), behaviorSelect(context), behaviorLasso(context), - modeDragNode(context).behavior + modeDragNode(context).behavior, + modeDragNote(context).behavior ]; diff --git a/modules/modes/drag_note.js b/modules/modes/drag_note.js new file mode 100644 index 000000000..d6d1f2b9b --- /dev/null +++ b/modules/modes/drag_note.js @@ -0,0 +1,254 @@ +import _find from 'lodash-es/find'; + +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; + +import { dispatch as d3_dispatch } from 'd3-dispatch'; + +import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; + +import { geoVecInterp } from '../geo'; + +import { t } from '../util/locale'; + +import { services } from '../services'; + + +import { + actionAddMidpoint, + actionConnect, + actionMoveNode, + actionNoop +} from '../actions'; + +import { + behaviorEdit, + behaviorHover, + behaviorDrag +} from '../behavior'; + +import { + geoChooseEdge, + geoHasLineIntersections, + geoHasSelfIntersections, + geoVecSubtract, + geoViewportEdge +} from '../geo'; + +import { modeBrowse, modeSelectNote } from './index'; +import { osmJoinWays, osmNode } from '../osm'; +import { uiFlash } from '../ui'; + + +export function modeDragNote(context) { + var mode = { + id: 'drag-note', + button: 'browse' + }; + var hover = behaviorHover(context).altDisables(true) + .on('hover', context.ui().sidebar.hover); + var edit = behaviorEdit(context); + + var dispatch = d3_dispatch('redraw', 'change'); + + var _nudgeInterval; + var _restoreSelectedNoteID = []; + var _wasMidpoint = false; + var _isCancelled = false; + var _activeEntity; + var _startLoc; + var _lastLoc; + + + function startNudge(entity, nudge) { + if (_nudgeInterval) window.clearInterval(_nudgeInterval); + _nudgeInterval = window.setInterval(function() { + context.pan(nudge); + doMove(entity, nudge); + }, 50); + } + + + function stopNudge() { + if (_nudgeInterval) { + window.clearInterval(_nudgeInterval); + _nudgeInterval = null; + } + } + + + function origin(entity) { + return context.projection(entity.loc); + } + + + function keydown() { + if (d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (context.surface().classed('nope')) { + context.surface() + .classed('nope-suppressed', true); + } + context.surface() + .classed('nope', false) + .classed('nope-disabled', true); + } + } + + + function keyup() { + if (d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (context.surface().classed('nope-suppressed')) { + context.surface() + .classed('nope', true); + } + context.surface() + .classed('nope-suppressed', false) + .classed('nope-disabled', false); + } + } + + + function start(entity) { + console.log('TAH - drag_note start()'); + + context.perform(actionNoop()); + + _activeEntity = entity; + _startLoc = entity.loc; + + context.surface().selectAll('.note-' + _activeEntity.id) + .classed('active', true); + + context.enter(mode); + } + + + function move(entity) { + if (_isCancelled) return; + d3_event.sourceEvent.stopPropagation(); + + context.surface().classed('nope-disabled', d3_event.sourceEvent.altKey); + + _lastLoc = context.projection.invert(d3_event.point); + + doMove(entity); + // var nudge = geoViewportEdge(d3_event.point, context.map().dimensions()); + // if (nudge) { + // startNudge(entity, nudge); + // } else { + // stopNudge(); + // } + + } + + + function doMove(entity, nudge) { + nudge = nudge || [0, 0]; + + var currPoint = (d3_event && d3_event.point) || context.projection(_lastLoc); + var currMouse = geoVecSubtract(currPoint, nudge); + var loc = context.projection.invert(currMouse); + + entity = entity.move(geoVecInterp(entity.loc, loc, 1)); + + var osm = services.osm; + if (osm) { + osm.replaceNote(entity); // update note cache + } + dispatch.call('change', this, 'difference'); + console.log('moved: ', entity.loc); + } + + + function end(entity) { + console.log('TAH - drag_note end()'); + context + .selectedNoteID(entity.id) + .enter(modeSelectNote(context, entity.id)); + } + + + function cancel() { + drag.cancel(); + context.enter(modeBrowse(context)); + } + + + var drag = behaviorDrag() + .selector('.layer-notes .new') + .surface(d3_select('#map').node()) + .origin(origin) + .on('start', start) + .on('move', move) + .on('end', end); + + + mode.enter = function() { + context.install(hover); + context.install(edit); + + d3_select(window) + .on('keydown.drawWay', keydown) + .on('keyup.drawWay', keyup); + + context.history() + .on('undone.drag-note', cancel); + }; + + + mode.exit = function() { + context.ui().sidebar.hover.cancel(); + context.uninstall(hover); + context.uninstall(edit); + + d3_select(window) + .on('keydown.hover', null) + .on('keyup.hover', null); + + context.history() + .on('undone.drag-note', null); + + context.map() + .on('drawn.drag-note', null); + + _activeEntity = null; + + context.surface() + .classed('nope', false) + .classed('nope-suppressed', false) + .classed('nope-disabled', false) + .selectAll('.active') + .classed('active', false); + + stopNudge(); + }; + + + mode.selectedNoteID = function() { + if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; + // no assign + return mode; + }; + + + mode.activeID = function() { + if (!arguments.length) return _activeEntity && _activeEntity.id; + // no assign + return mode; + }; + + + mode.restoreSelectedNoteID = function(_) { + if (!arguments.length) return _restoreSelectedNoteID; + _restoreSelectedNoteID = _; + return mode; + }; + + + mode.behavior = drag; + + + return mode; +} diff --git a/modules/modes/index.js b/modules/modes/index.js index 56ed999d0..83838c4e1 100644 --- a/modules/modes/index.js +++ b/modules/modes/index.js @@ -4,6 +4,7 @@ export { modeAddPoint } from './add_point'; export { modeAddNote } from './add_note'; export { modeBrowse } from './browse'; export { modeDragNode } from './drag_node'; +export { modeDragNote } from './drag_note'; export { modeDrawArea } from './draw_area'; export { modeDrawLine } from './draw_line'; export { modeMove } from './move'; diff --git a/modules/modes/select_note.js b/modules/modes/select_note.js index e2c008fbc..56fe0f198 100644 --- a/modules/modes/select_note.js +++ b/modules/modes/select_note.js @@ -11,6 +11,8 @@ import { behaviorSelect } from '../behavior'; +import { modeDragNote } from '../modes'; + import { services } from '../services'; import { modeBrowse } from './browse'; import { uiNoteEditor } from '../ui'; @@ -37,6 +39,7 @@ export function modeSelectNote(context, selectedNoteID) { behaviorHover(context), behaviorSelect(context), behaviorLasso(context), + modeDragNote(context).behavior ]; var newFeature = false; diff --git a/modules/osm/note.js b/modules/osm/note.js index 3e10e8cf1..ed03c0504 100644 --- a/modules/osm/note.js +++ b/modules/osm/note.js @@ -55,6 +55,10 @@ _extend(osmNote.prototype, { isNew: function() { return this.id < 0; + }, + + move: function(loc) { + return this.update({ loc: loc }); } }); diff --git a/modules/renderer/map.js b/modules/renderer/map.js index 1118488b7..d6c7d809c 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -38,7 +38,8 @@ import { svgLines, svgMidpoints, svgPoints, - svgVertices + svgVertices, + svgNotes } from '../svg'; import { uiFlash } from '../ui'; @@ -73,6 +74,8 @@ export function rendererMap(context) { var drawMidpoints = svgMidpoints(projection, context); var drawLabels = svgLabels(projection, context); + var drawNotes = svgNotes(projection, context); + var _selection = d3_select(null); var supersurface = d3_select(null); var wrapper = d3_select(null); @@ -341,6 +344,9 @@ export function rendererMap(context) { .call(drawLabels, graph, data, filter, dimensions, fullRedraw) .call(drawPoints, graph, data, filter); + surface.selectAll('.data-layer-notes') + .call(drawNotes); + dispatch.call('drawn', this, {full: true}); } diff --git a/modules/services/osm.js b/modules/services/osm.js index 079a64c1a..9afd91fb8 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -327,6 +327,20 @@ function parseXML(xml, callback, options) { } +// replace or remove note from rtree +function updateRtree(item, replace) { // update (or insert) in _noteCache.rtree + + // TODO: other checks needed? (e.g., if cache.data.children.length decrements ...) + + // remove note + _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; }); + if (replace) { + _noteCache.rtree.insert(item); // add note (updated) + } + +} + + function wrapcb(thisArg, callback, cid) { return function(err, result) { if (err) { @@ -1033,23 +1047,23 @@ export default { }, + // remove a single note from the cache + removeNote: function(note) { + if (!(note instanceof osmNote) || !note.id) return; + + delete _noteCache.note[note.id]; + + updateRtree(encodeNoteRtree(note), false); + }, + + // replace a single note in the cache replaceNote: function(note) { if (!(note instanceof osmNote) || !note.id) return; _noteCache.note[note.id] = note; // update (or insert) in _noteCache.note - function updateRtree(item) { // update (or insert) in _noteCache.rtree - - // TODO: other checks needed? (e.g., if cache.data.children.length decrements ...) - - // remove note - _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; }); - _noteCache.rtree.insert(item); // add note (updated) - - } - - updateRtree(encodeNoteRtree(note)); + updateRtree(encodeNoteRtree(note), true); return note; } diff --git a/modules/svg/notes.js b/modules/svg/notes.js index 511f10c00..e5f7d88c3 100644 --- a/modules/svg/notes.js +++ b/modules/svg/notes.js @@ -1,12 +1,14 @@ import _throttle from 'lodash-es/throttle'; import { select as d3_select } from 'd3-selection'; +import { dispatch as d3_dispatch } from 'd3-dispatch'; import { svgPointTransform } from './index'; import { services } from '../services'; export function svgNotes(projection, context, dispatch) { + if (!dispatch) { dispatch = d3_dispatch('change'); } var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000); var minZoom = 12; var layer = d3_select(null); diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 4d01b6820..ad00af2b4 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -16,6 +16,7 @@ import { import { svgIcon } from '../svg'; import { tooltip } from '../util/tooltip'; import { uiTooltipHtml } from './tooltipHtml'; +import { services } from '../services/index.js'; export function uiModes(context) { @@ -36,7 +37,7 @@ export function uiModes(context) { function toggleNewNote() { return svgNotes().enabled() && context.connection().authenticated() - && ~~context.map().zoom() >= 12; + && ~~context.map().zoom() >= 16; } @@ -97,7 +98,6 @@ export function uiModes(context) { modes.forEach(function(mode) { keybinding.on(mode.key, function() { - // TODO: allow zooming out beyond minZoom when adding new note. Currently prevented if ((editable() && mode.id !== 'add-note') || (toggleNewNote() && mode.id === 'add-note')) { if (mode.id === context.mode().id) { context.enter(modeBrowse(context)); diff --git a/modules/ui/note_editor.js b/modules/ui/note_editor.js index 1ded94d46..fd526076f 100644 --- a/modules/ui/note_editor.js +++ b/modules/ui/note_editor.js @@ -40,7 +40,11 @@ export function uiNoteEditor(context) { headerEnter .append('button') .attr('class', 'fr note-editor-close') - .on('click', function() { context.enter(modeBrowse(context)); }) + .on('click', function() { + var osm = services.osm; + if (_note.isNew()) { osm.removeNote(_note); } // delete new note + context.enter(modeBrowse(context)); + }) .call(svgIcon('#iD-icon-close')); headerEnter From 1d61355d08bc9636ebfd187155f213d3b2b0ea5a Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 20 Jul 2018 22:19:57 -0400 Subject: [PATCH 6/7] added service for new note --- modules/services/osm.js | 39 ++++++++++++++++++++++++++++++++++++++- modules/ui/note_editor.js | 2 +- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/modules/services/osm.js b/modules/services/osm.js index 9afd91fb8..74e9a5cb8 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -861,7 +861,43 @@ export default { // Create a note // POST /api/0.6/notes?params postNoteCreate: function(note, callback) { - // todo + if (!this.authenticated()) { + return callback({ message: 'Not Authenticated', status: -3 }, note); + } + if (_noteCache.inflightPost[note.id]) { + return callback({ message: 'Note update already inflight', status: -2 }, note); + } + + if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required + + var path = '/api/0.6/notes?' + + 'lat=' + note.loc[1] + + '&lon=' + note.loc[0] + + '&' + utilQsString({ text: note.newComment }); + _noteCache.inflightPost[note.id] = oauth.xhr( + { method: 'POST', path: path }, + wrapcb(this, done, _connectionID) + ); + + + function done(err, xml) { + delete _noteCache.inflightPost[note.id]; + if (err) { return callback(err); } + + // we get the updated note back, remove from caches and reparse.. + var item = encodeNoteRtree(note); + _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; }); + delete _noteCache.note[note.id]; + + var options = { skipSeen: false }; + return parseXML(xml, function(err, results) { + if (err) { + return callback(err); + } else { + return callback(undefined, results[0]); + } + }, options); + } }, @@ -884,6 +920,7 @@ export default { action = 'reopen'; } else { action = 'comment'; + if (!note.newComment) return; // when commenting, comment required } var path = '/api/0.6/notes/' + note.id + '/' + action; diff --git a/modules/ui/note_editor.js b/modules/ui/note_editor.js index fd526076f..10c78ab9e 100644 --- a/modules/ui/note_editor.js +++ b/modules/ui/note_editor.js @@ -322,7 +322,7 @@ export function uiNoteEditor(context) { this.blur(); // avoid keeping focus on the button - #4641 var osm = services.osm; if (osm) { - osm.postNoteAdd(d, d.status, function(err, note) { + osm.postNoteCreate(d, function(err, note) { dispatch.call('change', note); }); } From fbedbb1608b2cd1ea723c16295008683ae51357c Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 20 Jul 2018 23:17:41 -0400 Subject: [PATCH 7/7] select note before rendering sidebar --- modules/behavior/hash.js | 2 -- modules/modes/drag_note.js | 4 ---- modules/modes/select_note.js | 5 +++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/modules/behavior/hash.js b/modules/behavior/hash.js index cb073c40a..2aa76ff2b 100644 --- a/modules/behavior/hash.js +++ b/modules/behavior/hash.js @@ -51,8 +51,6 @@ export function behaviorHash(context) { var newParams = {}; delete q.id; - console.log('TAH - hash: context.selectedIDs()', context.selectedIDs()); - console.log('TAH - hash: context.selectedNoteID()', context.selectedNoteID()); var selected = context.selectedIDs().filter(function(id) { return !context.entity(id).isNew(); }); diff --git a/modules/modes/drag_note.js b/modules/modes/drag_note.js index d6d1f2b9b..602fac67e 100644 --- a/modules/modes/drag_note.js +++ b/modules/modes/drag_note.js @@ -111,8 +111,6 @@ export function modeDragNote(context) { function start(entity) { - console.log('TAH - drag_note start()'); - context.perform(actionNoop()); _activeEntity = entity; @@ -158,12 +156,10 @@ export function modeDragNote(context) { osm.replaceNote(entity); // update note cache } dispatch.call('change', this, 'difference'); - console.log('moved: ', entity.loc); } function end(entity) { - console.log('TAH - drag_note end()'); context .selectedNoteID(entity.id) .enter(modeSelectNote(context, entity.id)); diff --git a/modules/modes/select_note.js b/modules/modes/select_note.js index 56fe0f198..3692d4c13 100644 --- a/modules/modes/select_note.js +++ b/modules/modes/select_note.js @@ -80,6 +80,7 @@ export function modeSelectNote(context, selectedNoteID) { } else { selection .classed('selected', true); + context.selectedNoteID(selectedNoteID); } } @@ -100,13 +101,13 @@ export function modeSelectNote(context, selectedNoteID) { d3_select(document) .call(keybinding); + selectNote(); + context.ui().sidebar .show(noteEditor.note(note)); context.map() .on('drawn.select', selectNote); - - selectNote(); };