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