merged from

This commit is contained in:
Thomas Hervey
2018-07-20 23:21:24 -04:00
22 changed files with 376 additions and 467 deletions
+5
View File
@@ -168,11 +168,16 @@ export function behaviorDrag() {
for (; target && target !== root; target = target.parentNode) {
var datum = target.__data__;
<<<<<<< HEAD
var entity;
if (datum instanceof osmNote) { entity = datum;}
else {
entity = datum && datum.properties && datum.properties.entity;
}
=======
var entity = datum instanceof osmNote ?
datum : datum && datum.properties && datum.properties.entity;
>>>>>>> drag-note
if (entity && target[matchesSelector](_selector)) {
return dragstart.call(target, entity);
-1
View File
@@ -138,7 +138,6 @@ export function behaviorDraw(context) {
return;
}
}
dispatch.call('click', this, context.map().mouseCoordinates(), d);
}
-93
View File
@@ -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;
}
-1
View File
@@ -1,3 +1,2 @@
export { d3combobox } from './d3.combobox';
export { d3geoTile } from './d3.geo.tile';
export { d3keybinding } from './d3.keybinding';
+1 -2
View File
@@ -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', this);
context
.selectedNoteID(note.id)
+2 -2
View File
@@ -8,7 +8,7 @@ import {
} from '../behavior';
import { modeDragNode } from './drag_node';
import { modeDragNote2 } from './drag_note2';
import { modeDragNote } from './drag_note';
export function modeBrowse(context) {
@@ -25,7 +25,7 @@ export function modeBrowse(context) {
behaviorSelect(context),
behaviorLasso(context),
modeDragNode(context).behavior,
modeDragNote2(context).behavior
modeDragNote(context).behavior
];
+55 -187
View File
@@ -1,5 +1,3 @@
import { dispatch as d3_dispatch } from 'd3-dispatch';
import _find from 'lodash-es/find';
import {
@@ -7,14 +5,21 @@ import {
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,
actionMoveNote,
actionMoveNode,
actionNoop
} from '../actions';
@@ -32,8 +37,8 @@ import {
geoViewportEdge
} from '../geo';
import { modeBrowse, modeSelect } from './index';
import { osmJoinWays, osmNote } from '../osm';
import { modeBrowse, modeSelectNote } from './index';
import { osmJoinWays, osmNode } from '../osm';
import { uiFlash } from '../ui';
@@ -46,16 +51,16 @@ export function modeDragNote(context) {
.on('hover', context.ui().sidebar.hover);
var edit = behaviorEdit(context);
var dispatch = d3_dispatch('redraw', 'change');
var _nudgeInterval;
var _restoreSelectedNoteIDs = [];
var _restoreSelectedNoteID = [];
var _wasMidpoint = false;
var _isCancelled = false;
var _activeEntity;
var _startLoc;
var _lastLoc;
var dispatch = d3_dispatch('change');
function startNudge(entity, nudge) {
if (_nudgeInterval) window.clearInterval(_nudgeInterval);
@@ -74,17 +79,6 @@ export function modeDragNote(context) {
}
function moveAnnotation(entity) {
console.log('entity')
return t('operations.move.annotation.' + entity.geometry(context.graph()));
}
function connectAnnotation(entity) {
return t('operations.connect.annotation.' + entity.geometry(context.graph()));
}
function origin(entity) {
return context.projection(entity.loc);
}
@@ -117,7 +111,7 @@ export function modeDragNote(context) {
function start(entity) {
_isCancelled = d3_event.sourceEvent.shiftKey;
context.perform(actionNoop());
_activeEntity = entity;
_startLoc = entity.loc;
@@ -129,18 +123,22 @@ export function modeDragNote(context) {
}
// related code
// - `behavior/draw.js` `datum()`
function datum() {
var event = d3_event && d3_event.sourceEvent;
if (!event || event.altKey) {
return {};
} else {
// When dragging, snap only to touch targets..
// (this excludes area fills and active drawing elements)
var d = event.target.__data__;
return (d && d.properties && d.properties.target) ? d : {};
}
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();
// }
}
@@ -151,147 +149,20 @@ export function modeDragNote(context) {
var currMouse = geoVecSubtract(currPoint, nudge);
var loc = context.projection.invert(currMouse);
if (!_nudgeInterval) { // If not nudging at the edge of the viewport, try to snap..
// related code
// - `mode/drag_node.js` `doMode()`
// - `behavior/draw.js` `click()`
// - `behavior/draw_way.js` `move()`
var d = datum();
var target = d;
var targetLoc = target && target.loc;
var targetNotes = d;
var edge;
entity = entity.move(geoVecInterp(entity.loc, loc, 1));
// if (targetLoc) { // snap to node/vertex - a point target with `.loc`
// loc = targetLoc;
// } else if (targetNodes) { // snap to way - a line target with `.nodes`
// edge = geoChooseEdge(targetNodes, context.mouse(), context.projection, end.id);
// if (edge) {
// loc = edge.loc;
// }
// }
}
actionMoveNote(entity.id, loc);
dispatch.call('change');
var nope = context.surface().classed('nope');
if (isInvalid === 'relation' || isInvalid === 'restriction') {
if (!nope) { // about to nope - show hint
uiFlash()
.duration(4000)
.text(t('operations.connect.' + isInvalid,
{ relation: context.presets().item('type/restriction').name() }
))();
}
} else {
if (nope) { // about to un-nope, remove hint
uiFlash()
.duration(1)
.text('')();
}
}
// Below here: validations
var isInvalid = false;
var nopeDisabled = context.surface().classed('nope-disabled');
if (nopeDisabled) {
context.surface()
.classed('nope', false)
.classed('nope-suppressed', isInvalid);
} else {
context.surface()
.classed('nope', isInvalid)
.classed('nope-suppressed', false);
}
_lastLoc = loc;
}
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();
var osm = services.osm;
if (osm) {
osm.replaceNote(entity); // update note cache
}
dispatch.call('change', this, 'difference');
}
function end(entity) {
if (_isCancelled) return;
var d = datum();
var nope = (d && d.properties && d.properties.nope) || context.surface().classed('nope');
var target = d && d.properties && d.properties.entity; // entity to snap to
if (nope) { // bounce back
context.perform(
_actionBounceBack(entity.id, _startLoc)
);
} else if (target && target.type === 'way') {
var choice = geoChooseEdge(context.childNodes(target), context.mouse(), context.projection, entity.id);
context.replace(
actionAddMidpoint({
loc: choice.loc,
edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
}, entity),
// connectAnnotation(target) TODO: - likely replace
);
} else if (target && target.type === 'node') {
context.replace(
actionConnect([target.id, entity.id]),
// connectAnnotation(target) TODO: - likely replace
);
} else if (_wasMidpoint) {
context.replace(
actionNoop(),
t('operations.add.annotation.vertex')
);
} else {
context.replace(
actionNoop(),
// moveAnnotation(entity) TODO: - likely replace
);
}
var reselection = _restoreSelectedNoteIDs.filter(function(id) {
return context.graph().hasEntity(id);
});
if (reselection.length) {
context.enter(modeSelect(context, reselection));
} else {
context.enter(modeBrowse(context));
}
}
function _actionBounceBack(nodeID, toLoc) {
var moveNode = actionMoveNode(nodeID, toLoc);
var action = function(graph, t) {
// last time through, pop off the bounceback perform.
// it will then overwrite the initial perform with a moveNode that does nothing
if (t === 1) context.pop();
return moveNode(graph, t);
};
action.transitionable = true;
return action;
context
.selectedNoteID(entity.id)
.enter(modeSelectNote(context, entity.id));
}
@@ -302,7 +173,7 @@ export function modeDragNote(context) {
var drag = behaviorDrag()
.selector('.layer-notes .note')
.selector('.layer-notes .new')
.surface(d3_select('#map').node())
.origin(origin)
.on('start', start)
@@ -314,15 +185,12 @@ export function modeDragNote(context) {
context.install(hover);
context.install(edit);
// context.selectedIDs(null); TODO: possibly add something like this
// context.selectedNoteID(_activeEntity);
d3_select(window)
.on('keydown.drawWay', keydown)
.on('keyup.drawWay', keyup);
// d3_select(window)
// .on('keydown.drawWay', keydown)
// .on('keyup.drawWay', keyup);
// context.history()
// .on('undone.drag-node', cancel);
context.history()
.on('undone.drag-note', cancel);
};
@@ -331,15 +199,15 @@ export function modeDragNote(context) {
context.uninstall(hover);
context.uninstall(edit);
// d3_select(window)
// .on('keydown.hover', null)
// .on('keyup.hover', null);
d3_select(window)
.on('keydown.hover', null)
.on('keyup.hover', null);
// context.history()
// .on('undone.drag-node', null);
context.history()
.on('undone.drag-note', null);
// context.map()
// .on('drawn.drag-node', null);
context.map()
.on('drawn.drag-note', null);
_activeEntity = null;
@@ -354,7 +222,7 @@ export function modeDragNote(context) {
};
mode.selectedIDs = function() {
mode.selectedNoteID = function() {
if (!arguments.length) return _activeEntity ? [_activeEntity.id] : [];
// no assign
return mode;
@@ -368,9 +236,9 @@ export function modeDragNote(context) {
};
mode.restoreSelectedNoteIDs = function(_) {
if (!arguments.length) return _restoreSelectedNoteIDs;
_restoreSelectedNoteIDs = _;
mode.restoreSelectedNoteID = function(_) {
if (!arguments.length) return _restoreSelectedNoteID;
_restoreSelectedNoteID = _;
return mode;
};
+1 -1
View File
@@ -4,7 +4,7 @@ export { modeAddPoint } from './add_point';
export { modeAddNote } from './add_note';
export { modeBrowse } from './browse';
export { modeDragNode } from './drag_node';
export { modeDragNote2 } from './drag_note2';
export { modeDragNote } from './drag_note';
export { modeDrawArea } from './draw_area';
export { modeDrawLine } from './draw_line';
export { modeMove } from './move';
+11 -2
View File
@@ -12,6 +12,10 @@ import {
behaviorDrag
} from '../behavior';
<<<<<<< HEAD
=======
import { modeDragNote } from '../modes';
>>>>>>> drag-note
import { services } from '../services';
import { modeBrowse } from './browse';
@@ -40,7 +44,11 @@ export function modeSelectNote(context, selectedNoteID) {
behaviorHover(context),
behaviorSelect(context),
behaviorLasso(context),
<<<<<<< HEAD
// modeDragNote(context).restoreSelectedNoteIDs(selectedNoteID).behavior TAH - re-add
=======
modeDragNote(context).behavior
>>>>>>> drag-note
];
var newFeature = false;
@@ -81,6 +89,7 @@ export function modeSelectNote(context, selectedNoteID) {
} else {
selection
.classed('selected', true);
context.selectedNoteID(selectedNoteID);
}
}
@@ -101,13 +110,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();
};
+2 -2
View File
@@ -58,7 +58,7 @@ _extend(osmNote.prototype, {
},
move: function(loc) {
return this.update({loc: loc});
},
return this.update({ loc: loc });
}
});
+7 -1
View File
@@ -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});
}
+2 -3
View File
@@ -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 = {};
+19 -36
View File
@@ -18,10 +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 { utilQsString, utilRebind } from '../util';
import { utilDetect } from '../util/detect';
import { utilQsString, utilRebind, utilTile } from '../util';
var geoTile = utilTile();
var apibase = 'https://a.mapillary.com/v3/';
var viewercss = 'mapillary-js/mapillary.min.css';
@@ -63,33 +65,18 @@ function maxPageAtZoom(z) {
if (z > 18) return 80;
}
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 localeTimestamp(s) {
if (!s) return null;
var detected = utilDetect();
var options = {
day: 'numeric', month: 'short', year: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric',
timeZone: 'UTC'
};
var d = new Date(s);
if (isNaN(d.getTime())) return null;
return d.toLocaleString(detected.locale, options);
}
@@ -97,15 +84,11 @@ 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]);
});
var dimension = projection.clipExtent()[1];
var tiles = geoTile.getTiles(projection, dimension, tileZoom, 0);
tiles = geoTile.filterNullIsland(tiles);
_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);
geoTile.removeInflightRequests(which, tiles, abortRequest, ',0');
tiles.forEach(function(tile) {
loadNextTilePage(which, currZoom, url, tile);
+6 -38
View File
@@ -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,6 +33,7 @@ import {
utilSetTransform
} from '../util';
var geoTile = utilTile();
var apibase = 'https://openstreetcam.org';
var maxResults = 1000;
@@ -74,48 +75,15 @@ 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]);
});
var dimension = projection.clipExtent()[1];
var tiles = geoTile.getTiles(projection, dimension, tileZoom, 0);
tiles = geoTile.filterNullIsland(tiles);
_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);
geoTile.removeInflightRequests(which, tiles, abortRequest, ',0');
tiles.forEach(function(tile) {
loadNextTilePage(which, currZoom, url, tile);
+69 -48
View File
@@ -17,7 +17,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, geoVecAdd } from '../geo';
import {
@@ -31,9 +30,11 @@ import {
import {
utilRebind,
utilIdleWorker,
utilTile,
utilQsString
} from '../util';
var geoTile = utilTile();
var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
var urlroot = 'https://www.openstreetmap.org';
@@ -326,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) {
@@ -789,44 +804,13 @@ 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]
];
// what tiles cover the view?
var tiler = d3_geoTile()
.scaleExtent([tilezoom, tilezoom])
.scale(s)
.size(dimensions)
.translate(projection.translate());
var tiles = tiler().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])
)
};
});
// 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);
_filter(cache.inflight, function(v, i) {
var wanted = _find(tiles, function(tile) { return i === tile.id; });
if (!wanted) {
delete cache.inflight[i];
}
return !wanted;
}).map(abortRequest);
geoTile.removeInflightRequests(cache, tiles, abortRequest);
if (hadRequests && !loadingNotes && _isEmpty(cache.inflight)) {
dispatch.call('loaded'); // stop the spinner
}
@@ -877,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);
}
},
@@ -900,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;
@@ -1063,23 +1084,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;
}
+6 -46
View File
@@ -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,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));
// 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 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);
+2
View File
@@ -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);
+1 -1
View File
@@ -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) {
@@ -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));
+6 -2
View File
@@ -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
@@ -318,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);
});
}
+1
View File
@@ -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';
+179
View File
@@ -0,0 +1,179 @@
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(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.
// 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(dimensions)
.translate(projection.translate())
.margin(margin || 0); // request nearby tiles so we can connect sequences.
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,
extent: geoExtent(
projection.invert([x, y + ts]),
projection.invert([x + ts, y])
)
};
});
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) {
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;
}
+1 -1
View File
@@ -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",