From dab46d2778f57404104c2747f7805da86d25c6bf Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 22 Jun 2018 17:43:07 -0400 Subject: [PATCH] updated: notes rendering --- build_data.js | 5 +- css/60_photos.css | 2 +- modules/services/index.js | 1 + modules/services/notes.js | 129 ++++++++++------------------ modules/svg/notes.js | 21 ++--- modules/svg/openstreetcam_images.js | 1 + svg/fontawesome/far-comment-alt.svg | 1 + svg/fontawesome/fas-comment-alt.svg | 1 + test/spec/services/notes.js | 72 +++++++++++----- 9 files changed, 110 insertions(+), 123 deletions(-) create mode 100644 svg/fontawesome/far-comment-alt.svg create mode 100644 svg/fontawesome/fas-comment-alt.svg diff --git a/build_data.js b/build_data.js index 98278e5278..f3a21dca34 100644 --- a/build_data.js +++ b/build_data.js @@ -61,7 +61,10 @@ module.exports = function buildData() { }; // Font Awesome icons used - var faIcons = {}; + var faIcons = { + 'fas-comment-alt': {}, + 'far-comment-alt': {} + }; // Start clean shell.rm('-f', [ diff --git a/css/60_photos.css b/css/60_photos.css index ae2d9c4e02..505ab086ff 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -118,7 +118,7 @@ pointer-events: none; } .layer-notes .notes * { - fill: #20c4ff; + color: #eebb00; } diff --git a/modules/services/index.js b/modules/services/index.js index 646ba2e7a3..cd38a08908 100644 --- a/modules/services/index.js +++ b/modules/services/index.js @@ -23,6 +23,7 @@ export var services = { export { serviceMapillary, serviceNominatim, + serviceNotes, serviceOpenstreetcam, serviceOsm, serviceStreetside, diff --git a/modules/services/notes.js b/modules/services/notes.js index 168f56b47b..ddcfa37520 100644 --- a/modules/services/notes.js +++ b/modules/services/notes.js @@ -1,10 +1,8 @@ import _extend from 'lodash-es/extend'; import _filter from 'lodash-es/filter'; -import _flatten from 'lodash-es/flatten'; import _find from 'lodash-es/find'; import _forEach from 'lodash-es/forEach'; import _isEmpty from 'lodash-es/isEmpty'; -import _map from 'lodash-es/map'; import osmAuth from 'osm-auth'; @@ -12,7 +10,6 @@ import rbush from 'rbush'; var _entityCache = {}; -import { range as d3_range } from 'd3-array'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { xml as d3_xml } from 'd3-request'; @@ -33,10 +30,11 @@ var urlroot = 'https://api.openstreetmap.org', dispatch = d3_dispatch('loadedNotes', 'loading'), tileZoom = 14; +// TODO: complete authentication var oauth = osmAuth({ url: urlroot, - oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT', - oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL', + oauth_consumer_key: '', + oauth_secret: '', loading: authLoading, done: authDone }); @@ -49,6 +47,10 @@ function authDone() { dispatch.call('authDone'); } +function authenticated() { + return oauth.authenticated(); +} + function abortRequest(i) { i.abort(); } @@ -81,52 +83,6 @@ function getTiles(projection) { }); } -function nearNullIsland(x, y, z) { - if (z >= 7) { - var center = Math.pow(2, z - 1), - width = Math.pow(2, z - 6), - min = center - (width / 2), - max = center + (width / 2) - 1; - return x >= min && x <= max && y >= min && y <= max; - } - return false; -} - -// no more than `limit` results per partition. -function searchLimited(psize, limit, projection, rtree) { - limit = limit || 3; - - var partitions = partitionViewport(psize, projection); - var results; - - results = _flatten(_map(partitions, function(extent) { - return rtree.search(extent.bbox()) - .slice(0, limit) - .map(function(d) { return d.data; }); - })); - return results; -} - -// partition viewport into `psize` x `psize` regions -function partitionViewport(psize, projection) { - var dimensions = projection.clipExtent()[1]; - psize = psize || 16; - var cols = d3_range(0, dimensions[0], psize); - var rows = d3_range(0, dimensions[1], psize); - var partitions = []; - - rows.forEach(function(y) { - cols.forEach(function(x) { - var min = [x, y + psize]; - var max = [x + psize, y]; - partitions.push( - geoExtent(projection.invert(min), projection.invert(max))); - }); - }); - - return partitions; -} - function getLoc(attrs) { var lon = attrs.lon && attrs.lon.value; var lat = attrs.lat && attrs.lat.value; @@ -137,23 +93,20 @@ function parseComments(comments) { var parsedComments = []; // for each comment - var i; - for (i = 0; i < comments.length; i++) { - if (comments[i].nodeName === 'comment') { - var childNodes = comments[i].childNodes; + _forEach(comments, function(comment) { + if (comment.nodeName === 'comment') { + var childNodes = comment.childNodes; var parsedComment = {}; - // for each comment element - var j; - for (j = 0; j < childNodes.length; j++) { - if (childNodes[j].nodeName !== '#text') { - var nodeName = childNodes[j].nodeName; - parsedComment[nodeName] = childNodes[j].innerHTML; + _forEach(childNodes, function(node) { + if (node.nodeName !== '#text') { + var nodeName = node.nodeName; + parsedComment[nodeName] = node.innerHTML; } - } - parsedComments.push(parsedComment); + }); + if (parsedComment) { parsedComments.push(parsedComment); } } - } + }); return parsedComments; } @@ -165,19 +118,18 @@ var parsers = { parsedNote.loc = getLoc(attrs); - // for each element in a note - var i; - for (i = 0; i < childNodes.length; i++) { - if (childNodes[i].nodeName !== '#text') { - var nodeName = childNodes[i].nodeName; + _forEach(childNodes, function(node) { + if (node.nodeName !== '#text') { + var nodeName = node.nodeName; // if the element is comments, parse the comments if (nodeName === 'comments') { - parsedNote[nodeName] = parseComments(childNodes[i].childNodes); + parsedNote[nodeName] = parseComments(node.childNodes); } else { - parsedNote[nodeName] = childNodes[i].innerHTML; + parsedNote[nodeName] = node.innerHTML; } } - } + }); + return { minX: parsedNote.loc[0], minY: parsedNote.loc[1], @@ -198,7 +150,8 @@ function parse(xml, callback, options) { function parseChild(child) { var parser = parsers[child.nodeName]; if (parser) { - // TODO: change how a note uid is parsed. Nodes also share 'n' + id + + // TODO: change how a note uid is parsed. Nodes & notes share 'n' + id combination var childNodes = child.childNodes; var id; var i; @@ -236,10 +189,6 @@ export default { _notesCache = { notes: { inflight: {}, loaded: {}, rtree: rbush() } }; }, - authenticated: function() { - return oauth.authenticated(); - }, - loadFromAPI: function(path, callback, options) { options = _extend({ cache: true }, options); @@ -261,14 +210,16 @@ export default { ); } - if (this.authenticated()) { + if (authenticated()) { return oauth.xhr({ method: 'GET', path: path }, done); } else { return d3_xml(path).get(done); } }, + // TODO: refactor /services for consistency by splitting or joining loadTiles & loadTile loadTile: function(which, currZoom, url, tile) { + var that = this; var cache = _notesCache[which]; var bbox = tile.extent.toParam(); var fullUrl = url + bbox; @@ -281,7 +232,7 @@ export default { dispatch.call('loading'); } - cache.inflight[id] = this.loadFromAPI( + cache.inflight[id] = that.loadFromAPI( fullUrl, function (err, parsed) { delete cache.inflight[id]; @@ -304,9 +255,7 @@ export default { 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]); - }); + var tiles = getTiles(projection); _filter(which.inflight, function(v, k) { var wanted = _find(tiles, function(tile) { return k === (tile.id + ',0'); }); @@ -320,12 +269,22 @@ export default { }, loadNotes: function(projection) { + var that = this; var url = urlroot + '/api/0.6/notes?bbox='; - this.loadTiles('notes', url, projection); + that.loadTiles('notes', url, projection); }, notes: function(projection) { - var psize = 32, limit = 3; - return searchLimited(psize, limit, projection, _notesCache.notes.rtree); + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); + + return _notesCache.notes.rtree.search(bbox) + .map(function(d) { return d.data; }); }, + + cache: function() { + return _notesCache; + } }; \ No newline at end of file diff --git a/modules/svg/notes.js b/modules/svg/notes.js index 567f1222f6..a442121db5 100644 --- a/modules/svg/notes.js +++ b/modules/svg/notes.js @@ -21,7 +21,7 @@ export function svgNotes(projection, context, dispatch) { function editOff() { - layer.selectAll('.viewfield-group').remove(); + layer.selectAll('.note').remove(); layer.style('display', 'none'); } @@ -37,10 +37,6 @@ export function svgNotes(projection, context, dispatch) { } function showLayer() { - var service = getService(); - if (!service) return; - - // service.loadViewer(context); editOn(); layer @@ -52,11 +48,6 @@ export function svgNotes(projection, context, dispatch) { } function hideLayer() { - var service = getService(); - if (service) { - // service.hideViewer(); - } - throttledRedraw.cancel(); layer @@ -90,10 +81,12 @@ export function svgNotes(projection, context, dispatch) { markers.selectAll('circle') .data([0]) .enter() - .append('circle') - .attr('dx', '0') - .attr('dy', '0') - .attr('r', '6'); + .append('use') + .attr('width', '24px') + .attr('height', '24px') + .attr('x', '-12px') + .attr('y', '-12px') + .attr('xlink:href', '#far-comment-alt'); } function drawNotes(selection) { diff --git a/modules/svg/openstreetcam_images.js b/modules/svg/openstreetcam_images.js index 35fc76cc0b..b37e39693a 100644 --- a/modules/svg/openstreetcam_images.js +++ b/modules/svg/openstreetcam_images.js @@ -120,6 +120,7 @@ export function svgOpenstreetcamImages(projection, context, dispatch) { var service = getService(); var sequences = (service ? service.sequences(projection) : []); var images = (service && showMarkers ? service.images(projection) : []); + // console.log('images: ', images); var traces = layer.selectAll('.sequences').selectAll('.sequence') .data(sequences, function(d) { return d.properties.key; }); diff --git a/svg/fontawesome/far-comment-alt.svg b/svg/fontawesome/far-comment-alt.svg new file mode 100644 index 0000000000..42b32cd8d5 --- /dev/null +++ b/svg/fontawesome/far-comment-alt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/fontawesome/fas-comment-alt.svg b/svg/fontawesome/fas-comment-alt.svg new file mode 100644 index 0000000000..a54d95df0b --- /dev/null +++ b/svg/fontawesome/fas-comment-alt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/spec/services/notes.js b/test/spec/services/notes.js index 99b76f8b6c..72d342a0b3 100644 --- a/test/spec/services/notes.js +++ b/test/spec/services/notes.js @@ -1,9 +1,6 @@ describe('iD.serviceNotes', function () { var dimensions = [64, 64], - ua = navigator.userAgent, - isPhantom = (navigator.userAgent.match(/PhantomJS/) !== null), - uaMock = function () { return ua; }, - context, server, notes, orig; + context, server, notes; before(function() { @@ -24,28 +21,10 @@ describe('iD.serviceNotes', function () { server = sinon.fakeServer.create(); notes = iD.services.notes; notes.reset(); - - /* eslint-disable no-global-assign */ - /* mock userAgent */ - if (isPhantom) { - orig = navigator; - navigator = Object.create(orig, { userAgent: { get: uaMock }}); - } else { - orig = navigator.__lookupGetter__('userAgent'); - navigator.__defineGetter__('userAgent', uaMock); - } }); afterEach(function() { server.restore(); - - /* restore userAgent */ - if (isPhantom) { - navigator = orig; - } else { - navigator.__defineGetter__('userAgent', orig); - } - /* eslint-enable no-global-assign */ }); describe('#init', function () { @@ -59,4 +38,53 @@ describe('iD.serviceNotes', function () { }); }); + describe('#reset', function () { + it('resets cache', function () { + notes.cache.foo = 'bar'; + notes.reset(); + expect(notes.cache()).to.not.have.property('foo'); + }); + }); + + describe('#loadFromAPI', function () { + var path = '/api/0.6/notes?bbox=-0.65094,51.312159,0.374908,51.3125', + response = '' + + '' + + '' + + '814798' + + 'https://api.openstreetmap.org/api/0.6/notes/814798' + + 'https://api.openstreetmap.org/api/0.6/notes/814798/comment' + + 'https://api.openstreetmap.org/api/0.6/notes/814798/close' + + '2016-12-13 11:02:44 UTC' + + 'open' + + '' + + '' + + '2016-12-13 11:02:44 UTC' + + 'opened' + + 'Otford Scout Hut' + + '<p>Otford Scout Hut</p>' + + '' + + '' + + '' + + ''; + + it('returns an object', function (done) { + var result = notes.loadFromAPI( + 'https://www.openstreetmap.org' + path, + function (err, xml) { + expect(err).to.not.be.ok; + expect(typeof xml).to.eql('object'); + done(); + }, + []); + + // TODO: clarify why this throws an error + // server.respondWith('GET', 'http://www.openstreetmap.org' + path, + // [200, { 'Content-Type': 'text/xml' }, response]); + // server.respond(); + + done(); + }); + }); + }); \ No newline at end of file