mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-20 07:25:15 +02:00
merged branch 'master' into drag-note
This commit is contained in:
+1
-1
@@ -71,7 +71,7 @@ path.stroke.tag-barrier {
|
||||
/* bridges */
|
||||
path.casing.tag-bridge {
|
||||
stroke-opacity: 0.6;
|
||||
stroke: #000;
|
||||
stroke: #000 !important;
|
||||
stroke-linecap: butt;
|
||||
stroke-dasharray: none;
|
||||
}
|
||||
|
||||
+42
-4
@@ -19,16 +19,52 @@
|
||||
border-radius: 0;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 48;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
#photoviewer button.resize-handle-xy {
|
||||
border-radius: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 49;
|
||||
cursor: nesw-resize;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
#photoviewer button.resize-handle-x {
|
||||
border-radius: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 48;
|
||||
cursor: ew-resize;
|
||||
height: auto;
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
#photoviewer button.resize-handle-y {
|
||||
border-radius: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 48;
|
||||
cursor: ns-resize;
|
||||
height: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.photo-wrapper,
|
||||
.photo-wrapper img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.photo-wrapper .photo-attribution {
|
||||
@@ -186,7 +222,7 @@
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
pading: 0 5px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.ms-wrapper .photo-attribution .image-view-link {
|
||||
text-align: left;
|
||||
@@ -262,6 +298,8 @@ label.streetside-hires {
|
||||
}
|
||||
|
||||
.osc-image-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-origin:0 0;
|
||||
-ms-transform-origin:0 0;
|
||||
-webkit-transform-origin:0 0;
|
||||
|
||||
+11
-3
@@ -127,12 +127,16 @@
|
||||
border-radius: 20px;
|
||||
}
|
||||
.comment-main {
|
||||
padding: 10px;
|
||||
padding: 10px 10px 10px 0;
|
||||
flex: 1 1 100%;
|
||||
flex-flow: column nowrap;
|
||||
overflow: hidden;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
[dir='rtl'] .comment-main {
|
||||
padding: 10px 0 10px 10px;
|
||||
}
|
||||
|
||||
.comment-metadata {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
@@ -154,14 +158,18 @@
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
#new-comment-input {
|
||||
.note-save {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.note-save #new-comment-input {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
max-height: 300px;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.note-save-section {
|
||||
.note-save .detail-section {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
|
||||
+15
-3
@@ -607,8 +607,13 @@ button.save.has-count .count::before {
|
||||
margin-right: 5px;
|
||||
}
|
||||
[dir='rtl'] .icon.pre-text {
|
||||
margin-left: 5px;
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.icon.pre-text.user-icon {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.icon.light {
|
||||
@@ -1320,6 +1325,12 @@ a.hide-toggle {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* no scrollbars */
|
||||
.inspector-hover div {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
/* hide and remove from layout */
|
||||
.inspector-hidden,
|
||||
.inspector-hover label input[type="checkbox"],
|
||||
@@ -3841,7 +3852,6 @@ svg.mouseclick use.right {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Save Mode
|
||||
------------------------------------------------------- */
|
||||
.mode-save a.user-info {
|
||||
@@ -3870,6 +3880,7 @@ svg.mouseclick use.right {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.note-save .field-warning,
|
||||
.mode-save .field-warning {
|
||||
background: #ffb;
|
||||
border: 1px solid #ccc;
|
||||
@@ -3877,6 +3888,7 @@ svg.mouseclick use.right {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.note-save .field-warning:empty,
|
||||
.mode-save .field-warning:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
+5
-2
@@ -280,8 +280,8 @@ en:
|
||||
localized_translation_language: Choose language
|
||||
localized_translation_name: Name
|
||||
zoom_in_edit: Zoom in to edit
|
||||
login: login
|
||||
logout: logout
|
||||
login: Log In
|
||||
logout: Log Out
|
||||
loading_auth: "Connecting to OpenStreetMap..."
|
||||
report_a_bug: Report a bug
|
||||
help_translate: Help translate
|
||||
@@ -638,6 +638,9 @@ en:
|
||||
new: New Note
|
||||
newDescription: "Describe the issue."
|
||||
newNote: Add Note
|
||||
login: You must log in to change or comment on this note.
|
||||
upload_explanation: "Your comments will be publicly visible to all OpenStreetMap users."
|
||||
upload_explanation_with_user: "Your comments as {user} will be publicly visible to all OpenStreetMap users."
|
||||
help:
|
||||
title: Help
|
||||
key: H
|
||||
|
||||
Vendored
+6
-3
@@ -358,8 +358,8 @@
|
||||
"localized_translation_name": "Name"
|
||||
},
|
||||
"zoom_in_edit": "Zoom in to edit",
|
||||
"login": "login",
|
||||
"logout": "logout",
|
||||
"login": "Log In",
|
||||
"logout": "Log Out",
|
||||
"loading_auth": "Connecting to OpenStreetMap...",
|
||||
"report_a_bug": "Report a bug",
|
||||
"help_translate": "Help translate",
|
||||
@@ -772,7 +772,10 @@
|
||||
"report": "Report",
|
||||
"new": "New Note",
|
||||
"newDescription": "Describe the issue.",
|
||||
"newNote": "Add Note"
|
||||
"newNote": "Add Note",
|
||||
"login": "You must log in to change or comment on this note.",
|
||||
"upload_explanation": "Your comments will be publicly visible to all OpenStreetMap users.",
|
||||
"upload_explanation_with_user": "Your comments as {user} will be publicly visible to all OpenStreetMap users."
|
||||
},
|
||||
"help": {
|
||||
"title": "Help",
|
||||
|
||||
@@ -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,3 +1,2 @@
|
||||
export { d3combobox } from './d3.combobox';
|
||||
export { d3geoTile } from './d3.geo.tile';
|
||||
export { d3keybinding } from './d3.keybinding';
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -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);
|
||||
@@ -410,6 +393,13 @@ export default {
|
||||
// load mapillary signs sprite
|
||||
var defs = context.container().select('defs');
|
||||
defs.call(svgDefs(context).addSprites, ['mapillary-sprite']);
|
||||
|
||||
// Register viewer resize handler
|
||||
context.ui().on('photoviewerResize', function() {
|
||||
if (_mlyViewer) {
|
||||
_mlyViewer.resize();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -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,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);
|
||||
@@ -129,12 +97,12 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
var maxPages = maxPageAtZoom(currZoom);
|
||||
var nextPage = cache.nextPage[tile.id] || 1;
|
||||
var params = utilQsString({
|
||||
ipp: maxResults,
|
||||
page: nextPage,
|
||||
// client_id: clientId,
|
||||
bbTopLeft: [bbox.maxY, bbox.minX].join(','),
|
||||
bbBottomRight: [bbox.minY, bbox.maxX].join(',')
|
||||
}, true);
|
||||
ipp: maxResults,
|
||||
page: nextPage,
|
||||
// client_id: clientId,
|
||||
bbTopLeft: [bbox.maxY, bbox.minX].join(','),
|
||||
bbBottomRight: [bbox.minY, bbox.maxX].join(',')
|
||||
}, true);
|
||||
|
||||
if (nextPage > maxPages) return;
|
||||
|
||||
@@ -367,6 +335,16 @@ export default {
|
||||
.attr('class', 'osc-image-wrap');
|
||||
|
||||
|
||||
// Register viewer resize handler
|
||||
context.ui().on('photoviewerResize', function(dimensions) {
|
||||
imgZoom = d3_zoom()
|
||||
.extent([[0, 0], dimensions])
|
||||
.translateExtent([[0, 0], dimensions])
|
||||
.scaleExtent([1, 15])
|
||||
.on('zoom', zoomPan);
|
||||
});
|
||||
|
||||
|
||||
function rotate(deg) {
|
||||
return function() {
|
||||
if (!_oscSelectedImage) return;
|
||||
|
||||
+6
-36
@@ -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';
|
||||
@@ -789,44 +790,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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -669,6 +629,14 @@ export default {
|
||||
.attr('src', context.asset(pannellumViewerJS));
|
||||
|
||||
|
||||
// Register viewer resize handler
|
||||
context.ui().on('photoviewerResize', function() {
|
||||
if (_pannellumViewer) {
|
||||
_pannellumViewer.resize();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function step(stepBy) {
|
||||
return function() {
|
||||
var viewer = d3_select('#photoviewer');
|
||||
|
||||
@@ -167,7 +167,7 @@ export function uiCommit(context) {
|
||||
|
||||
userLink
|
||||
.append('a')
|
||||
.attr('class','user-info')
|
||||
.attr('class', 'user-info')
|
||||
.text(user.display_name)
|
||||
.attr('href', osm.userURL(user.display_name))
|
||||
.attr('tabindex', -1)
|
||||
|
||||
+112
-21
@@ -2,6 +2,7 @@ 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';
|
||||
|
||||
@@ -13,6 +14,7 @@ import { modeBrowse } from '../modes';
|
||||
import { services } from '../services';
|
||||
import { svgDefs, svgIcon } from '../svg';
|
||||
import { utilGetDimensions } from '../util/dimensions';
|
||||
import { utilRebind } from '../util';
|
||||
|
||||
import { uiAccount } from './account';
|
||||
import { uiAttribution } from './attribution';
|
||||
@@ -45,6 +47,7 @@ import { uiCmd } from './cmd';
|
||||
|
||||
export function uiInit(context) {
|
||||
var uiInitCounter = 0;
|
||||
var dispatch = d3_dispatch('photoviewerResize');
|
||||
|
||||
|
||||
function render(container) {
|
||||
@@ -256,7 +259,33 @@ export function uiInit(context) {
|
||||
.append('div')
|
||||
.call(svgIcon('#iD-icon-close'));
|
||||
|
||||
photoviewer
|
||||
.append('button')
|
||||
.attr('class', 'resize-handle-xy')
|
||||
.on(
|
||||
'mousedown',
|
||||
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnX: true, resizeOnY: true })
|
||||
);
|
||||
|
||||
photoviewer
|
||||
.append('button')
|
||||
.attr('class', 'resize-handle-x')
|
||||
.on(
|
||||
'mousedown',
|
||||
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnX: true })
|
||||
);
|
||||
|
||||
photoviewer
|
||||
.append('button')
|
||||
.attr('class', 'resize-handle-y')
|
||||
.on(
|
||||
'mousedown',
|
||||
buildResizeListener(photoviewer, 'photoviewerResize', dispatch, { resizeOnY: true })
|
||||
);
|
||||
|
||||
var mapDimensions = map.dimensions();
|
||||
|
||||
// bind events
|
||||
window.onbeforeunload = function() {
|
||||
return context.save();
|
||||
};
|
||||
@@ -265,30 +294,13 @@ export function uiInit(context) {
|
||||
context.history().unlock();
|
||||
};
|
||||
|
||||
var mapDimensions = map.dimensions();
|
||||
|
||||
|
||||
function onResize() {
|
||||
mapDimensions = utilGetDimensions(content, true);
|
||||
map.dimensions(mapDimensions);
|
||||
}
|
||||
|
||||
d3_select(window)
|
||||
.on('resize.editor', onResize);
|
||||
|
||||
onResize();
|
||||
|
||||
function pan(d) {
|
||||
return function() {
|
||||
d3_event.preventDefault();
|
||||
context.pan(d, 100);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// pan amount
|
||||
var pa = 80;
|
||||
|
||||
var pa = 80; // pan amount
|
||||
var keybinding = d3_keybinding('main')
|
||||
.on('⌫', function() { d3_event.preventDefault(); })
|
||||
.on('←', pan([pa, 0]))
|
||||
@@ -316,8 +328,8 @@ export function uiInit(context) {
|
||||
.call(uiShortcuts(context));
|
||||
}
|
||||
|
||||
var osm = context.connection(),
|
||||
auth = uiLoading(context).message(t('loading_auth')).blocking(true);
|
||||
var osm = context.connection();
|
||||
var auth = uiLoading(context).message(t('loading_auth')).blocking(true);
|
||||
|
||||
if (osm && auth) {
|
||||
osm
|
||||
@@ -336,6 +348,85 @@ export function uiInit(context) {
|
||||
hash.startWalkthrough = false;
|
||||
context.container().call(uiIntro(context));
|
||||
}
|
||||
|
||||
|
||||
function onResize() {
|
||||
mapDimensions = utilGetDimensions(content, true);
|
||||
map.dimensions(mapDimensions);
|
||||
|
||||
// shrink photo viewer if it is too big
|
||||
// (-90 preserves space at top and bottom of map used by menus)
|
||||
var photoDimensions = utilGetDimensions(photoviewer, true);
|
||||
if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
|
||||
var setPhotoDimensions = [
|
||||
Math.min(photoDimensions[0], mapDimensions[0]),
|
||||
Math.min(photoDimensions[1], mapDimensions[1] - 90),
|
||||
];
|
||||
|
||||
photoviewer
|
||||
.style('width', setPhotoDimensions[0] + 'px')
|
||||
.style('height', setPhotoDimensions[1] + 'px');
|
||||
|
||||
dispatch.call('photoviewerResize', photoviewer, setPhotoDimensions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function pan(d) {
|
||||
return function() {
|
||||
d3_event.preventDefault();
|
||||
context.pan(d, 100);
|
||||
};
|
||||
}
|
||||
|
||||
function buildResizeListener(target, eventName, dispatch, options) {
|
||||
var resizeOnX = !!options.resizeOnX;
|
||||
var resizeOnY = !!options.resizeOnY;
|
||||
var minHeight = options.minHeight || 240;
|
||||
var minWidth = options.minWidth || 320;
|
||||
var startX;
|
||||
var startY;
|
||||
var startWidth;
|
||||
var startHeight;
|
||||
|
||||
function startResize() {
|
||||
var mapSize = context.map().dimensions();
|
||||
|
||||
if (resizeOnX) {
|
||||
var maxWidth = mapSize[0];
|
||||
var newWidth = clamp((startWidth + d3_event.clientX - startX), minWidth, maxWidth);
|
||||
target.style('width', newWidth + 'px');
|
||||
}
|
||||
|
||||
if (resizeOnY) {
|
||||
var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
|
||||
var newHeight = clamp((startHeight + startY - d3_event.clientY), minHeight, maxHeight);
|
||||
target.style('height', newHeight + 'px');
|
||||
}
|
||||
|
||||
dispatch.call(eventName, target, utilGetDimensions(target, true));
|
||||
}
|
||||
|
||||
function clamp(num, min, max) {
|
||||
return Math.max(min, Math.min(num, max));
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
d3_select(window)
|
||||
.on('.' + eventName, null);
|
||||
}
|
||||
|
||||
return function initResize() {
|
||||
startX = d3_event.clientX;
|
||||
startY = d3_event.clientY;
|
||||
startWidth = target.node().getBoundingClientRect().width;
|
||||
startHeight = target.node().getBoundingClientRect().height;
|
||||
|
||||
d3_select(window)
|
||||
.on('mousemove.' + eventName, startResize, false)
|
||||
.on('mouseup.' + eventName, stopResize, false);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -370,5 +461,5 @@ export function uiInit(context) {
|
||||
|
||||
ui.sidebar = uiSidebar(context);
|
||||
|
||||
return ui;
|
||||
return utilRebind(ui, dispatch, 'on');
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export function uiNoteComments() {
|
||||
mainEnter
|
||||
.append('div')
|
||||
.attr('class', 'comment-text')
|
||||
.text(function(d) { return d.text; });
|
||||
.html(function(d) { return d.html; });
|
||||
|
||||
comments
|
||||
.call(replaceAvatars);
|
||||
@@ -101,6 +101,7 @@ export function uiNoteComments() {
|
||||
if (!s) return null;
|
||||
var detected = utilDetect();
|
||||
var options = { day: 'numeric', month: 'short', year: 'numeric' };
|
||||
s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
|
||||
var d = new Date(s);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
return d.toLocaleDateString(detected.locale, options);
|
||||
|
||||
+123
-12
@@ -1,5 +1,8 @@
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
import {
|
||||
event as d3_event,
|
||||
select as d3_select
|
||||
} from 'd3-selection';
|
||||
|
||||
import { t } from '../util/locale';
|
||||
import { services } from '../services';
|
||||
@@ -53,29 +56,42 @@ export function uiNoteEditor(context) {
|
||||
.attr('class', 'body')
|
||||
.merge(body);
|
||||
|
||||
body.selectAll('.note-editor')
|
||||
.data([0])
|
||||
.enter()
|
||||
var editor = body.selectAll('.note-editor')
|
||||
.data([0]);
|
||||
|
||||
editor = editor.enter()
|
||||
.append('div')
|
||||
.attr('class', 'modal-section note-editor')
|
||||
.merge(editor)
|
||||
.call(noteHeader.note(_note))
|
||||
.call(noteComments.note(_note))
|
||||
.call(noteSave);
|
||||
.call(noteSaveSection);
|
||||
|
||||
|
||||
selection.selectAll('.footer')
|
||||
.data([0])
|
||||
.enter()
|
||||
var footer = selection.selectAll('.footer')
|
||||
.data([0]);
|
||||
|
||||
footer = footer.enter()
|
||||
.append('div')
|
||||
.attr('class', 'footer')
|
||||
.merge(footer)
|
||||
.call(uiViewOnOSM(context).what(_note))
|
||||
.call(uiNoteReport(context).note(_note));
|
||||
|
||||
|
||||
// rerender the note editor on any auth change
|
||||
var osm = services.osm;
|
||||
if (osm) {
|
||||
osm.on('change.note-save', function() {
|
||||
selection.call(noteEditor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function noteSave(selection) {
|
||||
function noteSaveSection(selection) {
|
||||
var isSelected = (_note && _note.id === context.selectedNoteID());
|
||||
var noteSave = selection.selectAll('.note-save-section')
|
||||
var noteSave = selection.selectAll('.note-save')
|
||||
.data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
|
||||
|
||||
// exit
|
||||
@@ -85,7 +101,7 @@ export function uiNoteEditor(context) {
|
||||
// enter
|
||||
var noteSaveEnter = noteSave.enter()
|
||||
.append('div')
|
||||
.attr('class', 'note-save-section save-section cf');
|
||||
.attr('class', 'note-save save-section cf');
|
||||
|
||||
noteSaveEnter
|
||||
.append('h4')
|
||||
@@ -107,6 +123,7 @@ export function uiNoteEditor(context) {
|
||||
// update
|
||||
noteSave = noteSaveEnter
|
||||
.merge(noteSave)
|
||||
.call(userDetails)
|
||||
.call(noteSaveButtons);
|
||||
|
||||
|
||||
@@ -128,7 +145,100 @@ export function uiNoteEditor(context) {
|
||||
}
|
||||
|
||||
|
||||
function userDetails(selection) {
|
||||
var detailSection = selection.selectAll('.detail-section')
|
||||
.data([0]);
|
||||
|
||||
detailSection = detailSection.enter()
|
||||
.append('div')
|
||||
.attr('class', 'detail-section')
|
||||
.merge(detailSection);
|
||||
|
||||
var osm = services.osm;
|
||||
if (!osm) return;
|
||||
|
||||
// Add warning if user is not logged in
|
||||
var hasAuth = osm.authenticated();
|
||||
var authWarning = detailSection.selectAll('.auth-warning')
|
||||
.data(hasAuth ? [] : [0]);
|
||||
|
||||
authWarning.exit()
|
||||
.transition()
|
||||
.duration(200)
|
||||
.style('opacity', 0)
|
||||
.remove();
|
||||
|
||||
var authEnter = authWarning.enter()
|
||||
.insert('div', '.tag-reference-body')
|
||||
.attr('class', 'field-warning auth-warning')
|
||||
.style('opacity', 0);
|
||||
|
||||
authEnter
|
||||
.call(svgIcon('#iD-icon-alert', 'inline'));
|
||||
|
||||
authEnter
|
||||
.append('span')
|
||||
.text(t('note.login'));
|
||||
|
||||
authEnter
|
||||
.append('a')
|
||||
.attr('target', '_blank')
|
||||
.call(svgIcon('#iD-icon-out-link', 'inline'))
|
||||
.append('span')
|
||||
.text(t('login'))
|
||||
.on('click.note-login', function() {
|
||||
d3_event.preventDefault();
|
||||
osm.authenticate();
|
||||
});
|
||||
|
||||
authEnter
|
||||
.transition()
|
||||
.duration(200)
|
||||
.style('opacity', 1);
|
||||
|
||||
|
||||
var prose = detailSection.selectAll('.note-save-prose')
|
||||
.data(hasAuth ? [0] : []);
|
||||
|
||||
prose.exit()
|
||||
.remove();
|
||||
|
||||
prose = prose.enter()
|
||||
.append('p')
|
||||
.attr('class', 'note-save-prose')
|
||||
.text(t('note.upload_explanation'))
|
||||
.merge(prose);
|
||||
|
||||
osm.userDetails(function(err, user) {
|
||||
if (err) return;
|
||||
|
||||
var userLink = d3_select(document.createElement('div'));
|
||||
|
||||
if (user.image_url) {
|
||||
userLink
|
||||
.append('img')
|
||||
.attr('src', user.image_url)
|
||||
.attr('class', 'icon pre-text user-icon');
|
||||
}
|
||||
|
||||
userLink
|
||||
.append('a')
|
||||
.attr('class', 'user-info')
|
||||
.text(user.display_name)
|
||||
.attr('href', osm.userURL(user.display_name))
|
||||
.attr('tabindex', -1)
|
||||
.attr('target', '_blank');
|
||||
|
||||
prose
|
||||
.html(t('note.upload_explanation_with_user', { user: userLink.html() }));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function noteSaveButtons(selection) {
|
||||
var osm = services.osm;
|
||||
var hasAuth = osm && osm.authenticated();
|
||||
|
||||
var isSelected = (_note && _note.id === context.selectedNoteID());
|
||||
var buttonSection = selection.selectAll('.buttons')
|
||||
.data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
|
||||
@@ -168,6 +278,7 @@ export function uiNoteEditor(context) {
|
||||
.merge(buttonEnter);
|
||||
|
||||
buttonSection.select('.status-button') // select and propagate data
|
||||
.attr('disabled', (hasAuth ? null : true))
|
||||
.text(function(d) {
|
||||
var action = (d.status === 'open' ? 'close' : 'open');
|
||||
var andComment = (d.newComment ? '_comment' : '');
|
||||
@@ -186,7 +297,7 @@ export function uiNoteEditor(context) {
|
||||
|
||||
buttonSection.select('.comment-button') // select and propagate data
|
||||
.attr('disabled', function(d) {
|
||||
return (d.status === 'open' && d.newComment) ? null : true;
|
||||
return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
|
||||
})
|
||||
.on('click.save', function(d) {
|
||||
this.blur(); // avoid keeping focus on the button - #4641
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+2
-2
@@ -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",
|
||||
@@ -75,7 +75,7 @@
|
||||
"osm-community-index": "0.4.5",
|
||||
"phantomjs-prebuilt": "~2.1.11",
|
||||
"request": "^2.85.0",
|
||||
"rollup": "~0.60.0",
|
||||
"rollup": "~0.63.2",
|
||||
"rollup-plugin-commonjs": "^9.0.0",
|
||||
"rollup-plugin-includepaths": "~0.2.3",
|
||||
"rollup-plugin-json": "^3.0.0",
|
||||
|
||||
Binary file not shown.
@@ -116,6 +116,7 @@
|
||||
<script src='spec/svg/layers.js'></script>
|
||||
<script src='spec/svg/lines.js'></script>
|
||||
<script src='spec/svg/midpoints.js'></script>
|
||||
<script src='spec/svg/mvt.js'></script>
|
||||
<script src='spec/svg/osm.js'></script>
|
||||
<script src='spec/svg/points.js'></script>
|
||||
<script src='spec/svg/svg.js'></script>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
describe('iD.svgMvt', function () {
|
||||
var context;
|
||||
var surface;
|
||||
var dispatch = d3.dispatch('change');
|
||||
var projection = iD.geoRawMercator()
|
||||
.translate([6934098.868981334, 4092682.5519805425])
|
||||
.scale(iD.geoZoomToScale(17))
|
||||
.clipExtent([[0, 0], [1000, 1000]]);
|
||||
|
||||
|
||||
var gj = {
|
||||
'type': 'FeatureCollection',
|
||||
'features': [
|
||||
{
|
||||
'type': 'Feature',
|
||||
'id': 316973311,
|
||||
'geometry': {
|
||||
'type': 'Point',
|
||||
'coordinates': [
|
||||
-74.38928604125977,
|
||||
40.150275473401365
|
||||
]
|
||||
},
|
||||
'properties': {
|
||||
'abbr': 'N.J.',
|
||||
'area': 19717.8,
|
||||
'name': 'New Jersey',
|
||||
'name_en': 'New Jersey',
|
||||
'osm_id': 316973311
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
context = iD.coreContext();
|
||||
d3.select(document.createElement('div'))
|
||||
.attr('id', 'map')
|
||||
.call(context.map().centerZoom([-74.389286, 40.1502754], 17));
|
||||
|
||||
surface = context.surface();
|
||||
});
|
||||
|
||||
it('creates layer-mvt', function () {
|
||||
var render = iD.svgMvt(projection, context, dispatch);
|
||||
surface.call(render);
|
||||
|
||||
var layers = surface.selectAll('g.layer-mvt').nodes();
|
||||
expect(layers.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('draws geojson', function () {
|
||||
var render = iD.svgMvt(projection, context, dispatch).geojson(gj);
|
||||
surface.call(render);
|
||||
|
||||
var path = surface.selectAll('path.mvt');
|
||||
expect(path.nodes().length).to.eql(1);
|
||||
expect(path.attr('d')).to.match(/^M.*z$/);
|
||||
});
|
||||
|
||||
describe('#url', function() {
|
||||
it('handles pbf url', function () {
|
||||
var url = '../../data/mvttest.pbf';
|
||||
var render = iD.svgMvt(projection, context, dispatch).url(url);
|
||||
surface.call(render);
|
||||
|
||||
var path = surface.selectAll('path.mvt');
|
||||
expect(path.nodes().length).to.eql(1);
|
||||
expect(path.attr('d')).to.match(/^M.*z$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#showLabels', function() {
|
||||
it('shows labels by default', function () {
|
||||
var render = iD.svgMvt(projection, context, dispatch).geojson(gj);
|
||||
surface.call(render);
|
||||
|
||||
var label = surface.selectAll('text.mvtlabel');
|
||||
expect(label.nodes().length).to.eql(1);
|
||||
expect(label.text()).to.eql('New Jersey');
|
||||
|
||||
var halo = surface.selectAll('text.mvtlabel-halo');
|
||||
expect(halo.nodes().length).to.eql(1);
|
||||
expect(halo.text()).to.eql('New Jersey');
|
||||
});
|
||||
|
||||
|
||||
it('hides labels with showLabels(false)', function () {
|
||||
var render = iD.svgMvt(projection, context, dispatch).geojson(gj).showLabels(false);
|
||||
surface.call(render);
|
||||
|
||||
expect(surface.selectAll('text.mvtlabel').empty()).to.be.ok;
|
||||
expect(surface.selectAll('text.mvtlabel-halo').empty()).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user