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 + *