mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-18 22:48:10 +02:00
updated: pull from remote
This commit is contained in:
@@ -53,7 +53,7 @@ export function setAreaKeys(value) {
|
||||
|
||||
export function coreContext() {
|
||||
var context = {};
|
||||
context.version = '2.9.0';
|
||||
context.version = '2.9.2';
|
||||
|
||||
// create a special translation that contains the keys in place of the strings
|
||||
var tkeys = _cloneDeep(dataEn);
|
||||
|
||||
+81
-55
@@ -2,66 +2,92 @@ import { range as d3_range } from 'd3-array';
|
||||
|
||||
|
||||
export function d3geoTile() {
|
||||
var size = [960, 500],
|
||||
scale = 256,
|
||||
scaleExtent = [0, 20],
|
||||
translate = [size[0] / 2, size[1] / 2],
|
||||
zoomDelta = 0;
|
||||
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(_) {
|
||||
return Math.min(scaleExtent[1], Math.max(scaleExtent[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),
|
||||
z0 = bound(Math.round(z + zoomDelta)),
|
||||
k = Math.pow(2, z - z0 + 8),
|
||||
origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
|
||||
tiles = [],
|
||||
cols = d3_range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
|
||||
rows = d3_range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
|
||||
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
|
||||
];
|
||||
|
||||
rows.forEach(function(y) {
|
||||
cols.forEach(function(x) {
|
||||
tiles.push([x, y, z0]);
|
||||
});
|
||||
});
|
||||
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)
|
||||
);
|
||||
|
||||
tiles.translate = origin;
|
||||
tiles.scale = k;
|
||||
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];
|
||||
|
||||
return tiles;
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
tile.scaleExtent = function(_) {
|
||||
if (!arguments.length) return scaleExtent;
|
||||
scaleExtent = _;
|
||||
return tile;
|
||||
};
|
||||
|
||||
tile.size = function(_) {
|
||||
if (!arguments.length) return size;
|
||||
size = _;
|
||||
return tile;
|
||||
};
|
||||
|
||||
tile.scale = function(_) {
|
||||
if (!arguments.length) return scale;
|
||||
scale = _;
|
||||
return tile;
|
||||
};
|
||||
|
||||
tile.translate = function(_) {
|
||||
if (!arguments.length) return translate;
|
||||
translate = _;
|
||||
return tile;
|
||||
};
|
||||
|
||||
tile.zoomDelta = function(_) {
|
||||
if (!arguments.length) return zoomDelta;
|
||||
zoomDelta = +_;
|
||||
return tile;
|
||||
};
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
+19
-38
@@ -1,18 +1,25 @@
|
||||
import _extend from 'lodash-es/extend';
|
||||
|
||||
import { osmEntity } from './entity';
|
||||
import { geoExtent } from '../geo';
|
||||
|
||||
import { debug } from '../index';
|
||||
|
||||
|
||||
export function osmNote() {
|
||||
if (!(this instanceof osmNote)) return;
|
||||
|
||||
this.initialize(arguments);
|
||||
return this;
|
||||
if (!(this instanceof osmNote)) {
|
||||
return (new osmNote()).initialize(arguments);
|
||||
} else if (arguments.length) {
|
||||
this.initialize(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
osmNote.id = function() {
|
||||
return osmNote.id.next--;
|
||||
};
|
||||
|
||||
|
||||
osmNote.id.next = -1;
|
||||
|
||||
|
||||
_extend(osmNote.prototype, {
|
||||
|
||||
type: 'note',
|
||||
@@ -31,20 +38,8 @@ _extend(osmNote.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.id && this.type) {
|
||||
this.id = osmEntity.id(this.type);
|
||||
}
|
||||
if (!this.hasOwnProperty('visible')) {
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
Object.freeze(this);
|
||||
Object.freeze(this.tags);
|
||||
|
||||
if (this.loc) Object.freeze(this.loc);
|
||||
if (this.nodes) Object.freeze(this.nodes);
|
||||
if (this.members) Object.freeze(this.members);
|
||||
if (!this.id) {
|
||||
this.id = osmNote.id();
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -54,21 +49,7 @@ _extend(osmNote.prototype, {
|
||||
return new geoExtent(this.loc);
|
||||
},
|
||||
|
||||
geometry: function(graph) {
|
||||
return graph.transient(this, 'geometry', function() {
|
||||
return graph.isPoi(this) ? 'point' : 'vertex';
|
||||
});
|
||||
},
|
||||
|
||||
getID: function() {
|
||||
return this.id;
|
||||
},
|
||||
|
||||
getType: function() {
|
||||
return this.type;
|
||||
},
|
||||
|
||||
getComments: function() {
|
||||
return this.comments;
|
||||
update: function(attrs) {
|
||||
return osmNote(this, attrs, {v: 1 + (this.v || 0)});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,15 @@ export function rendererBackground(context) {
|
||||
|
||||
|
||||
function background(selection) {
|
||||
// If we are displaying an Esri basemap at high zoom,
|
||||
// check its tilemap to see how high the zoom can go
|
||||
if (context.map().zoom() > 18) {
|
||||
var basemap = baseLayer.source();
|
||||
if (basemap && /^EsriWorldImagery/.test(basemap.id)) {
|
||||
var center = context.map().center();
|
||||
basemap.fetchTilemap(center);
|
||||
}
|
||||
}
|
||||
|
||||
var baseFilter = '';
|
||||
if (detected.cssfilters) {
|
||||
@@ -114,16 +123,17 @@ export function rendererBackground(context) {
|
||||
background.updateImagery = function() {
|
||||
if (context.inIntro()) return;
|
||||
|
||||
var b = background.baseLayerSource(),
|
||||
o = _overlayLayers
|
||||
.filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
|
||||
.map(function (d) { return d.source().id; })
|
||||
.join(','),
|
||||
meters = geoOffsetToMeters(b.offset()),
|
||||
epsilon = 0.01,
|
||||
x = +meters[0].toFixed(2),
|
||||
y = +meters[1].toFixed(2),
|
||||
q = utilStringQs(window.location.hash.substring(1));
|
||||
var b = background.baseLayerSource();
|
||||
var o = _overlayLayers
|
||||
.filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
|
||||
.map(function (d) { return d.source().id; })
|
||||
.join(',');
|
||||
|
||||
var meters = geoOffsetToMeters(b.offset());
|
||||
var epsilon = 0.01;
|
||||
var x = +meters[0].toFixed(2);
|
||||
var y = +meters[1].toFixed(2);
|
||||
var q = utilStringQs(window.location.hash.substring(1));
|
||||
|
||||
var id = b.id;
|
||||
if (id === 'custom') {
|
||||
@@ -215,13 +225,12 @@ export function rendererBackground(context) {
|
||||
if (!osm) return background;
|
||||
|
||||
var blacklists = context.connection().imageryBlacklists();
|
||||
var template = d.template();
|
||||
var fail = false;
|
||||
var tested = 0;
|
||||
var regex;
|
||||
|
||||
var template = d.template(),
|
||||
fail = false,
|
||||
tested = 0,
|
||||
regex, i;
|
||||
|
||||
for (i = 0; i < blacklists.length; i++) {
|
||||
for (var i = 0; i < blacklists.length; i++) {
|
||||
try {
|
||||
regex = new RegExp(blacklists[i]);
|
||||
fail = regex.test(template);
|
||||
@@ -270,7 +279,6 @@ export function rendererBackground(context) {
|
||||
|
||||
background.toggleOverlayLayer = function(d) {
|
||||
var layer;
|
||||
|
||||
for (var i = 0; i < _overlayLayers.length; i++) {
|
||||
layer = _overlayLayers[i];
|
||||
if (layer.source() === d) {
|
||||
@@ -350,12 +358,12 @@ export function rendererBackground(context) {
|
||||
return geoExtent([args[2], args[1]]);
|
||||
}
|
||||
|
||||
var dataImagery = data.imagery || [],
|
||||
q = utilStringQs(window.location.hash.substring(1)),
|
||||
requested = q.background || q.layer,
|
||||
extent = parseMap(q.map),
|
||||
first,
|
||||
best;
|
||||
var dataImagery = data.imagery || [];
|
||||
var q = utilStringQs(window.location.hash.substring(1));
|
||||
var requested = q.background || q.layer;
|
||||
var extent = parseMap(q.map);
|
||||
var first;
|
||||
var best;
|
||||
|
||||
// Add all the available imagery sources
|
||||
_backgroundSources = dataImagery.map(function(source) {
|
||||
|
||||
@@ -6,8 +6,16 @@ import {
|
||||
geoMercatorRaw as d3_geoMercatorRaw
|
||||
} from 'd3-geo';
|
||||
|
||||
import { json as d3_json } from 'd3-request';
|
||||
|
||||
import { t } from '../util/locale';
|
||||
import { geoExtent, geoPolygonIntersectsPolygon } from '../geo';
|
||||
|
||||
import {
|
||||
geoExtent,
|
||||
geoPolygonIntersectsPolygon,
|
||||
geoSphericalDistance
|
||||
} from '../geo';
|
||||
|
||||
import { jsonpRequest } from '../util/jsonp_request';
|
||||
import { utilDetect } from '../util/detect';
|
||||
|
||||
@@ -109,19 +117,20 @@ export function rendererBackgroundSource(data) {
|
||||
var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
|
||||
|
||||
switch (this.projection) {
|
||||
case 'EPSG:4326': // todo: alternative codes of WGS 84?
|
||||
return {
|
||||
x: lon * 180 / Math.PI,
|
||||
y: lat * 180 / Math.PI
|
||||
};
|
||||
default: // EPSG:3857 and synonyms
|
||||
var mercCoords = d3_geoMercatorRaw(lon, lat);
|
||||
return {
|
||||
x: 20037508.34 / Math.PI * mercCoords[0],
|
||||
y: 20037508.34 / Math.PI * mercCoords[1]
|
||||
};
|
||||
case 'EPSG:4326': // todo: alternative codes of WGS 84?
|
||||
return {
|
||||
x: lon * 180 / Math.PI,
|
||||
y: lat * 180 / Math.PI
|
||||
};
|
||||
default: // EPSG:3857 and synonyms
|
||||
var mercCoords = d3_geoMercatorRaw(lon, lat);
|
||||
return {
|
||||
x: 20037508.34 / Math.PI * mercCoords[0],
|
||||
y: 20037508.34 / Math.PI * mercCoords[1]
|
||||
};
|
||||
}
|
||||
}).bind(this);
|
||||
|
||||
var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
|
||||
var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
|
||||
return template
|
||||
@@ -205,13 +214,13 @@ rendererBackgroundSource.Bing = function(data, dispatch) {
|
||||
|
||||
data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
|
||||
|
||||
var bing = rendererBackgroundSource(data),
|
||||
key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
|
||||
url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
|
||||
key + '&jsonp={callback}',
|
||||
cache = {},
|
||||
inflight = {},
|
||||
providers = [];
|
||||
var bing = rendererBackgroundSource(data);
|
||||
var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // Same as P2 and JOSM
|
||||
var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
|
||||
key + '&jsonp={callback}';
|
||||
var cache = {};
|
||||
var inflight = {};
|
||||
var providers = [];
|
||||
|
||||
jsonpRequest(url, function(json) {
|
||||
providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
|
||||
@@ -244,10 +253,10 @@ rendererBackgroundSource.Bing = function(data, dispatch) {
|
||||
|
||||
|
||||
bing.getMetadata = function(center, tileCoord, callback) {
|
||||
var tileId = tileCoord.slice(0, 3).join('/'),
|
||||
zoom = Math.min(tileCoord[2], 21),
|
||||
centerPoint = center[1] + ',' + center[0], // lat,lng
|
||||
url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
|
||||
var tileId = tileCoord.slice(0, 3).join('/');
|
||||
var zoom = Math.min(tileCoord[2], 21);
|
||||
var centerPoint = center[1] + ',' + center[0]; // lat,lng
|
||||
var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
|
||||
'?zl=' + zoom + '&key=' + key + '&jsonp={callback}';
|
||||
|
||||
if (inflight[tileId]) return;
|
||||
@@ -290,25 +299,62 @@ rendererBackgroundSource.Bing = function(data, dispatch) {
|
||||
|
||||
|
||||
rendererBackgroundSource.Esri = function(data) {
|
||||
|
||||
// don't request blank tiles, instead overzoom real tiles - #4327
|
||||
// deprecated technique, but it works (for now)
|
||||
// in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
|
||||
if (data.template.match(/blankTile/) === null) {
|
||||
data.template = data.template + '?blankTile=false';
|
||||
}
|
||||
|
||||
var esri = rendererBackgroundSource(data),
|
||||
cache = {},
|
||||
inflight = {};
|
||||
var esri = rendererBackgroundSource(data);
|
||||
var cache = {};
|
||||
var inflight = {};
|
||||
var _prevCenter;
|
||||
|
||||
// use a tilemap service to set maximum zoom for esri tiles dynamically
|
||||
// https://developers.arcgis.com/documentation/tiled-elevation-service/
|
||||
esri.fetchTilemap = function(center) {
|
||||
// skip if we have already fetched a tilemap within 5km
|
||||
if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
|
||||
_prevCenter = center;
|
||||
|
||||
// tiles are available globally to zoom level 19, afterward they may or may not be present
|
||||
var z = 20;
|
||||
|
||||
// first generate a random url using the template
|
||||
var dummyUrl = esri.url([1,2,3]);
|
||||
|
||||
// calculate url z/y/x from the lat/long of the center of the map
|
||||
var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z)));
|
||||
var y = (Math.floor((1 - Math.log(Math.tan(center[1] * Math.PI / 180) + 1 / Math.cos(center[1] * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, z)));
|
||||
|
||||
// fetch an 8x8 grid because responses to leverage cache
|
||||
var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+/, 'tilemap') + '/' + z + '/' + y + ' /' + x + '/8/8';
|
||||
|
||||
// make the request and introspect the response from the tilemap server
|
||||
d3_json(tilemapUrl, function (err, tilemap) {
|
||||
if (err || !tilemap) return;
|
||||
|
||||
var hasTiles = true;
|
||||
for (var i = 0; i < tilemap.data.length; i++) {
|
||||
// 0 means an individual tile in the grid doesn't exist
|
||||
if (!tilemap.data[i]) {
|
||||
hasTiles = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if any tiles are missing at level 20 we restrict maxZoom to 19
|
||||
esri.scaleExtent[1] = (hasTiles ? 22 : 19);
|
||||
});
|
||||
};
|
||||
|
||||
esri.getMetadata = function(center, tileCoord, callback) {
|
||||
var tileId = tileCoord.slice(0, 3).join('/'),
|
||||
zoom = Math.min(tileCoord[2], esri.scaleExtent[1]),
|
||||
centerPoint = center[0] + ',' + center[1], // long, lat (as it should be)
|
||||
unknown = t('info_panels.background.unknown'),
|
||||
metadataLayer,
|
||||
vintage = {},
|
||||
metadata = {};
|
||||
var tileId = tileCoord.slice(0, 3).join('/');
|
||||
var zoom = Math.min(tileCoord[2], esri.scaleExtent[1]);
|
||||
var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
|
||||
var unknown = t('info_panels.background.unknown');
|
||||
var metadataLayer;
|
||||
var vintage = {};
|
||||
var metadata = {};
|
||||
|
||||
if (inflight[tileId]) return;
|
||||
|
||||
|
||||
+16
-3
@@ -6,6 +6,7 @@ import _values from 'lodash-es/values';
|
||||
import { set as d3_set } from 'd3-collection';
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { interpolate as d3_interpolate } from 'd3-interpolate';
|
||||
import { scaleLinear as d3_scaleLinear } from 'd3-scale';
|
||||
|
||||
import {
|
||||
event as d3_event,
|
||||
@@ -455,12 +456,24 @@ export function rendererMap(context) {
|
||||
difference = extent = undefined;
|
||||
}
|
||||
|
||||
var z = String(~~map.zoom());
|
||||
var zoom = map.zoom();
|
||||
var z = String(~~zoom);
|
||||
|
||||
if (surface.attr('data-zoom') !== z) {
|
||||
surface.attr('data-zoom', z)
|
||||
.classed('low-zoom', z <= 16);
|
||||
surface.attr('data-zoom', z);
|
||||
}
|
||||
|
||||
// class surface as `lowzoom` around z17-z18.5 (based on latitude)
|
||||
var lat = map.center()[1];
|
||||
var lowzoom = d3_scaleLinear()
|
||||
.domain([-60, 0, 60])
|
||||
.range([17, 18.5, 17])
|
||||
.clamp(true);
|
||||
|
||||
surface
|
||||
.classed('low-zoom', zoom <= lowzoom(lat));
|
||||
|
||||
|
||||
if (!difference) {
|
||||
supersurface.call(context.background());
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ function getTiles(projection) {
|
||||
s / 2 - projection.translate()[0],
|
||||
s / 2 - projection.translate()[1]];
|
||||
|
||||
return d3_geoTile()
|
||||
var tiles = d3_geoTile()
|
||||
.scaleExtent([tileZoom, tileZoom])
|
||||
.scale(s)
|
||||
.size(projection.clipExtent()[1])
|
||||
@@ -82,6 +82,8 @@ function getTiles(projection) {
|
||||
)
|
||||
};
|
||||
});
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
function getLoc(attrs) {
|
||||
|
||||
+143
-14
@@ -8,6 +8,10 @@ import _isEmpty from 'lodash-es/isEmpty';
|
||||
import _map from 'lodash-es/map';
|
||||
import _uniq from 'lodash-es/uniq';
|
||||
|
||||
import rbush from 'rbush';
|
||||
|
||||
var _notesCache = {};
|
||||
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { xml as d3_xml } from 'd3-request';
|
||||
|
||||
@@ -19,13 +23,14 @@ import {
|
||||
osmEntity,
|
||||
osmNode,
|
||||
osmRelation,
|
||||
osmWay
|
||||
osmWay,
|
||||
osmNote
|
||||
} from '../osm';
|
||||
|
||||
import { utilRebind, utilIdleWorker } from '../util';
|
||||
|
||||
|
||||
var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded');
|
||||
var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
|
||||
var urlroot = 'https://www.openstreetmap.org';
|
||||
var oauth = osmAuth({
|
||||
url: urlroot,
|
||||
@@ -39,12 +44,14 @@ var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
|
||||
var _tiles = { loaded: {}, inflight: {} };
|
||||
var _changeset = {};
|
||||
var _entityCache = {};
|
||||
var _noteTileCache = { loaded: {}, inflight: {}, rtree: rbush() };
|
||||
var _connectionID = 1;
|
||||
var _tileZoom = 16;
|
||||
var _rateLimitError;
|
||||
var _userChangesets;
|
||||
var _userDetails;
|
||||
var _off;
|
||||
var _loadingNotes = false;
|
||||
|
||||
|
||||
function authLoading() {
|
||||
@@ -113,6 +120,28 @@ function getVisible(attrs) {
|
||||
}
|
||||
|
||||
|
||||
function parseComments(comments) {
|
||||
var parsedComments = [];
|
||||
|
||||
// for each comment
|
||||
_forEach(comments, function(comment) {
|
||||
if (comment.nodeName === 'comment') {
|
||||
var childNodes = comment.childNodes;
|
||||
var parsedComment = {};
|
||||
|
||||
_forEach(childNodes, function(node) {
|
||||
if (node.nodeName !== '#text') {
|
||||
var nodeName = node.nodeName;
|
||||
parsedComment[nodeName] = node.innerHTML;
|
||||
}
|
||||
});
|
||||
if (parsedComment) { parsedComments.push(parsedComment); }
|
||||
}
|
||||
});
|
||||
return parsedComments;
|
||||
}
|
||||
|
||||
|
||||
var parsers = {
|
||||
node: function nodeData(obj, uid) {
|
||||
var attrs = obj.attributes;
|
||||
@@ -157,6 +186,37 @@ var parsers = {
|
||||
tags: getTags(obj),
|
||||
members: getMembers(obj)
|
||||
});
|
||||
},
|
||||
|
||||
note: function parseNote(obj, uid) {
|
||||
var attrs = obj.attributes;
|
||||
var childNodes = obj.childNodes;
|
||||
var parsedNote = {};
|
||||
|
||||
parsedNote.loc = getLoc(attrs);
|
||||
|
||||
_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(node.childNodes);
|
||||
} else {
|
||||
parsedNote[nodeName] = node.innerHTML;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parsedNote.id = uid;
|
||||
parsedNote.type = 'note';
|
||||
|
||||
return {
|
||||
minX: parsedNote.loc[0],
|
||||
minY: parsedNote.loc[1],
|
||||
maxX: parsedNote.loc[0],
|
||||
maxY: parsedNote.loc[1],
|
||||
data: new osmNote(parsedNote)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -171,8 +231,24 @@ function parse(xml, callback, options) {
|
||||
function parseChild(child) {
|
||||
var parser = parsers[child.nodeName];
|
||||
if (parser) {
|
||||
var uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
|
||||
if (options.cache && _entityCache[uid]) {
|
||||
|
||||
var uid;
|
||||
var cache = _loadingNotes ? _notesCache : _entityCache;
|
||||
|
||||
// if loading notes, get the note id
|
||||
if (_loadingNotes) {
|
||||
var childNodes = child.childNodes;
|
||||
_forEach(childNodes, function(node) {
|
||||
if (node.nodeName === 'id') {
|
||||
uid = child.nodeName + node.innerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
// otherwise, get the osmEntity id
|
||||
else {
|
||||
uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
|
||||
}
|
||||
if (options.cache && cache[uid]) {
|
||||
return null;
|
||||
}
|
||||
return parser(child, uid);
|
||||
@@ -200,6 +276,13 @@ export default {
|
||||
_tiles = { loaded: {}, inflight: {} };
|
||||
_changeset = {};
|
||||
_entityCache = {};
|
||||
|
||||
if (_noteTileCache && _noteTileCache.inflight) {
|
||||
_forEach(_noteTileCache.inflight, abortRequest);
|
||||
}
|
||||
_noteTileCache = { loaded: {}, inflight: {}, rtree: rbush() };
|
||||
_notesCache = {};
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -272,7 +355,11 @@ export default {
|
||||
parse(xml, function (entities) {
|
||||
if (options.cache) {
|
||||
for (var i in entities) {
|
||||
_entityCache[entities[i].id] = true;
|
||||
if (_loadingNotes) {
|
||||
_notesCache[entities[i].id] = true;
|
||||
} else {
|
||||
_entityCache[entities[i].id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(null, entities);
|
||||
@@ -573,6 +660,9 @@ export default {
|
||||
if (_off) return;
|
||||
|
||||
var that = this;
|
||||
|
||||
var cache = _loadingNotes ? _noteTileCache : _tiles;
|
||||
|
||||
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);
|
||||
@@ -598,36 +688,48 @@ export default {
|
||||
};
|
||||
});
|
||||
|
||||
_filter(_tiles.inflight, function(v, i) {
|
||||
_filter(cache.inflight, function(v, i) {
|
||||
var wanted = _find(tiles, function(tile) {
|
||||
return i === tile.id;
|
||||
});
|
||||
if (!wanted) delete _tiles.inflight[i];
|
||||
if (!wanted) delete cache.inflight[i];
|
||||
return !wanted;
|
||||
}).map(abortRequest);
|
||||
|
||||
// check if loading entities, or notes
|
||||
var path = _loadingNotes ? '/api/0.6/notes?bbox=' : '/api/0.6/map?bbox=';
|
||||
|
||||
tiles.forEach(function(tile) {
|
||||
var id = tile.id;
|
||||
|
||||
if (_tiles.loaded[id] || _tiles.inflight[id]) return;
|
||||
if (cache.loaded[id] || cache.inflight[id]) return;
|
||||
|
||||
if (_isEmpty(_tiles.inflight)) {
|
||||
if (_isEmpty(cache.inflight)) {
|
||||
dispatch.call('loading');
|
||||
}
|
||||
|
||||
_tiles.inflight[id] = that.loadFromAPI(
|
||||
'/api/0.6/map?bbox=' + tile.extent.toParam(),
|
||||
cache.inflight[id] = that.loadFromAPI(
|
||||
path + tile.extent.toParam(),
|
||||
function(err, parsed) {
|
||||
delete _tiles.inflight[id];
|
||||
delete cache.inflight[id];
|
||||
if (!err) {
|
||||
_tiles.loaded[id] = true;
|
||||
cache.loaded[id] = true;
|
||||
}
|
||||
// NOTE: if zoom above min zoom & notes turned on before osm, osm won't render
|
||||
// TODO: either pick this option, or the next one
|
||||
if (_loadingNotes) {
|
||||
cache.rtree.load(parsed);
|
||||
}
|
||||
|
||||
// TODO: figure out how this callback should handle parsed results
|
||||
if (callback) {
|
||||
callback(err, _extend({ data: parsed }, tile));
|
||||
}
|
||||
|
||||
if (_isEmpty(_tiles.inflight)) {
|
||||
if (_isEmpty(cache.inflight)) {
|
||||
if (_loadingNotes) {
|
||||
dispatch.call('loadedNotes');
|
||||
}
|
||||
dispatch.call('loaded');
|
||||
}
|
||||
}
|
||||
@@ -696,5 +798,32 @@ export default {
|
||||
}
|
||||
|
||||
return oauth.authenticate(done);
|
||||
},
|
||||
|
||||
loadNotes: function(projection, dimensions, callback) {
|
||||
var that = this;
|
||||
_loadingNotes = true;
|
||||
that.loadTiles(projection, dimensions, callback);
|
||||
},
|
||||
|
||||
// TODO: possibly remove rtree & refactor caches
|
||||
notes: function(projection) {
|
||||
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 _noteTileCache.rtree.search(bbox)
|
||||
.map(function(d) { return d.data; });
|
||||
},
|
||||
|
||||
loadedNotes: function(_) {
|
||||
if (!arguments.length) return _noteTileCache.loaded;
|
||||
_noteTileCache.loaded = _;
|
||||
return this;
|
||||
},
|
||||
|
||||
notesCache: function() {
|
||||
return _noteTileCache;
|
||||
}
|
||||
};
|
||||
|
||||
+313
-49
@@ -1,3 +1,4 @@
|
||||
import _extend from 'lodash-es/extend';
|
||||
import _flatten from 'lodash-es/flatten';
|
||||
import _forEach from 'lodash-es/forEach';
|
||||
import _map from 'lodash-es/map';
|
||||
@@ -8,6 +9,7 @@ import { range as d3_range } from 'd3-array';
|
||||
import { timer as d3_timer } from 'd3-timer';
|
||||
|
||||
import {
|
||||
event as d3_event,
|
||||
select as d3_select,
|
||||
selectAll as d3_selectAll
|
||||
} from 'd3-selection';
|
||||
@@ -20,6 +22,7 @@ import { geoExtent } from '../geo';
|
||||
import { utilDetect } from '../util/detect';
|
||||
import { utilQsString, utilRebind } from '../util';
|
||||
|
||||
import Q from 'q';
|
||||
|
||||
var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
|
||||
var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
|
||||
@@ -29,10 +32,16 @@ var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
|
||||
var maxResults = 2000;
|
||||
var tileZoom = 16.5;
|
||||
var dispatch = d3_dispatch('loadedBubbles', 'viewerChanged');
|
||||
var minHfov = 10; // zoom in degrees: 20, 10, 5
|
||||
var maxHfov = 90; // zoom out degrees
|
||||
var defaultHfov = 45;
|
||||
var _hires = false;
|
||||
var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
|
||||
var _currScene = 0;
|
||||
var _ssCache;
|
||||
var _pannellumViewer;
|
||||
var _sceneOptions;
|
||||
var _dataUrlArray = [];
|
||||
|
||||
/**
|
||||
* abortRequest().
|
||||
@@ -72,7 +81,7 @@ function localeTimestamp(s) {
|
||||
* 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) {
|
||||
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.
|
||||
@@ -86,12 +95,15 @@ function getTiles(projection) {
|
||||
s / 2 - projection.translate()[1]
|
||||
];
|
||||
|
||||
return d3_geoTile()
|
||||
var tiler = d3_geoTile()
|
||||
.scaleExtent([tileZoom, tileZoom])
|
||||
.scale(s)
|
||||
.size(projection.clipExtent()[1])
|
||||
.translate(projection.translate())()
|
||||
.map(function (tile) {
|
||||
.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 {
|
||||
@@ -108,12 +120,12 @@ function getTiles(projection) {
|
||||
/**
|
||||
* loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
|
||||
*/
|
||||
function loadTiles(which, url, projection) {
|
||||
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).filter(function (t) {
|
||||
var tiles = getTiles(projection, margin).filter(function (t) {
|
||||
return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]);
|
||||
});
|
||||
|
||||
@@ -298,6 +310,182 @@ function searchLimited(psize, limit, projection, rtree) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getImage()
|
||||
*/
|
||||
function getImage(imgInfo) {
|
||||
var response = Q.defer();
|
||||
var img = new Image();
|
||||
|
||||
img.onload = function() {
|
||||
var canvas = document.getElementById('canvas' + imgInfo.face);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, imgInfo.x, imgInfo.y);
|
||||
response.resolve({imgInfo:imgInfo, status: 'ok'});
|
||||
};
|
||||
img.onerror = function() {
|
||||
response.resolve({data: imgInfo, status: 'error'});
|
||||
};
|
||||
img.setAttribute('crossorigin', '');
|
||||
img.src = imgInfo.url;
|
||||
|
||||
return response.promise;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* loadCanvas()
|
||||
*/
|
||||
function loadCanvas(imgInfoGroup) {
|
||||
var response = Q.defer();
|
||||
var getImagePromises = imgInfoGroup.map(function(imgInfo) {
|
||||
return getImage(imgInfo);
|
||||
});
|
||||
|
||||
Q.all(getImagePromises).then(function(data) {
|
||||
var canvas = document.getElementById('canvas' + data[0].imgInfo.face);
|
||||
switch (data[0].imgInfo.face) {
|
||||
case '01':
|
||||
_dataUrlArray[0] = canvas.toDataURL('image/jpeg', 1.0);
|
||||
break;
|
||||
case '02':
|
||||
_dataUrlArray[1] = canvas.toDataURL('image/jpeg', 1.0);
|
||||
break;
|
||||
case '03':
|
||||
_dataUrlArray[2] = canvas.toDataURL('image/jpeg', 1.0);
|
||||
break;
|
||||
case '10':
|
||||
_dataUrlArray[3] = canvas.toDataURL('image/jpeg', 1.0);
|
||||
break;
|
||||
case '11':
|
||||
_dataUrlArray[4] = canvas.toDataURL('image/jpeg', 1.0);
|
||||
break;
|
||||
case '12':
|
||||
_dataUrlArray[5] = canvas.toDataURL('image/jpeg', 1.0);
|
||||
break;
|
||||
}
|
||||
response.resolve({status:'loadCanvas for face ' + data[0].imgInfo.face + 'ok'});
|
||||
});
|
||||
|
||||
return response.promise;
|
||||
}
|
||||
|
||||
|
||||
function setupCanvas(selection, reset) {
|
||||
if (reset) {
|
||||
selection.selectAll('#divForCanvasWork')
|
||||
.remove();
|
||||
}
|
||||
|
||||
// Add the Streetside working canvases. These are used for 'stitching', or combining,
|
||||
// multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
|
||||
selection.selectAll('#divForCanvasWork')
|
||||
.data([0])
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('id', 'divForCanvasWork')
|
||||
.attr('display', 'none')
|
||||
.selectAll('canvas')
|
||||
.data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12'])
|
||||
.enter()
|
||||
.append('canvas')
|
||||
.attr('id', function(d) { return d; })
|
||||
.attr('width', _resolution)
|
||||
.attr('height', _resolution);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* processFaces()
|
||||
*/
|
||||
function processFaces(imgFaceInfoGroups) {
|
||||
var response = Q.defer();
|
||||
var loadCanvasPromises = imgFaceInfoGroups.map(function(faceImgGroup) {
|
||||
return loadCanvas(faceImgGroup);
|
||||
});
|
||||
|
||||
Q.all(loadCanvasPromises).then(function() {
|
||||
response.resolve({status: 'processFaces done'});
|
||||
});
|
||||
|
||||
return response.promise;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function qkToXY(qk) {
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var scale = 256;
|
||||
for (var i = qk.length; i > 0; i--) {
|
||||
var key = qk[i-1];
|
||||
x += (+(key === '1' || key === '3')) * scale;
|
||||
y += (+(key === '2' || key === '3')) * scale;
|
||||
scale *= 2;
|
||||
}
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
|
||||
function getQuadKeys() {
|
||||
var dim = _resolution / 256;
|
||||
var quadKeys;
|
||||
|
||||
if (dim === 16) {
|
||||
quadKeys = [
|
||||
'0000','0001','0010','0011','0100','0101','0110','0111', '1000','1001','1010','1011','1100','1101','1110','1111',
|
||||
'0002','0003','0012','0013','0102','0103','0112','0113', '1002','1003','1012','1013','1102','1103','1112','1113',
|
||||
'0020','0021','0030','0031','0120','0121','0130','0131', '1020','1021','1030','1031','1120','1121','1130','1131',
|
||||
'0022','0023','0032','0033','0122','0123','0132','0133', '1022','1023','1032','1033','1122','1123','1132','1133',
|
||||
'0200','0201','0210','0211','0300','0301','0310','0311', '1200','1201','1210','1211','1300','1301','1310','1311',
|
||||
'0202','0203','0212','0213','0302','0303','0312','0313', '1202','1203','1212','1213','1302','1303','1312','1313',
|
||||
'0220','0221','0230','0231','0320','0321','0330','0331', '1220','1221','1230','1231','1320','1321','1330','1331',
|
||||
'0222','0223','0232','0233','0322','0323','0332','0333', '1222','1223','1232','1233','1322','1323','1332','1333',
|
||||
|
||||
'2000','2001','2010','2011','2100','2101','2110','2111', '3000','3001','3010','3011','3100','3101','3110','3111',
|
||||
'2002','2003','2012','2013','2102','2103','2112','2113', '3002','3003','3012','3013','3102','3103','3112','3113',
|
||||
'2020','2021','2030','2031','2120','2121','2130','2131', '3020','3021','3030','3031','3120','3121','3130','3131',
|
||||
'2022','2023','2032','2033','2122','2123','2132','2133', '3022','3023','3032','3033','3122','3123','3132','3133',
|
||||
'2200','2201','2210','2211','2300','2301','2310','2311', '3200','3201','3210','3211','3300','3301','3310','3311',
|
||||
'2202','2203','2212','2213','2302','2303','2312','2313', '3202','3203','3212','3213','3302','3303','3312','3313',
|
||||
'2220','2221','2230','2231','2320','2321','2330','2331', '3220','3221','3230','3231','3320','3321','3330','3331',
|
||||
'2222','2223','2232','2233','2322','2323','2332','2333', '3222','3223','3232','3233','3322','3323','3332','3333'
|
||||
];
|
||||
|
||||
} else if (dim === 8) {
|
||||
quadKeys = [
|
||||
'000','001','010','011', '100','101','110','111',
|
||||
'002','003','012','013', '102','103','112','113',
|
||||
'020','021','030','031', '120','121','130','131',
|
||||
'022','023','032','033', '122','123','132','133',
|
||||
|
||||
'200','201','210','211', '300','301','310','311',
|
||||
'202','203','212','213', '302','303','312','313',
|
||||
'220','221','230','231', '320','321','330','331',
|
||||
'222','223','232','233', '322','323','332','333'
|
||||
];
|
||||
|
||||
} else if (dim === 4) {
|
||||
quadKeys = [
|
||||
'00','01', '10','11',
|
||||
'02','03', '12','13',
|
||||
|
||||
'20','21', '30','31',
|
||||
'22','23', '32','33'
|
||||
];
|
||||
|
||||
} else { // dim === 2
|
||||
quadKeys = [
|
||||
'0', '1',
|
||||
'2', '3'
|
||||
];
|
||||
}
|
||||
|
||||
return quadKeys;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* init() initialize streetside.
|
||||
@@ -362,8 +550,11 @@ export default {
|
||||
/**
|
||||
* loadBubbles()
|
||||
*/
|
||||
loadBubbles: function (projection) {
|
||||
loadTiles('bubbles', bubbleApi, projection);
|
||||
loadBubbles: function (projection, margin) {
|
||||
// by default: request 2 nearby tiles so we can connect sequences.
|
||||
if (margin === undefined) margin = 2;
|
||||
|
||||
loadTiles('bubbles', bubbleApi, projection, margin);
|
||||
},
|
||||
|
||||
|
||||
@@ -427,6 +618,11 @@ export default {
|
||||
.append('div')
|
||||
.attr('class', 'photo-attribution fillD');
|
||||
|
||||
// create working canvas for stitching together images
|
||||
wrap = wrap
|
||||
.merge(wrapEnter)
|
||||
.call(setupCanvas, true);
|
||||
|
||||
// load streetside pannellum viewer css
|
||||
d3_select('head').selectAll('#streetside-viewercss')
|
||||
.data([0])
|
||||
@@ -470,7 +666,6 @@ export default {
|
||||
_pannellumViewer
|
||||
.removeScene(sceneID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var wrap = d3_select('#photoviewer')
|
||||
@@ -513,6 +708,9 @@ export default {
|
||||
* selectImage().
|
||||
*/
|
||||
selectImage: function (d) {
|
||||
var response = Q.defer();
|
||||
var that = this;
|
||||
|
||||
var viewer = d3_select('#photoviewer');
|
||||
if (!viewer.empty()) viewer.datum(d);
|
||||
|
||||
@@ -520,68 +718,134 @@ export default {
|
||||
|
||||
var wrap = d3_select('#photoviewer .ms-wrapper');
|
||||
var attribution = wrap.selectAll('.photo-attribution').html('');
|
||||
var year = (new Date()).getFullYear();
|
||||
|
||||
if (d) {
|
||||
if (d.captured_by) {
|
||||
attribution
|
||||
.append('a')
|
||||
.attr('class', 'captured_by')
|
||||
.attr('target', '_blank')
|
||||
.attr('href', 'https://www.microsoft.com/en-us/maps/streetside')
|
||||
.text('©' + year + ' Microsoft');
|
||||
wrap.selectAll('.pnlm-load-box') // display "loading.."
|
||||
.style('display', 'block');
|
||||
|
||||
attribution
|
||||
.append('span')
|
||||
.text('|');
|
||||
}
|
||||
if (!d) {
|
||||
response.resolve({status: 'ok'});
|
||||
return response.promise;
|
||||
}
|
||||
|
||||
if (d.captured_at) {
|
||||
attribution
|
||||
.append('span')
|
||||
.attr('class', 'captured_at')
|
||||
.text(localeTimestamp(d.captured_at));
|
||||
}
|
||||
// Add hires checkbox
|
||||
var label = attribution
|
||||
.append('label')
|
||||
.attr('class', 'streetside-hires');
|
||||
|
||||
label
|
||||
.append('input')
|
||||
.attr('type', 'checkbox')
|
||||
.attr('id', 'streetside-hires-input')
|
||||
.property('checked', _hires)
|
||||
.on('click', function() {
|
||||
d3_event.stopPropagation();
|
||||
|
||||
_hires = !_hires;
|
||||
_resolution = _hires ? 1024 : 512;
|
||||
wrap.call(setupCanvas, true);
|
||||
|
||||
var viewstate = {
|
||||
yaw: _pannellumViewer.getYaw(),
|
||||
pitch: _pannellumViewer.getPitch(),
|
||||
hfov: _pannellumViewer.getHfov()
|
||||
};
|
||||
|
||||
that.selectImage(d)
|
||||
.then(function(r) {
|
||||
if (r.status === 'ok') {
|
||||
_sceneOptions = _extend(_sceneOptions, viewstate);
|
||||
that.showViewer();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
label
|
||||
.append('span')
|
||||
.text(t('streetside.hires'));
|
||||
|
||||
|
||||
// Add capture date
|
||||
if (d.captured_by) {
|
||||
var yyyy = (new Date()).getFullYear();
|
||||
|
||||
attribution
|
||||
.append('a')
|
||||
.attr('class', 'image_link')
|
||||
.attr('class', 'captured_by')
|
||||
.attr('target', '_blank')
|
||||
.attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' + encodeURIComponent(d.key) +
|
||||
'&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17')
|
||||
.text(t('streetside.report'));
|
||||
.attr('href', 'https://www.microsoft.com/en-us/maps/streetside')
|
||||
.text('©' + yyyy + ' Microsoft');
|
||||
|
||||
attribution
|
||||
.append('span')
|
||||
.text('|');
|
||||
}
|
||||
|
||||
if (d.captured_at) {
|
||||
attribution
|
||||
.append('span')
|
||||
.attr('class', 'captured_at')
|
||||
.text(localeTimestamp(d.captured_at));
|
||||
}
|
||||
|
||||
// Add image link
|
||||
attribution
|
||||
.append('a')
|
||||
.attr('class', 'image_link')
|
||||
.attr('target', '_blank')
|
||||
.attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' + encodeURIComponent(d.key) +
|
||||
'&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17')
|
||||
.text(t('streetside.report'));
|
||||
|
||||
|
||||
var bubbleIdQuadKey = d.key.toString(4);
|
||||
var paddingNeeded = 16 - bubbleIdQuadKey.length;
|
||||
for (var i = 0; i < paddingNeeded; i++) {
|
||||
bubbleIdQuadKey = '0' + bubbleIdQuadKey;
|
||||
}
|
||||
var bubbleIdQuadKey = d.key.toString(4);
|
||||
var paddingNeeded = 16 - bubbleIdQuadKey.length;
|
||||
for (var i = 0; i < paddingNeeded; i++) {
|
||||
bubbleIdQuadKey = '0' + bubbleIdQuadKey;
|
||||
}
|
||||
var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
|
||||
var imgUrlSuffix = '.jpg?g=6338&n=z';
|
||||
|
||||
// Order matters here: front=01, right=02, back=03, left=10, up=11, down=12
|
||||
var imgLocIdxArr = ['01','02','03','10','11','12'];
|
||||
var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
|
||||
var imgUrlSuffix = '.jpg?g=6338&n=z';
|
||||
// Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
|
||||
var faceKeys = ['01','02','03','10','11','12'];
|
||||
|
||||
// Map images to cube faces
|
||||
var quadKeys = getQuadKeys();
|
||||
var faces = faceKeys.map(function(faceKey) {
|
||||
return quadKeys.map(function(quadKey) {
|
||||
var xy = qkToXY(quadKey);
|
||||
return {
|
||||
face: faceKey,
|
||||
url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
|
||||
x: xy[0],
|
||||
y: xy[1]
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
processFaces(faces).then(function() {
|
||||
_sceneOptions = {
|
||||
showFullscreenCtrl: false,
|
||||
autoLoad: true,
|
||||
compass: true,
|
||||
northOffset: d.ca,
|
||||
yaw: 0,
|
||||
minHfov: minHfov,
|
||||
maxHfov: maxHfov,
|
||||
hfov: defaultHfov,
|
||||
type: 'cubemap',
|
||||
cubeMap: [
|
||||
imgUrlPrefix + imgLocIdxArr[0] + imgUrlSuffix,
|
||||
imgUrlPrefix + imgLocIdxArr[1] + imgUrlSuffix,
|
||||
imgUrlPrefix + imgLocIdxArr[2] + imgUrlSuffix,
|
||||
imgUrlPrefix + imgLocIdxArr[3] + imgUrlSuffix,
|
||||
imgUrlPrefix + imgLocIdxArr[4] + imgUrlSuffix,
|
||||
imgUrlPrefix + imgLocIdxArr[5] + imgUrlSuffix
|
||||
_dataUrlArray[0],
|
||||
_dataUrlArray[1],
|
||||
_dataUrlArray[2],
|
||||
_dataUrlArray[3],
|
||||
_dataUrlArray[4],
|
||||
_dataUrlArray[5]
|
||||
]
|
||||
};
|
||||
}
|
||||
response.resolve({status: 'ok'});
|
||||
});
|
||||
|
||||
return this;
|
||||
return response.promise;
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ export default {
|
||||
// A few OSM keys expect values to contain uppercase values (see #3377).
|
||||
// This is not an exhaustive list (e.g. `name` also has uppercase values)
|
||||
// but these are the fields where taginfo value lookup is most useful.
|
||||
var re = /network|taxon|genus|species|brand|grape_variety|rating|:output|_hours|_times/;
|
||||
var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|booth|rating|:output|_hours|_times/;
|
||||
var allowUpperCase = (params.key.match(re) !== null);
|
||||
var f = filterValues(allowUpperCase);
|
||||
|
||||
|
||||
+12
-5
@@ -30,10 +30,10 @@ export function svgNotes(projection, context, dispatch) {
|
||||
}
|
||||
|
||||
function getService() {
|
||||
if (services.notes && !_notes) {
|
||||
_notes = services.notes;
|
||||
_notes.event.on('loadedNotes', throttledRedraw);
|
||||
} else if (!services.notes && _notes) {
|
||||
if (services.osm && !_notes) {
|
||||
_notes = services.osm;
|
||||
_notes.on('loadedNotes', throttledRedraw);
|
||||
} else if (!services.osm && _notes) {
|
||||
_notes = null;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,13 @@ export function svgNotes(projection, context, dispatch) {
|
||||
var enabled = svgNotes.enabled,
|
||||
service = getService();
|
||||
|
||||
function dimensions() {
|
||||
return [window.innerWidth, window.innerHeight];
|
||||
}
|
||||
function done() {
|
||||
console.log('placeholder done within svg/notes.upload.done');
|
||||
}
|
||||
|
||||
layer = selection.selectAll('.layer-notes')
|
||||
.data(service ? [0] : []);
|
||||
|
||||
@@ -123,7 +130,7 @@ export function svgNotes(projection, context, dispatch) {
|
||||
if (service && ~~context.map().zoom() >= minZoom) {
|
||||
editOn();
|
||||
update();
|
||||
service.loadNotes(projection);
|
||||
service.loadNotes(projection, dimensions(), done);
|
||||
} else {
|
||||
editOff();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export function svgStreetside(projection, context, dispatch) {
|
||||
var layer = d3_select(null);
|
||||
var _viewerYaw = 0;
|
||||
var _selectedSequence = null;
|
||||
var _hoveredBubble = null;
|
||||
var _streetside;
|
||||
|
||||
/**
|
||||
@@ -105,7 +106,12 @@ export function svgStreetside(projection, context, dispatch) {
|
||||
|
||||
service
|
||||
.selectImage(d)
|
||||
.showViewer(_viewerYaw);
|
||||
.then(function(r) {
|
||||
if (r.status === 'ok'){
|
||||
service.showViewer(_viewerYaw);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
context.map().centerEase(d.loc);
|
||||
}
|
||||
@@ -115,7 +121,8 @@ export function svgStreetside(projection, context, dispatch) {
|
||||
*/
|
||||
function mouseover(d) {
|
||||
var service = getService();
|
||||
if (service) service.setStyles(d);
|
||||
_hoveredBubble = d;
|
||||
if (service) service.setStyles(d, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +130,8 @@ export function svgStreetside(projection, context, dispatch) {
|
||||
*/
|
||||
function mouseout() {
|
||||
var service = getService();
|
||||
if (service) service.setStyles(null);
|
||||
_hoveredBubble = null;
|
||||
if (service) service.setStyles(null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +196,10 @@ export function svgStreetside(projection, context, dispatch) {
|
||||
|
||||
|
||||
var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
|
||||
.data(bubbles, function(d) { return d.key; });
|
||||
.data(bubbles, function(d) {
|
||||
// force reenter once bubbles are attached to a sequence
|
||||
return d.key + (d.sequenceKey ? 'v1' : 'v0');
|
||||
});
|
||||
|
||||
// exit
|
||||
groups.exit()
|
||||
@@ -241,6 +252,11 @@ export function svgStreetside(projection, context, dispatch) {
|
||||
.attr('d', viewfieldPath);
|
||||
|
||||
|
||||
if (service) {
|
||||
service.setStyles(_hoveredBubble, true);
|
||||
}
|
||||
|
||||
|
||||
function viewfieldPath() {
|
||||
var d = this.parentNode.__data__;
|
||||
if (d.pano) {
|
||||
@@ -287,7 +303,6 @@ export function svgStreetside(projection, context, dispatch) {
|
||||
if (service && ~~context.map().zoom() >= minZoom) {
|
||||
editOn();
|
||||
update();
|
||||
|
||||
service.loadBubbles(projection);
|
||||
} else {
|
||||
editOff();
|
||||
|
||||
Reference in New Issue
Block a user