From d92afe3dc7a6d7cde5318362759cec2a493cc88b Mon Sep 17 00:00:00 2001 From: sezerbozbiyik Date: Wed, 24 May 2023 18:08:24 +0300 Subject: [PATCH] add point and line --- css/60_photos.css | 12 ++++ modules/services/mapilio.js | 105 +++++++++++++++++++++++----------- modules/svg/mapilio_images.js | 84 +++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 32 deletions(-) diff --git a/css/60_photos.css b/css/60_photos.css index d3f2f1f96..18a7c37a9 100644 --- a/css/60_photos.css +++ b/css/60_photos.css @@ -244,6 +244,18 @@ stroke: #20c4ff; } +/* Mapilio Image Layer */ +.layer-mapilio { + pointer-events: none; +} +.layer-mapilio .viewfield-group * { + fill: #0056f1; + stroke: #ffffff; + fill-opacity: .7; +} +.layer-mapilio .sequence { + stroke: #0056f1; +} /* Streetside Viewer (pannellum) */ .ms-wrapper .photo-attribution .image-link { diff --git a/modules/services/mapilio.js b/modules/services/mapilio.js index 40a56c8fd..67250ce1e 100644 --- a/modules/services/mapilio.js +++ b/modules/services/mapilio.js @@ -5,9 +5,12 @@ import RBush from 'rbush'; import { VectorTile } from '@mapbox/vector-tile'; import { utilRebind, utilTiler } from '../util'; +import {geoExtent, geoScaleToZoom} from '../geo'; const baseTileUrl = 'https://geo.mapilio.com/geoserver/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=mapilio:'; -const pointsTileUrl = `${baseTileUrl}points_mapilio_map&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}`; +const pointLayer = 'points_mapilio_map'; +const lineLayer = 'captured_roads_line'; +const tileStyle = '&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}'; const minZoom = 14; const dispatch = d3_dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged'); @@ -16,6 +19,31 @@ let _mlyActiveImage; let _mlyCache; +// Partition viewport into higher zoom tiles +function partitionViewport(projection) { + const z = geoScaleToZoom(projection.scale()); + const z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5 + const tiler = utilTiler().zoomExtent([z2, z2]); + + return tiler.getTiles(projection) + .map(function(tile) { return tile.extent; }); +} + + +// Return no more than `limit` results per partition. +function searchLimited(limit, projection, rtree) { + limit = limit || 5; + + return partitionViewport(projection) + .reduce(function(result, extent) { + const found = rtree.search(extent.bbox()) + .slice(0, limit) + .map(function(d) { return d.data; }); + + return (found.length ? result.concat(found) : result); + }, []); +} + // Load all data for the specified type from Mapilio vector tiles function loadTiles(which, url, maxZoom, projection) { const tiler = utilTiler().zoomExtent([minZoom, maxZoom]).skipNullIsland(true); @@ -100,46 +128,21 @@ function loadTileDataToCache(data, tile, which) { } } - if (vectorTile.layers.hasOwnProperty('sequence')) { + if (vectorTile.layers.hasOwnProperty('captured_roads_line')) { features = []; cache = _mlyCache.sequences; - layer = vectorTile.layers.sequence; + layer = vectorTile.layers.captured_roads_line; for (i = 0; i < layer.length; i++) { feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]); - if (cache.lineString[feature.properties.id]) { - cache.lineString[feature.properties.id].push(feature); + if (cache.lineString[feature.properties.sequence_uuid]) { + cache.lineString[feature.properties.sequence_uuid].push(feature); } else { - cache.lineString[feature.properties.id] = [feature]; + cache.lineString[feature.properties.sequence_uuid] = [feature]; } } } - if (vectorTile.layers.hasOwnProperty('point')) { - features = []; - cache = _mlyCache[which]; - layer = vectorTile.layers.point; - - for (i = 0; i < layer.length; i++) { - feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]); - loc = feature.geometry.coordinates; - - d = { - loc: loc, - id: feature.properties.id, - first_seen_at: feature.properties.first_seen_at, - last_seen_at: feature.properties.last_seen_at, - value: feature.properties.value - }; - features.push({ - minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d - }); - } - if (cache.rtree) { - cache.rtree.load(features); - } - } - } @@ -168,10 +171,48 @@ export default { _mlyActiveImage = null; }, + // Get visible images + images: function(projection) { + const limit = 5; + return searchLimited(limit, projection, _mlyCache.images.rtree); + }, + // Load images in the visible area loadImages: function(projection) { - loadTiles('images', pointsTileUrl, 14, projection); + let url = baseTileUrl + pointLayer + tileStyle; + loadTiles('images', url, 14, projection); + }, + + // Load line in the visible area + loadLines: function(projection) { + let url = baseTileUrl + lineLayer + tileStyle; + loadTiles('line', url, 14, projection); + }, + + // Get visible sequences + sequences: function(projection) { + const viewport = projection.clipExtent(); + const min = [viewport[0][0], viewport[1][1]]; + const max = [viewport[1][0], viewport[0][1]]; + const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); + const sequenceIds = {}; + let lineStrings = []; + + _mlyCache.images.rtree.search(bbox) + .forEach(function(d) { + if (d.data.sequence_id) { + sequenceIds[d.data.sequence_id] = true; + } + }); + + Object.keys(sequenceIds).forEach(function(sequenceId) { + if (_mlyCache.sequences.lineString[sequenceId]) { + lineStrings = lineStrings.concat(_mlyCache.sequences.lineString[sequenceId]); + } + }); + + return lineStrings; }, diff --git a/modules/svg/mapilio_images.js b/modules/svg/mapilio_images.js index 7724ee126..13d383321 100644 --- a/modules/svg/mapilio_images.js +++ b/modules/svg/mapilio_images.js @@ -2,6 +2,7 @@ import _throttle from 'lodash-es/throttle'; import { select as d3_select } from 'd3-selection'; import { services } from '../services'; +import {svgPath, svgPointTransform} from './helpers'; export function svgMapilioImages(projection, context, dispatch) { @@ -55,6 +56,14 @@ export function svgMapilioImages(projection, context, dispatch) { .on('end', editOff); } + function transform(d) { + let t = svgPointTransform(projection)(d); + if (d.ca) { + t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; + } + return t; + } + function editOn() { layer.style('display', 'block'); @@ -66,6 +75,79 @@ export function svgMapilioImages(projection, context, dispatch) { layer.style('display', 'none'); } + function update() { + + const z = ~~context.map().zoom(); + + const service = getService(); + let sequences = (service ? service.sequences(projection) : []); + let images = (service ? service.images(projection) : []); + + + // service.filterViewer(context); + + let traces = layer.selectAll('.sequences').selectAll('.sequence') + .data(sequences, function(d) { return d.properties.id; }); + + // exit + traces.exit() + .remove(); + // + // // enter/update + traces = traces.enter() + .append('path') + .attr('class', 'sequence') + .merge(traces) + .attr('d', svgPath(projection).geojson); + + + const groups = layer.selectAll('.markers').selectAll('.viewfield-group') + .data(images, function(d) { return d.id; }); + + // exit + groups.exit() + .remove(); + + // enter + const groupsEnter = groups.enter() + .append('g') + .attr('class', 'viewfield-group'); + + groupsEnter + .append('g') + .attr('class', 'viewfield-scale'); + + // update + const markers = groups + .merge(groupsEnter) + .sort(function(a, b) { + return b.loc[1] - a.loc[1]; // sort Y + }) + .attr('transform', transform) + .select('.viewfield-scale'); + + + markers.selectAll('circle') + .data([0]) + .enter() + .append('circle') + .attr('dx', '0') + .attr('dy', '0') + .attr('r', '6'); + + const viewfields = markers.selectAll('.viewfield') + .data([0]); + + viewfields.exit() + .remove(); + + viewfields.enter() // viewfields may or may not be drawn... + .insert('path', 'circle') // but if they are, draw below the circles + .attr('class', 'viewfield') + .attr('transform', 'scale(1.5,1.5),translate(-8, -13)') + + } + function drawImages(selection) { const enabled = svgMapilioImages.enabled; @@ -96,7 +178,9 @@ export function svgMapilioImages(projection, context, dispatch) { if (enabled) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); + update(); service.loadImages(projection); + service.loadLines(projection); } else { editOff(); }