diff --git a/ui/src/constants.ts b/ui/src/constants.ts index 4f9197ad5..88ef73e83 100644 --- a/ui/src/constants.ts +++ b/ui/src/constants.ts @@ -8,6 +8,8 @@ export const SWITZERLAND_RECTANGLE = Rectangle.fromDegrees(...SWITZERLAND_BOUNDS export const MINIMAP_EXTENT = [5.910642046, 45.191912227, 10.554524194, 48.04750923]; +export const MAP_RECTANGLE = Rectangle.fromDegrees(...[5.91, 45.8179, 10.9, 47.9]); + export const WEB_MERCATOR_TILING_SCHEME = new GeographicTilingScheme({ ellipsoid: Ellipsoid.WGS84, rectangle: SWITZERLAND_RECTANGLE, diff --git a/ui/src/earthquakeVisualization/earthquakeVisualizer.js b/ui/src/earthquakeVisualization/earthquakeVisualizer.js index 4a9edcdbd..b11258725 100644 --- a/ui/src/earthquakeVisualization/earthquakeVisualizer.js +++ b/ui/src/earthquakeVisualization/earthquakeVisualizer.js @@ -35,44 +35,42 @@ export default class EarthquakeVisualizer { } async showEarthquakes() { - fetch(this.config.downloadUrl).then(response => { - response.text().then(text => { - parseEarthquakeData(text).map(data => { - const size = Number(data.Magnitude.split(' ')[0]) * EARTHQUAKE_SPHERE_SIZE_COEF; - const depthMeters = Number(data.Depthkm.split(' ')[0]) * 1000; // convert km to m - const longitude = Number(data.Longitude); - const latitude = Number(data.Latitude); - delete data.Longitude; - delete data.Latitude; - const position = Cartesian3.fromDegrees(longitude, latitude, -depthMeters); - const posCart = Cartographic.fromCartesian(position); - const altitude = this.viewer.scene.globe.getHeight(posCart) || 0; - posCart.height = posCart.height + altitude; - Cartographic.toCartesian(posCart, undefined, position); - const cameraDistance = size * 4; - const zoomHeadingPitchRange = new HeadingPitchRange(0, CMath.toRadians(25), cameraDistance); - data['Details'] = this.config.detailsUrl; - this.boundingRectangle.west = Math.min(CMath.toRadians(longitude), this.boundingRectangle.west); - this.boundingRectangle.south = Math.min(CMath.toRadians(latitude), this.boundingRectangle.south); - this.boundingRectangle.east = Math.max(CMath.toRadians(longitude), this.boundingRectangle.east); - this.boundingRectangle.north = Math.max(CMath.toRadians(latitude), this.boundingRectangle.north); - this.maximumHeight = Math.max(this.maximumHeight, depthMeters * 2); - return this.earthquakeDataSource.entities.add({ - position: position, - ellipsoid: { - radii: new Cartesian3(size, size, size), - material: getColorFromTime(data.Time), - }, - properties: { - ...data, - propsOrder: this.config.propsOrder, - zoomHeadingPitchRange: zoomHeadingPitchRange - } - }); - }); - this.boundingSphere = BoundingSphere.fromRectangle3D(this.boundingRectangle); + const response = await fetch(this.config.downloadUrl); + const text = await response.text(); + parseEarthquakeData(text).map(data => { + const size = Number(data.Magnitude.split(' ')[0]) * EARTHQUAKE_SPHERE_SIZE_COEF; + const depthMeters = Number(data.Depthkm.split(' ')[0]) * 1000; // convert km to m + const longitude = Number(data.Longitude); + const latitude = Number(data.Latitude); + delete data.Longitude; + delete data.Latitude; + const position = Cartesian3.fromDegrees(longitude, latitude, -depthMeters); + const posCart = Cartographic.fromCartesian(position); + const altitude = this.viewer.scene.globe.getHeight(posCart) || 0; + posCart.height = posCart.height + altitude; + Cartographic.toCartesian(posCart, undefined, position); + const cameraDistance = size * 4; + const zoomHeadingPitchRange = new HeadingPitchRange(0, CMath.toRadians(25), cameraDistance); + data['Details'] = this.config.detailsUrl; + this.boundingRectangle.west = Math.min(CMath.toRadians(longitude), this.boundingRectangle.west); + this.boundingRectangle.south = Math.min(CMath.toRadians(latitude), this.boundingRectangle.south); + this.boundingRectangle.east = Math.max(CMath.toRadians(longitude), this.boundingRectangle.east); + this.boundingRectangle.north = Math.max(CMath.toRadians(latitude), this.boundingRectangle.north); + this.maximumHeight = Math.max(this.maximumHeight, depthMeters * 2); + return this.earthquakeDataSource.entities.add({ + position: position, + ellipsoid: { + radii: new Cartesian3(size, size, size), + material: getColorFromTime(data.Time), + }, + properties: { + ...data, + propsOrder: this.config.propsOrder, + zoomHeadingPitchRange: zoomHeadingPitchRange + } }); }); + this.boundingSphere = BoundingSphere.fromRectangle3D(this.boundingRectangle); } /** diff --git a/ui/src/elements/dashboard/ngm-dashboard.ts b/ui/src/elements/dashboard/ngm-dashboard.ts index c396d6543..0409d6e1e 100644 --- a/ui/src/elements/dashboard/ngm-dashboard.ts +++ b/ui/src/elements/dashboard/ngm-dashboard.ts @@ -248,7 +248,6 @@ export class NgmDashboard extends LitElementI18n { const name = await parseKml(this.viewer, href, uploadedLayer, !!asset.clampToGround); this.assetConfigs[href] = { label: name, - zoomToBbox: true, opacity: DEFAULT_LAYER_OPACITY, notSaveToPermalink: true, topicKml: true diff --git a/ui/src/elements/ngm-side-bar.ts b/ui/src/elements/ngm-side-bar.ts index 244458bbe..17a804ff9 100644 --- a/ui/src/elements/ngm-side-bar.ts +++ b/ui/src/elements/ngm-side-bar.ts @@ -40,11 +40,9 @@ import './ngm-share-link'; import '../layers/ngm-layers-upload'; import MainStore from '../store/main'; import {classMap} from 'lit/directives/class-map.js'; -import {zoomTo} from '../utils'; import $ from '../jquery'; import {customElement, property, query, state} from 'lit/decorators.js'; import type QueryManager from '../query/QueryManager'; -import NavToolsStore from '../store/navTools'; import DashboardStore from '../store/dashboard'; import {getAssets} from '../api-ion'; @@ -295,20 +293,13 @@ export class SideBar extends LitElementI18n { html` this.onLayersOrderChange(evt.detail)} - @zoomTo=${evt => { - NavToolsStore.hideTargetPoint(); - zoomTo(this.viewer!, evt.detail); - }}> + .actions=${this.layerActions} + @orderChanged=${(evt) => this.onLayersOrderChange(evt.detail)}> ` : html` { - NavToolsStore.hideTargetPoint(); - zoomTo(this.viewer!, evt.detail); - }} @removeDisplayedLayer=${evt => this.onRemoveDisplayedLayer(evt)} @layerChanged=${evt => this.onLayerChanged(evt)}> ` @@ -726,7 +717,6 @@ export class SideBar extends LitElementI18n { label: name, layer, promise: promise, - zoomToBbox: true, opacity: DEFAULT_LAYER_OPACITY, notSaveToPermalink: true, ownKml: true, diff --git a/ui/src/layers/LayersActions.ts b/ui/src/layers/LayersActions.ts index 17fa87f6d..020f23bb4 100644 --- a/ui/src/layers/LayersActions.ts +++ b/ui/src/layers/LayersActions.ts @@ -1,6 +1,6 @@ import {syncLayersParam} from '../permalink'; import {calculateBox, calculateRectangle, getBoxFromRectangle} from './helpers'; -import {LayerType} from '../constants'; +import {LayerType, MAP_RECTANGLE} from '../constants'; import { Cartesian3, Rectangle, @@ -9,7 +9,7 @@ import { Cesium3DTileset, ImageryLayer, CustomDataSource, - GeoJsonDataSource + GeoJsonDataSource, VoxelPrimitive } from 'cesium'; import type {Viewer} from 'cesium'; @@ -61,29 +61,46 @@ export default class LayersAction { const p = await config.promise; // wrong type in cesium; const rootBoundingVolume = ( p)?.root?.boundingVolume; - if (p instanceof EarthquakeVisualizer && p.boundingRectangle) { // earthquakes - this.boundingBoxEntity.position = Cartographic.toCartesian(Rectangle.center(p.boundingRectangle)); - this.boundingBoxEntity.box.dimensions = getBoxFromRectangle(p.boundingRectangle, p.maximumHeight); - this.boundingBoxEntity.rectangle.coordinates = p.boundingRectangle; - this.boundingBoxEntity.show = true; - this.viewer.scene.requestRender(); + if (p instanceof EarthquakeVisualizer) { // earthquakes + // fetch earthquakes to get bbox info + if (!isFinite(p.boundingRectangle.west)) { + await p.showEarthquakes(); + p.earthquakeDataSource.show = false; + } + this.showBbox(p.boundingRectangle, p.maximumHeight, true); } else if ((p instanceof Cesium3DTileset) && rootBoundingVolume) { const boundingVolume = rootBoundingVolume.boundingVolume; const boundingRectangle = rootBoundingVolume.rectangle; - this.boundingBoxEntity.position = boundingVolume.center; if (boundingRectangle) { + this.boundingBoxEntity.position = boundingVolume.center; this.boundingBoxEntity.box.dimensions = getBoxFromRectangle(boundingRectangle, rootBoundingVolume.maximumHeight); this.boundingBoxEntity.rectangle.coordinates = boundingRectangle; + this.boundingBoxEntity.show = true; + this.viewer.scene.requestRender(); } else { const boxSize = calculateBox(boundingVolume.halfAxes, p.root.boundingSphere.radius); - this.boundingBoxEntity.box.dimensions = boxSize; - this.boundingBoxEntity.rectangle.coordinates = calculateRectangle(boxSize.x, boxSize.y, boundingVolume.center); + const boundingRectangle = calculateRectangle(boxSize.x, boxSize.y, boundingVolume.center); + this.showBbox(boundingRectangle, boxSize.z); } - this.boundingBoxEntity.show = true; - this.viewer.scene.requestRender(); + } else if (p instanceof ImageryLayer) { + this.showBbox(p.imageryProvider.rectangle, 20000); + } else if (p instanceof VoxelPrimitive) { + const boxSize = calculateBox(p.orientedBoundingBox.halfAxes, p.boundingSphere.radius); + const boundingRectangle = calculateRectangle(boxSize.x, boxSize.y, p.orientedBoundingBox.center); + this.showBbox(boundingRectangle, boxSize.z); } } + private showBbox(boundingRectangle: Rectangle, height: number = 0, skipIntersectionCheck: boolean = false) { + if (!boundingRectangle) return; + if (!skipIntersectionCheck) Rectangle.intersection(MAP_RECTANGLE, boundingRectangle, boundingRectangle); + this.boundingBoxEntity.position = Cartographic.toCartesian(Rectangle.center(boundingRectangle)); + this.boundingBoxEntity.box.dimensions = getBoxFromRectangle(boundingRectangle, height); + this.boundingBoxEntity.rectangle.coordinates = boundingRectangle; + this.boundingBoxEntity.show = true; + this.viewer.scene.requestRender(); + } + hideBoundingBox() { if (this.boundingBoxEntity.show) { this.boundingBoxEntity.show = false; @@ -114,4 +131,12 @@ export default class LayersAction { stuff[eventName].addEventListener(callback); } } + + zoomToBbox() { + this.viewer.zoomTo(this.boundingBoxEntity, { + heading: 0, + pitch: -1.57079633, + range: 0 + }); + } } diff --git a/ui/src/layers/ngm-layers-item.ts b/ui/src/layers/ngm-layers-item.ts index 27edb2059..5888477fd 100644 --- a/ui/src/layers/ngm-layers-item.ts +++ b/ui/src/layers/ngm-layers-item.ts @@ -250,14 +250,16 @@ export class NgmLayersItem extends LitElementI18n {
{ if (this.actions && this.actions.showBoundingBox) this.actions.showBoundingBox(this.config); }} @mouseleave=${() => { if (this.actions && this.actions.hideBoundingBox) this.actions.hideBoundingBox(); }} - @click=${() => this.dispatchEvent(new CustomEvent('zoomTo'))}> + @click=${() => { + if (this.actions && this.actions.zoomToBbox) this.actions.zoomToBbox(); + }}>
this.dispatchEvent(new CustomEvent('zoomTo', {detail: config}))} + .actions=${this.actions} > `; diff --git a/ui/src/layers/ngm-layers.ts b/ui/src/layers/ngm-layers.ts index fb9e57ee5..daa3e811b 100644 --- a/ui/src/layers/ngm-layers.ts +++ b/ui/src/layers/ngm-layers.ts @@ -41,7 +41,6 @@ export default class NgmLayers extends LitElementI18n { @removeDisplayedLayer=${() => { this.dispatchEvent(new CustomEvent('removeDisplayedLayer', {detail})); }} - @zoomTo=${() => this.dispatchEvent(new CustomEvent('zoomTo', {detail: config}))} @layerChanged=${() => this.dispatchEvent(new CustomEvent('layerChanged', {detail: config}))} > diff --git a/ui/src/layertree.ts b/ui/src/layertree.ts index 2e3429cbe..07c7663ce 100644 --- a/ui/src/layertree.ts +++ b/ui/src/layertree.ts @@ -21,7 +21,6 @@ export interface LayerTreeNode { // Normally, visible => displayed visible?: boolean; pickable?: boolean; - zoomToBbox?: boolean; opacity?: number; opacityDisabled?: boolean; style?: any; @@ -659,7 +658,6 @@ const geo_base: LayerTreeNode = { visible: false, displayed: true, pickable: true, - zoomToBbox: true, geocatId: '2924c78a-8f1e-4eb4-b6f6-0fb2405fa7df', }, { @@ -683,7 +681,6 @@ const geo_base: LayerTreeNode = { visible: false, displayed: false, pickable: true, - zoomToBbox: true, propsOrder: ['CS-AAT-Cross-section', 'CS-AAT-Lithostratigraphy', 'CS-AAT-Type', 'CS-AAT-Legend', 'CS-AAT-Report'], geocatId: 'ab34eb52-30c4-4b69-840b-ef41f47f9e9a', } @@ -707,7 +704,6 @@ const geo_energy: LayerTreeNode = { layer: 'temperature_model', opacityDisabled: true, pickable: true, - zoomToBbox: false, geocatId: '63ed59b1-d9fb-4c6e-a629-550c8f6b9bf2', }, { @@ -881,7 +877,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_aaretal_litho', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-Aaretal-Legende.pdf', geocatId: 'b1a36f66-638a-4cfb-88d3-b0df6c7a7502', }, @@ -895,7 +890,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_aaretal_logk', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-Aaretal-Legende.pdf', geocatId: '9471ee1b-5811-489d-b050-612c011f9d57', }, @@ -909,7 +903,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_birrfeld_litho', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-Birrfeld-Legende.pdf', geocatId: 'f56c9c6c-ff59-463d-ba66-477fd2d92f39', }, @@ -923,7 +916,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_birrfeld_logk', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-Birrfeld-Legende.pdf', geocatId: '96f923d6-a747-481b-a0d8-2cfec321170e', }, @@ -937,7 +929,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_geneva_litho', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-GVA-Legende.pdf', geocatId: '697f4c99-ed1b-4901-bc87-3710fcce1352', }, @@ -951,7 +942,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_geneva_logk', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-GVA-Legende.pdf', geocatId: '4a4a530f-6a2a-423d-834e-2831d70fde20', }, @@ -965,7 +955,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_visp_litho', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-Visp-Legende.pdf', geocatId: 'b621de46-2553-4fb2-88b4-f770e0243299', }, @@ -979,7 +968,6 @@ const subsurface: LayerTreeNode = { layer: 'voxel_visp_logk', opacityDisabled: true, pickable: true, - zoomToBbox: true, downloadUrl: DOWNLOAD_ROOT_VOXEL + 'legends/Vox-Visp-Legende.pdf', geocatId: 'f7847c2c-bd3a-4dda-99c7-d50453b24c3d', }, @@ -1204,7 +1192,6 @@ const subsurface: LayerTreeNode = { layer: '3d_model_berne', opacity: DEFAULT_LAYER_OPACITY, pickable: true, - zoomToBbox: true, propsOrder: ['3DBern-Unit', '3DBern-Link', '3DBern-Lithology', '3DBern-TectonicUnit', '3DBern-ChronoB-T', '3DBern-OrigDesc', '3DBern-Version', '3DBern-Aothor', '3DBern-Purpose', '3DBern-Download'], diff --git a/ui/src/utils.ts b/ui/src/utils.ts index b6c2498b5..97442458a 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -1,7 +1,5 @@ import CSVParser from 'papaparse'; -import {SWITZERLAND_RECTANGLE} from './constants'; import type {Viewer} from 'cesium'; -import {BoundingSphere, Ellipsoid, HeadingPitchRange} from 'cesium'; export function getURLSearchParams(): URLSearchParams { return new URLSearchParams(location.search); @@ -112,28 +110,6 @@ export function getPercent(min: number, max: number, value: number): number { return value / diff * 100; } -export async function zoomTo(viewer: Viewer, config): Promise { - const p = await config.promise; - if (p.boundingSphere) { - const switzerlandBS = BoundingSphere.fromRectangle3D(SWITZERLAND_RECTANGLE, Ellipsoid.WGS84); - let radiusCoef = switzerlandBS.radius / p.boundingSphere.radius; - radiusCoef = radiusCoef > 3 ? 3 : radiusCoef; - let boundingSphere = p.boundingSphere; - const zoomHeadingPitchRange = new HeadingPitchRange(0, Math.PI / 8, radiusCoef * p.boundingSphere.radius); - if (radiusCoef <= 1) { - zoomHeadingPitchRange.range = p.boundingSphere.radius * 0.8; - zoomHeadingPitchRange.heading = Math.PI / 2; - boundingSphere = switzerlandBS; - } - viewer.camera.flyToBoundingSphere(boundingSphere, { - duration: 0, - offset: zoomHeadingPitchRange - }); - } else { - viewer.zoomTo(p); - } -} - export function debounce(f, ms, skipFirst = false) { let isCooldown = false; let argumentsArr: any[] = [];