updated: notes rendering

This commit is contained in:
Thomas Hervey
2018-06-22 17:43:07 -04:00
parent e16b411576
commit dab46d2778
9 changed files with 110 additions and 123 deletions
+4 -1
View File
@@ -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', [
+1 -1
View File
@@ -118,7 +118,7 @@
pointer-events: none;
}
.layer-notes .notes * {
fill: #20c4ff;
color: #eebb00;
}
+1
View File
@@ -23,6 +23,7 @@ export var services = {
export {
serviceMapillary,
serviceNominatim,
serviceNotes,
serviceOpenstreetcam,
serviceOsm,
serviceStreetside,
+44 -85
View File
@@ -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;
}
};
+7 -14
View File
@@ -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) {
+1
View File
@@ -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; });
+1
View File
@@ -0,0 +1 @@
<svg aria-hidden="true" data-prefix="far" data-icon="comment-alt" class="svg-inline--fa fa-comment-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 7.1 5.8 12 12 12 2.4 0 4.9-.7 7.1-2.4L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64zm16 352c0 8.8-7.2 16-16 16H288l-12.8 9.6L208 428v-60H64c-8.8 0-16-7.2-16-16V64c0-8.8 7.2-16 16-16h384c8.8 0 16 7.2 16 16v288z"></path></svg>

After

Width:  |  Height:  |  Size: 506 B

+1
View File
@@ -0,0 +1 @@
<svg aria-hidden="true" data-prefix="fas" data-icon="comment-alt" class="svg-inline--fa fa-comment-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 9.8 11.2 15.5 19.1 9.7L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64z"></path></svg>

After

Width:  |  Height:  |  Size: 366 B

+50 -22
View File
@@ -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 = '<?xml version="1.0" encoding="UTF-8"?>' +
'<osm version="0.6" generator="OpenStreetMap server">' +
'<note lon="0.1979819" lat="51.3122986">' +
'<id>814798</id>' +
'<url>https://api.openstreetmap.org/api/0.6/notes/814798</url>' +
'<comment_url>https://api.openstreetmap.org/api/0.6/notes/814798/comment</comment_url>' +
'<close_url>https://api.openstreetmap.org/api/0.6/notes/814798/close</close_url>' +
'<date_created>2016-12-13 11:02:44 UTC</date_created>' +
'<status>open</status>' +
'<comments>' +
'<comment>' +
'<date>2016-12-13 11:02:44 UTC</date>' +
'<action>opened</action>' +
'<text>Otford Scout Hut</text>' +
'<html>&lt;p&gt;Otford Scout Hut&lt;/p&gt;</html>' +
'</comment>' +
'</comments>' +
'</note>' +
'</osm>';
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();
});
});
});