diff --git a/css/80_app_fb.css b/css/80_app_fb.css index fa2a5268d..56b032203 100644 --- a/css/80_app_fb.css +++ b/css/80_app_fb.css @@ -202,20 +202,27 @@ button.rapid-features.layer-off use { .rapid-inspector .header svg.logo-rapid { height: 35px; } -.rapid-inspector .header button.rapid-inspector-close { +.rapid-inspector .header button.rapid-inspector-close, +.overture-inspector .header button.overture-inspector-close { position: absolute; top: 0; right: 0; background: transparent; border-radius: 0px; } -.ideditor[dir='rtl'] .rapid-inspector .header button.rapid-inspector-close { +.ideditor[dir='rtl'] .rapid-inspector .header button.rapid-inspector-close, +.ideditor[dir='rtl'] .overture-inspector .header button.overture-inspector-close + { right: unset; left: 0; } .rapid-inspector .header button.rapid-inspector-close svg { color: #da26d3; } +.rapid-inspector .header button.rapid-inspector-close svg { + color: #000000; +} + .rapid-inspector .header button.rapid-inspector-close:hover { background: #8885; } @@ -240,14 +247,16 @@ button.rapid-features.layer-off use { padding: 20px 0; } -.rapid-inspector .feature-info { +.rapid-inspector .feature-info, +.overture-inspector .feature-info { display: flex; padding: 5px 10px; border-radius: 5px 5px 0 0; border: 1px solid #333; } -.rapid-inspector .feature-info .dataset-label { +.rapid-inspector .feature-info .dataset-label, +.overture-inspector .feature-info .dataset-label { flex: 1; font-size: 14px; font-weight: bold; @@ -256,7 +265,8 @@ button.rapid-features.layer-off use { font-size: 13px; } -.rapid-inspector .tag-info { +.rapid-inspector .tag-info, +.overture-inspector .property-info { padding: 7px; background: #444; color: #ddd; @@ -267,9 +277,12 @@ button.rapid-features.layer-off use { display: flex; flex-flow: row wrap; } -.rapid-inspector .tag-heading { +.rapid-inspector .tag-heading, +.overture-inspector .property-heading { margin-right: 5px; + margin-top: 5px; padding-left: 3px; + font-size: larger; } .rapid-inspector .tag-info .tag-entry { display: flex; @@ -281,12 +294,31 @@ button.rapid-features.layer-off use { font-weight: 300; margin: 0 3px; } + +.overture-inspector .property-info .property-entry { + display: flex; + flex-flow: row wrap; + border-radius: 5px; + font-size: smaller; + font-weight: 300; + margin: 0 3px; +} + .rapid-inspector .tag-key, -.rapid-inspector .tag-value { +.rapid-inspector .tag-value, +.overture-inspector .property-key { flex: 0 1 auto; padding: 0px 3px; } -.rapid-inspector .tag-key { + +.overture-inspector .property-value { + border: 1px solid #aaa; + border-radius: 5px; + padding: 0 3px; +} + +.rapid-inspector .tag-key, +.overture-inspector .property-key { font-weight: bold; font-size: unset; } @@ -355,6 +387,10 @@ button.rapid-features.layer-off use { padding: 15px; } +.overture-inspector img.wordmark-overture { + width:100%; +} + /* For things that should stack in rows */ .rapid-stack { display: flex; diff --git a/dist/img/omf-wordmark-rtl.svg b/dist/img/omf-wordmark-rtl.svg new file mode 100644 index 000000000..985a0e3d5 --- /dev/null +++ b/dist/img/omf-wordmark-rtl.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/img/omf-wordmark.svg b/dist/img/omf-wordmark.svg new file mode 100644 index 000000000..b110e842b --- /dev/null +++ b/dist/img/omf-wordmark.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/behaviors/SelectBehavior.js b/modules/behaviors/SelectBehavior.js index d0dbb1a0b..818675a1c 100644 --- a/modules/behaviors/SelectBehavior.js +++ b/modules/behaviors/SelectBehavior.js @@ -345,6 +345,7 @@ export class SelectBehavior extends AbstractBehavior { // Clicked a non-OSM feature.. if ( data.__fbid__ || // Clicked a Rapid feature.. + data.overture || // Clicked an Overture feature.. data.__featurehash__ || // Clicked Custom Data (e.g. gpx track).. data instanceof QAItem || // Clicked a QA Item (OSM Note, KeepRight, Osmose, Maproulette).. data.type === 'detection' // Clicked on an object detection / traffic sign.. diff --git a/modules/modes/SelectMode.js b/modules/modes/SelectMode.js index fb6a0919c..94ff655ee 100644 --- a/modules/modes/SelectMode.js +++ b/modules/modes/SelectMode.js @@ -98,6 +98,8 @@ export class SelectMode extends AbstractMode { if (layerID === 'osm') layerID = 'notes'; } else if (datum.__fbid__) { // a Rapid feature layerID = 'rapid'; + } else if (datum.overture) { // Overture data + layerID = 'rapid'; } else if (datum.__featurehash__) { // custom data layerID = 'custom-data'; } else if (datum.type === 'detection') { // A detection (object or sign) @@ -178,6 +180,11 @@ export class SelectMode extends AbstractMode { } else if (datum.__featurehash__) { sidebarContent = uiDataEditor(context).datum(datum); + // Selected Overture feature... + } else if (datum.overture) { + Sidebar.OvertureInspector.datum = datum; + sidebarContent = Sidebar.OvertureInspector.render; + // Selected Rapid feature... } else if (datum.__fbid__) { Sidebar.RapidInspector.datum = datum; diff --git a/modules/pixi/PixiLayerOverture.js b/modules/pixi/PixiLayerOverture.js deleted file mode 100644 index d0f5a5501..000000000 --- a/modules/pixi/PixiLayerOverture.js +++ /dev/null @@ -1,155 +0,0 @@ -import * as PIXI from 'pixi.js'; - -import { AbstractLayer } from './AbstractLayer.js'; -import { PixiFeaturePoint } from './PixiFeaturePoint.js'; - - - -/** - * PixiLayerOverture - * This class contains rendering code for any overture datasets that are set in the Rapid catalog. - * The data for these are scraped from the RapidSystem's datasets, specifically those with the 'overture' service moniker. - * There is no actual Overture service yet though. - * @class - */ -export class PixiLayerOverture extends AbstractLayer { - - /** - * @constructor - * @param scene The Scene that owns this Layer - * @param layerID Unique string to use for the name of this Layer - */ - constructor(scene, layerID) { - super(scene, layerID); - this._enabled = true; - - const overlays = new PIXI.Container(); - overlays.name = `${this.layerID}`; - overlays.sortableChildren = false; - overlays.interactiveChildren = true; - this.overlaysContainer = overlays; - this._overlaysDefined = false; - - const datasets = this.context.systems.rapid.datasets; - for (const dataset of datasets.values()) { - if (dataset.overlay) { - this._overlaysDefined = true; - } - } - - const basemapContainer = this.scene.groups.get('basemap'); - basemapContainer.addChild(overlays); - } - - - /** - * supported - * Whether the Layer's service exists - */ - get supported() { - const service = this.context.services; - return !!service.overture; - } - - /** - * render - * Render the Overture PMTIle data - * @param frame Integer frame being rendered - * @param viewport Pixi viewport to use for rendering - * @param zoom Effective zoom to use for rendering - */ - render(frame, viewport, zoom) { - if (!this.enabled) return; - - const datasets = this.context.systems.rapid.datasets; - - for (const dataset of datasets.values()) { - if (dataset.service === 'overture' && dataset.enabled) { - this.renderDataset(dataset, frame, viewport, zoom); - } - } - } - - /** - * renderDataset - * Render any data we have, and schedule fetching more of it to cover the view - * - * @param dataset Object - * @param frame Integer frame being rendered - * @param viewport Pixi viewport to use for rendering - * @param zoom Effective zoom to use for rendering - */ - renderDataset(dataset, frame, viewport, zoom) { - const context = this.context; - - const service = context.services[dataset.service]; // Should be 'overture' if we've gotten here - if (!service?.started) return; - - if (zoom >= 16) { // avoid firing off too many API requests - service.loadTiles(dataset.id); // fetch more - } - - const entities = service.getData(dataset.id); - - this.renderPoints(dataset, frame, viewport, zoom, entities); - } - - - /** - * renderPoints - * @param dataset The Rapid dataset definition - * @param frame Integer frame being rendered - * @param viewport Pixi viewport to use for rendering - * @param zoom Effective zoom to use for rendering - * @param lines Array of point data - */ - renderPoints(dataset, frame, viewport, zoom, points) { - const l10n = this.context.systems.l10n; - const parentContainer = this.scene.groups.get('points'); - - const pointStyle = { - markerName: 'largeCircle', - markerTint: dataset.color, - iconName: 'maki-circle-stroked', - labelTint: dataset.color - }; - - for (const d of points) { - const dataID = d.id; - const version = d.v || 0; - const parts = (d.geojson.geometry.type === 'Point') ? [d.geojson.geometry.coordinates] - : (d.geometry.type === 'MultiPoint') ? d.geometry.coordinates : []; - - for (let i = 0; i < parts.length; ++i) { - const coords = parts[i]; - const featureID = `${this.layerID}-${dataID}-${i}`; - let feature = this.features.get(featureID); - - // If feature existed before as a different type, recreate it. - if (feature && feature.type !== 'point') { - feature.destroy(); - feature = null; - } - - if (!feature) { - feature = new PixiFeaturePoint(this, featureID); - feature.style = pointStyle; - feature.parentContainer = parentContainer; - } - - // If data has changed.. Replace it. - if (feature.v !== version) { - feature.v = version; - feature.geometry.setCoords(coords); - feature.label = l10n.displayName(d.geojson.properties); - feature.setData(dataID, d); - } - - this.syncFeatureClasses(feature); - feature.update(viewport, zoom); - this.retainFeature(feature, frame); - } - } - } - -} diff --git a/modules/pixi/PixiLayerRapid.js b/modules/pixi/PixiLayerRapid.js index 60c3c7606..4c7ec112f 100644 --- a/modules/pixi/PixiLayerRapid.js +++ b/modules/pixi/PixiLayerRapid.js @@ -180,9 +180,7 @@ export class PixiLayerRapid extends AbstractLayer { //this._uniforms.u_time = frame/10; for (const dataset of rapid.datasets.values()) { - if (dataset.service !== 'overture') { this.renderDataset(dataset, frame, viewport, zoom); - } } } @@ -214,9 +212,14 @@ export class PixiLayerRapid extends AbstractLayer { useConflation = false; } - // Adjust the dataset id for whether we want the data conflated or not. + // Adjust the dataset id for whether we want the data conflated or not const datasetID = dataset.id + (useConflation ? '-conflated' : ''); - const dsGraph = service.graph(datasetID); + + // Overture data isn't editable, nor conflatable... yet. + let dsGraph = null; + if (dataset.service !== 'overture') { + dsGraph = service.graph(datasetID); + } // Filter out features that have already been accepted or ignored by the user. function isAcceptedOrIgnored(entity) { @@ -272,6 +275,19 @@ export class PixiLayerRapid extends AbstractLayer { data.polygons.push(entity); } } + } else if (dataset.service === 'overture') { + + if (zoom >= 16) { // avoid firing off too many API requests + service.loadTiles(datasetID); // fetch more + } + const entities = service.getData(datasetID); + + // Just support points (for now) + for (const entity of entities) { + entity.overture = true; + entity.__datasetid__ = datasetID; + data.points.push(entity); + } } const pointsContainer = this.scene.groups.get('points'); @@ -432,7 +448,7 @@ export class PixiLayerRapid extends AbstractLayer { if (!feature) { feature = new PixiFeaturePoint(this, featureID); - feature.geometry.setCoords(entity.loc); + feature.geometry.setCoords(entity.loc || entity.geojson.geometry.coordinates); feature.parentContainer = parentContainer; feature.rapidFeature = true; feature.setData(entity.id, entity); @@ -442,12 +458,19 @@ export class PixiLayerRapid extends AbstractLayer { if (feature.dirty) { feature.style = pointStyle; - feature.label = l10n.displayName(entity.tags); - // experiment: label addresses - const housenumber = entity.tags['addr:housenumber']; - if (!feature.label && housenumber) { - feature.label = housenumber; + + if (entity.geojson){ + feature.label = entity.geojson.properties['@name']; + } else { + feature.label = l10n.displayName(entity.tags); + + // experiment: label addresses + const housenumber = entity.tags['addr:housenumber']; + if (!feature.label && housenumber) { + feature.label = housenumber; + } } + feature.update(viewport, zoom); } diff --git a/modules/pixi/PixiScene.js b/modules/pixi/PixiScene.js index 0da8e14e8..86265c2bf 100644 --- a/modules/pixi/PixiScene.js +++ b/modules/pixi/PixiScene.js @@ -15,7 +15,6 @@ import { PixiLayerMapUI } from './PixiLayerMapUI.js'; import { PixiLayerOsm } from './PixiLayerOsm.js'; import { PixiLayerOsmNotes } from './PixiLayerOsmNotes.js'; import { PixiLayerOsmose } from './PixiLayerOsmose.js'; -import { PixiLayerOverture } from './PixiLayerOverture.js'; import { PixiLayerRapid } from './PixiLayerRapid.js'; import { PixiLayerRapidOverlay } from './PixiLayerRapidOverlay.js'; import { PixiLayerStreetsidePhotos } from './PixiLayerStreetsidePhotos.js'; @@ -95,7 +94,6 @@ export class PixiScene extends EventEmitter { new PixiLayerOsm(this, 'osm'), new PixiLayerRapid(this, 'rapid'), new PixiLayerRapidOverlay(this, 'rapid-overlay'), - new PixiLayerOverture(this, 'overture'), new PixiLayerMapillaryDetections(this, 'mapillary-detections'), new PixiLayerMapillarySigns(this, 'mapillary-signs'), diff --git a/modules/services/OvertureService.js b/modules/services/OvertureService.js index cb7f63004..2541e340c 100644 --- a/modules/services/OvertureService.js +++ b/modules/services/OvertureService.js @@ -48,7 +48,7 @@ export class OvertureService extends AbstractSystem { this.latestRelease = this.pmTilesCatalog.releases.find(release => release.release_id === dateStrings[0]); }) .catch(error => { - console.error('Error fetching or parsing the PMTiles STAC Catalog: ', error); // eslint-disable-line no-console + console.error('Error fetching or parsing the PMTiles Catalog: ', error); // eslint-disable-line no-console }); } @@ -89,7 +89,7 @@ export class OvertureService extends AbstractSystem { // just this one for now const places = new RapidDataset(this.context, { id: 'overture-places', - conflated: true, + conflated: false, service: 'overture', categories: new Set(['overture', 'places', 'featured']), color: '#00ffff', diff --git a/modules/ui/UiOvertureInspector.js b/modules/ui/UiOvertureInspector.js new file mode 100644 index 000000000..d02a1fe47 --- /dev/null +++ b/modules/ui/UiOvertureInspector.js @@ -0,0 +1,302 @@ +import { select, selection } from 'd3-selection'; +import { marked } from 'marked'; + +import { uiIcon } from './icon.js'; +import { uiFlash } from './flash.js'; +import { uiTooltip } from './tooltip.js'; + + + +/** + * UiOvertureInspector + * The OvertureInspector is a UI component for viewing Overture Entities in the sidebar. + * Because Overture entities conform to a certain schema, we might at some point build a JSON-Schema-aware + * version of this code that modifies the display of the data. + * + * @example + *
+ *
+ *
+ *
// Theme name, e.g. "Places" or "Addresses" + *
// List of properties on this feature + *
+ *
+ */ +export class UiOvertureInspector { + /** + * @constructor + * @param `context` Global shared application context + */ + constructor(context) { + this.context = context; + + this.datum = null; + this._keys = null; + + // D3 selections + this.$parent = null; + this.$inspector = null; + + // Create child components + this.AcceptTooltip = uiTooltip(context).placement('bottom'); + this.IgnoreTooltip = uiTooltip(context).placement('bottom'); + + // Ensure methods used as callbacks always have `this` bound correctly. + // (This is also necessary when using `d3-selection.call`) + this.render = this.render.bind(this); + this.renderFeatureInfo = this.renderFeatureInfo.bind(this); + this.renderPropertyInfo = this.renderPropertyInfo.bind(this); + this.renderNotice = this.renderNotice.bind(this); + } + + + /** + * render + * Accepts a parent selection, and renders the content under it. + * (The parent selection is required the first time, but can be inferred on subsequent renders.) + * @param {d3-selection} $parent - A d3-selection to a HTMLElement that this component should render itself into + */ + render($parent = this.$parent) { + if ($parent instanceof selection) { + this.$parent = $parent; + } else { + return; // no parent - called too early? + } + + const context = this.context; + const l10n = context.systems.l10n; + const rtl = l10n.isRTL() ? '-rtl' : ''; + + let $inspector = $parent.selectAll('.overture-inspector') + .data([0]); + + const $$inspector = $inspector.enter() + .append('div') + .attr('class', 'overture-inspector'); + + + // add `.header` + const $$header = $$inspector + .append('div') + .attr('class', 'header'); + + $$header + .append('h3') + .append('img') + .attr('class', 'wordmark-overture'); + + $$header + .append('button') + .attr('class', 'overture-inspector-close') + .on('click', () => context.enter('browse')) + .call(uiIcon('#rapid-icon-close')); + + // add `.body` + $$inspector + .append('div') + .attr('class', 'body'); + + // update + this.$inspector = $inspector = $inspector.merge($$inspector); + $inspector.selectAll('img.wordmark-overture') + .attr('src', this.context.assetPath + 'img/omf-wordmark' + rtl + '.svg'); + + // localize logo + $inspector.selectAll('.logo-overture > use') + .attr('xlink:href', `#overture-logo-overture-wordmark${rtl}`); + + $inspector.selectAll('.body') + .call(this.renderFeatureInfo) + .call(this.renderPropertyInfo) + .call(this.renderNotice); + } + + + /** + * getBrightness + * This is used to get the brightness of the given hex color. + * (We use this to know whether text written over this color should be light or dark). + * https://www.w3.org/TR/AERT#color-contrast + * https://stackoverflow.com/questions/49437263/contrast-between-label-and-background-determine-if-color-is-light-or-dark/49437644#49437644 + * @param {string} color - a hexstring like '#rgb', '#rgba', '#rrggbb', '#rrggbbaa' (alpha values are ignored) + * @return {number} A number representing the perceived brightness + */ + getBrightness(color) { + const short = (color.length < 6); + const r = parseInt(short ? color[1] + color[1] : color[1] + color[2], 16); + const g = parseInt(short ? color[2] + color[2] : color[3] + color[4], 16); + const b = parseInt(short ? color[3] + color[3] : color[5] + color[6], 16); + return ((r * 299) + (g * 587) + (b * 114)) / 1000; + } + + + /** + * renderFeatureInfo + * Renders the 'feature-info' section (the dataset name) + * @param {d3-selection} $selection - A d3-selection to a HTMLElement that this content should render itself into + */ + renderFeatureInfo($selection) { + const datum = this.datum; + if (!datum) return; + + const context = this.context; + const l10n = context.systems.l10n; + const rapid = context.systems.rapid; + + const datasetID = datum.__datasetid__; + const dataset = rapid.datasets.get(datasetID); + const color = dataset.color; + + let $featureInfo = $selection.selectAll('.feature-info') + .data([0]); + + // enter + const $$featureInfo = $featureInfo.enter() + .append('div') + .attr('class', 'feature-info'); + + $$featureInfo + .append('div') + .attr('class', 'dataset-label'); + + // update + $featureInfo = $featureInfo.merge($$featureInfo); + + $featureInfo + .style('background', color) + .style('color', this.getBrightness(color) > 140.5 ? '#333' : '#fff'); + + // Attempt to localize the dataset name, fallback to 'label' or 'id' + const text = dataset.labelStringID ? l10n.t(dataset.labelStringID) : (dataset.label || dataset.id); + $featureInfo.selectAll('.dataset-label') + .text(text); + } + + + /** + * renderPropertyInfo + * Renders the 'property-info' section + * @param {d3-selection} $selection - A d3-selection to a HTMLElement that this content should render itself into + */ + renderPropertyInfo($selection) { + const properties = this.datum?.geojson.properties; + if (!properties) return; + + const context = this.context; + const l10n = context.systems.l10n; + + let $propInfo = $selection.selectAll('.property-info') + .data([0]); + + // enter + const $$propInfo = $propInfo.enter() + .append('div') + .attr('class', 'property-info'); + + const $$propBag = $$propInfo + .append('div') + .attr('class', 'property-bag'); + + + //Overture properties can come to us as strings, JSON arrays, or JSON objects. Handle all three! + for (const [k, v] of Object.entries(properties)) { + const $$propHeading = $$propBag + .append('div') + .attr('class', 'property-heading'); + + let key = k; + + // Some params come to us via pmtiles with a prepended '@' sign. + if (key.startsWith('@')) { + key = key.slice(1); + } + key = key.charAt(0).toUpperCase() + key.slice(1); + $$propHeading.text(key); + + const $$tagEntry = $$propBag.append('div').attr('class', 'property-entry'); + const parsedJson = this._getJsonStructure(v); + if (parsedJson === null) continue; + + if (Object.keys(parsedJson).length !== 0) { + // Object processing + if (!Array.isArray(parsedJson)) { + for (const [k1, v1] of Object.entries(parsedJson)) { + $$tagEntry.append('div').attr('class', 'property-value').text(k1 + ':' + v1); + } + + //Array processing + } else { + for (const entry of parsedJson) { + + if ( entry instanceof Object ) { + for (const [k1,v1] of Object.entries(entry)){ + $$tagEntry.append('div').attr('class', 'property-value').text(k1 + ':' + v1); + } + } else { + $$tagEntry.append('div').attr('class', 'property-value').text(entry); + } + } + } + } else { + //String handling- just make a key/value pair. + $$tagEntry.append('div').attr('class', 'property-value').text(v); + } + } + + // update + $propInfo = $propInfo.merge($$propInfo); + + $propInfo.selectAll('.tag-heading') + .text(l10n.t('overture_feature_inspector.properties')); + } + + /** + * _getJsonStructure is used to test the values we receive from the Overture data, which may be strings, Json arrays, or Json objects. + * @returns null if the str isn't a string, empty object {} if the string can't be parsed into JSON, or the parsed object. + */ + _getJsonStructure(str) { + if (typeof str !== 'string') return null; + try { + const result = JSON.parse(str); + return result; + } catch (err) { + return {}; + } +} + + /** + * renderNotice + * Renders the 'overture-inspector-notice' section + * This section contains remarks about the data - license, usage, or other hints + * @param {d3-selection} $selection - A d3-selection to a HTMLElement that this content should render itself into + */ + renderNotice($selection) { + const context = this.context; + const l10n = context.systems.l10n; + const rapid = context.systems.rapid; + const datum = this.datum; + if (!datum) return; + + const datasetID = datum.__datasetid__.replace('-conflated', ''); + const dataset = rapid.datasets.get(datasetID); + + // Only display notice data for open data (for now) + if (dataset.tags?.includes('opendata')) { + + let $notice = $selection.selectAll('.overture-inspector-notice') + .data([0]); + + // enter + const $$notice = $notice.enter() + .append('div') + .attr('class', 'overture-inspector-notice'); + + $$notice + .html(marked.parse(l10n.t('rapid_feature_inspector.notice.open_data', {license: dataset.license_markdown}))); + + // update + $notice = $notice.merge($$notice); + } + + } +} diff --git a/modules/ui/UiRapidDatasetToggle.js b/modules/ui/UiRapidDatasetToggle.js index 84606c5aa..cd9535449 100644 --- a/modules/ui/UiRapidDatasetToggle.js +++ b/modules/ui/UiRapidDatasetToggle.js @@ -436,7 +436,7 @@ export class UiRapidDatasetToggle { if (dataset) { dataset.color = color; - scene.dirtyLayers(['rapid', 'rapid-overlay', 'overture']); + scene.dirtyLayers(['rapid', 'rapid-overlay']); gfx.immediateRedraw(); this.render(); diff --git a/modules/ui/UiSidebar.js b/modules/ui/UiSidebar.js index a169219d4..5f76d6cf4 100644 --- a/modules/ui/UiSidebar.js +++ b/modules/ui/UiSidebar.js @@ -13,6 +13,7 @@ import { uiMapRouletteEditor } from './maproulette_editor.js'; import { uiOsmoseEditor } from './osmose_editor.js'; import { uiNoteEditor } from './note_editor.js'; import { UiRapidInspector } from './UiRapidInspector.js'; +import { UiOvertureInspector } from './UiOvertureInspector.js'; import { uiTooltip } from './tooltip.js'; @@ -57,6 +58,7 @@ export class UiSidebar { this.NoteEditor = uiNoteEditor(context); this.OsmoseEditor = uiOsmoseEditor(context); this.RapidInspector = new UiRapidInspector(context); + this.OvertureInspector = new UiOvertureInspector(context); this.Tooltip = uiTooltip(context); // D3 selections @@ -233,12 +235,14 @@ export class UiSidebar { // Hovering on Geo Data (vector tile, geojson, etc..) if (datum?.__featurehash__) { this.show(this.DataEditor.datum(datum)); - // Hovering on Rapid data.. } else if (datum?.__fbid__) { this.RapidInspector.datum = datum; this.show(this.RapidInspector.render); - + // Hovering on Overture data.. + } else if (datum?.overture) { + this.OvertureInspector.datum = datum; + this.show(this.OvertureInspector.render); // Hovering on Mapillary detection.. } else if (datum?.type === 'detection') { this.show(this.DetectionInspector.datum(datum)); diff --git a/modules/ui/index.js b/modules/ui/index.js index 3f5ff17b5..d651cee2b 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -45,6 +45,7 @@ export { uiNoteComments } from './note_comments.js'; export { uiNoteEditor } from './note_editor.js'; export { uiNoteHeader } from './note_header.js'; export { uiNoteReport } from './note_report.js'; +export { UiOvertureInspector } from './UiOvertureInspector.js'; export { uiOsmoseDetails } from './osmose_details.js'; export { uiOsmoseEditor } from './osmose_editor.js'; export { uiOsmoseHeader } from './osmose_header.js';