From 209147791fa4b6503dd443cccf9a39c2bcdd60ba Mon Sep 17 00:00:00 2001 From: Ib Green Date: Fri, 8 Sep 2023 16:38:44 -0400 Subject: [PATCH 1/4] feat(tiles): 3DTile services --- .../src/tile3d-service/tile3d-service.ts | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 modules/tiles/src/tile3d-service/tile3d-service.ts diff --git a/modules/tiles/src/tile3d-service/tile3d-service.ts b/modules/tiles/src/tile3d-service/tile3d-service.ts new file mode 100644 index 0000000000..05a4a975c1 --- /dev/null +++ b/modules/tiles/src/tile3d-service/tile3d-service.ts @@ -0,0 +1,142 @@ +// loaders.gl, MIT license +import type {Loader} from '@loaders.gl/loader-utils'; +import {_getIonTilesetMetadata} from '@loaders.gl/3d-tiles'; + +// TODO - we can't import everything here, so mock for now +const Tiles3DLoader: Loader = {}; +const I3SLoader: Loader = {}; +const CesiumIONLoader: Loader = {}; + +export type Tile3DAttribution = { + title: string; + url: string; + logoUrl?: string; + height?: number; + bottom?: number; +}; + +abstract class Tile3DService { + static getServiceFromUrl(url: string, services: Tile3DService[]): Tile3DService | null { + return services.find((service) => url.includes(service.urlKey)) || null; + } + + abstract readonly id: string; + abstract readonly name: string; + abstract readonly urlKey: string; + /** Default attribution per supported tileset provider. */ + abstract readonly attribution: Tile3DAttribution; + + abstract readonly loader: Loader; + + url: string; + accessToken: string; + + constructor(url: string, accessToken: string) { + this.url = url; + this.accessToken = accessToken; + } + + /** Queries metadata from a 3D tileset service. + * @param url Url of a 3D tileset. + * @param this.accessToken Optional access token. + * @returns Downloaded metadata. + */ + abstract getMetadata(): Promise; + + isSupported(mapState: any): boolean { + return true; + } +} + +/** + * @see https://developers.google.com/maps/documentation/tile/policies + * attribution shouldn't be hidden (right now dataset attribution is only partly shown and expanded on hover). + * attribution should be visible (right now displayed with a grayish color, unnoticeable with Google 3d tiles) + * Google logo should be displayed on the bottom left side (where FSQ watermark is located). + */ +export class Google3DTilesService extends Tile3DService { + readonly id = 'google'; + readonly name = 'Google 3D Tiles'; + readonly urlKey = 'google'; + // Also, attribution for Google 3D tiles is collected from visible tiles. + readonly attribution = { + title: 'Built with Google Maps.', + url: '', + logoUrl: + 'https://developers.google.com/static/maps/documentation/images/google_on_non_white.png', + height: 16 + }; + + readonly loader = Tiles3DLoader; + + isSupported(mapState: any): boolean { + return mapState.globe?.enabled || mapState.zoom < 8; + } + + async getMetadata(): Promise { + if (!this.accessToken) { + throw new Error(`No access token for ${this.name}`); + } + const response = await fetch(`${url}?key=${this.accessToken}`); + if (!response.ok) { + throw new Error(`Failed to fetch ${this.name} ${response.status}`); + } + return {name: this.name, ...(await response.json())}; + } +} + +export class CesiumIONService extends Tile3DService { + readonly id = 'cesium'; + readonly name = 'Cesium ion'; + readonly urlKey = 'ion.cesium'; + readonly attribution = { + title: 'Cesium.', + url: 'https://cesium.com/', + height: 16, + bottom: -2 + }; + + readonly loader = CesiumIONLoader; + + async getMetadata(): Promise { + if (!this.accessToken) { + throw new Error(`No access token for ${this.name}`); + } + const matched = this.url.match(/\/([0-9]+)\/tileset.json/); + const assetId = matched && matched[1]; + const response = await _getIonTilesetMetadata(this.accessToken, assetId); + if (response.status !== 'COMPLETE') { + throw new Error(`Failed to fetch ${this.name} ${response.status}`); + } + return {name: this.name, ...response}; + } + + getUrl(tileUrl: string): string { + return `${tileUrl}?key=${this.accessToken}`; + } + + getLoadOptions() { + return {fetch: {headers: {'X-GOOG-API-KEY': this.accessToken}}}; + } +} + +export class ArcGISI3SService extends Tile3DService { + id = 'arcgis'; + name = 'ArcGIS'; + urlKey = 'arcgis'; + attribution = { + title: 'Powered by Esri.', + url: 'https://arcgis.com/', + height: 16 + }; + + loader = I3SLoader; + + async getMetadata(): Promise { + const response = await fetch(this.url); + if (!response.ok) { + throw new Error(`Failed to fetch ${this.name} ${response.status}`); + } + return {name: this.name, ...(await response.json())}; + } +} From c50223550a3a6dd7d70ab2c307ed44714ac5225a Mon Sep 17 00:00:00 2001 From: Ib Green Date: Fri, 8 Sep 2023 17:49:47 -0400 Subject: [PATCH 2/4] wip --- modules/3d-tiles/src/index.ts | 4 + modules/3d-tiles/src/lib/ion/ion.ts | 1 + .../src/services/cesium-ion-service.ts | 45 ++++++ .../src/services/google-3d-tiles-service.ts | 41 +++++ modules/i3s/src/index.ts | 3 + .../i3s/src/services/arcgis-i3s-service.ts | 31 ++++ modules/tiles/src/index.ts | 3 + modules/tiles/src/services/tile3d-service.ts | 59 ++++++++ .../src/tile3d-service/tile3d-service.ts | 142 ------------------ 9 files changed, 187 insertions(+), 142 deletions(-) create mode 100644 modules/3d-tiles/src/services/cesium-ion-service.ts create mode 100644 modules/3d-tiles/src/services/google-3d-tiles-service.ts create mode 100644 modules/i3s/src/services/arcgis-i3s-service.ts create mode 100644 modules/tiles/src/services/tile3d-service.ts delete mode 100644 modules/tiles/src/tile3d-service/tile3d-service.ts diff --git a/modules/3d-tiles/src/index.ts b/modules/3d-tiles/src/index.ts index 5224b838d2..4d0350f9c5 100644 --- a/modules/3d-tiles/src/index.ts +++ b/modules/3d-tiles/src/index.ts @@ -28,3 +28,7 @@ export type { ImplicitTilingExensionData } from './types'; export type {Tiles3DLoaderOptions} from './tiles-3d-loader'; + +// EXPERIMENTAL SERVICES +export {Google3DTilesService} from './services/google-3d-tiles-service'; +export {CesiumIONService} from './services/cesium-ion-service'; diff --git a/modules/3d-tiles/src/lib/ion/ion.ts b/modules/3d-tiles/src/lib/ion/ion.ts index 5c71bfa354..cbabfbd963 100644 --- a/modules/3d-tiles/src/lib/ion/ion.ts +++ b/modules/3d-tiles/src/lib/ion/ion.ts @@ -1,3 +1,4 @@ +// loaders.gl, MIT license // Minimal support to load tilsets from the Cesium ION services import {fetchFile} from '@loaders.gl/core'; diff --git a/modules/3d-tiles/src/services/cesium-ion-service.ts b/modules/3d-tiles/src/services/cesium-ion-service.ts new file mode 100644 index 0000000000..b67bbb20d6 --- /dev/null +++ b/modules/3d-tiles/src/services/cesium-ion-service.ts @@ -0,0 +1,45 @@ +// loaders.gl, MIT license + +import {Tile3DService} from '@loaders.gl/tiles'; +import {getIonTilesetMetadata} from '../lib/ion/ion'; +import {CesiumIonLoader} from '../cesium-ion-loader'; + +/** + * Attribution for Cesium ion. + * @see https://cesium.com/legal/terms-of-service/ + */ +export class CesiumIONService extends Tile3DService { + readonly id = 'cesium'; + readonly name = 'Cesium ion'; + readonly urlKey = 'ion.cesium'; + readonly attribution = { + title: 'Cesium.', + url: 'https://cesium.com/', + height: 16, + bottom: -2 + }; + + /** @todo remove CesiumIONLoader, integrate into service? */ + readonly loader = CesiumIonLoader; + + async getMetadata(): Promise { + if (!this.accessToken) { + throw new Error(`No access token for ${this.name}`); + } + const matched = /\/([0-9]+)\/tileset.json/.exec(this.url); + const assetId = matched && matched[1]; + const response = await getIonTilesetMetadata(this.accessToken, assetId); + if (response.status !== 'COMPLETE') { + throw new Error(`Failed to fetch ${this.name} ${response.status}`); + } + return {name: this.name, ...response}; + } + + getUrl(tileUrl: string): string { + return `${tileUrl}?key=${this.accessToken}`; + } + + getLoadOptions() { + return {fetch: {headers: {'X-GOOG-API-KEY': this.accessToken}}}; + } +} diff --git a/modules/3d-tiles/src/services/google-3d-tiles-service.ts b/modules/3d-tiles/src/services/google-3d-tiles-service.ts new file mode 100644 index 0000000000..070abcf90c --- /dev/null +++ b/modules/3d-tiles/src/services/google-3d-tiles-service.ts @@ -0,0 +1,41 @@ +// loaders.gl, MIT license + +import {Tile3DService} from '@loaders.gl/tiles'; +import {Tiles3DLoader} from '../tiles-3d-loader'; + +/** + * @see https://developers.google.com/maps/documentation/tile/policies + * attribution shouldn't be hidden (right now dataset attribution is only partly shown and expanded on hover). + * attribution should be visible (right now displayed with a grayish color, unnoticeable with Google 3d tiles) + * Google logo should be displayed on the bottom left side (where FSQ watermark is located). + */ +export class Google3DTilesService extends Tile3DService { + readonly id = 'google'; + readonly name = 'Google 3D Tiles'; + readonly urlKey = 'google'; + // Also, attribution for Google 3D tiles is collected from visible tiles. + readonly attribution = { + title: 'Built with Google Maps.', + url: '', + logoUrl: + 'https://developers.google.com/static/maps/documentation/images/google_on_non_white.png', + height: 16 + }; + + readonly loader = Tiles3DLoader; + + isSupported(mapState: any): boolean { + return mapState.globe?.enabled || mapState.zoom < 8; + } + + async getMetadata(): Promise { + if (!this.accessToken) { + throw new Error(`No access token for ${this.name}`); + } + const response = await fetch(`${this.url}?key=${this.accessToken}`); + if (!response.ok) { + throw new Error(`Failed to fetch ${this.name} ${response.status}`); + } + return {name: this.name, ...(await response.json())}; + } +} diff --git a/modules/i3s/src/index.ts b/modules/i3s/src/index.ts index e12cc895c7..faf6bbab9a 100644 --- a/modules/i3s/src/index.ts +++ b/modules/i3s/src/index.ts @@ -47,3 +47,6 @@ export {I3SBuildingSceneLayerLoader} from './i3s-building-scene-layer-loader'; export {I3SNodePageLoader} from './i3s-node-page-loader'; export {ArcGISWebSceneLoader} from './arcgis-webscene-loader'; export {parseSLPK} from './lib/parsers/parse-slpk/parse-slpk'; + +// EXPERIMENTAL EXPORTS +export {ArcGISI3SService} from './services/arcgis-i3s-service'; diff --git a/modules/i3s/src/services/arcgis-i3s-service.ts b/modules/i3s/src/services/arcgis-i3s-service.ts new file mode 100644 index 0000000000..4a2ed9ba72 --- /dev/null +++ b/modules/i3s/src/services/arcgis-i3s-service.ts @@ -0,0 +1,31 @@ +// loaders.gl, MIT license + +import {Tile3DService} from '@loaders.gl/tiles'; +import {I3SLoader} from '../i3s-loader'; + +/** + * Layout guidelines for ArcGIS attribution. + * @see https://developers.arcgis.com/documentation/mapping-apis-and-services/deployment/basemap-attribution/ + * Custom layout guidelines for ArcGIS attribution. + * @see https://developers.arcgis.com/documentation/mapping-apis-and-services/deployment/basemap-attribution/#layout-and-design-guidelines + */ +export class ArcGISI3SService extends Tile3DService { + id = 'arcgis'; + name = 'ArcGIS'; + urlKey = 'arcgis'; + attribution = { + title: 'Powered by Esri.', + url: 'https://arcgis.com/', + height: 16 + }; + + loader = I3SLoader; + + async getMetadata(): Promise { + const response = await fetch(this.url); + if (!response.ok) { + throw new Error(`Failed to fetch ${this.name} ${response.status}`); + } + return {name: this.name, ...(await response.json())}; + } +} diff --git a/modules/tiles/src/index.ts b/modules/tiles/src/index.ts index 4991575555..febb0a865c 100644 --- a/modules/tiles/src/index.ts +++ b/modules/tiles/src/index.ts @@ -20,3 +20,6 @@ export { TILESET_TYPE, LOD_METRIC_TYPE } from './constants'; + +// EXPERIMENTAL EXPORTS +export {Tile3DService} from './services/tile3d-service'; diff --git a/modules/tiles/src/services/tile3d-service.ts b/modules/tiles/src/services/tile3d-service.ts new file mode 100644 index 0000000000..463fd6ad04 --- /dev/null +++ b/modules/tiles/src/services/tile3d-service.ts @@ -0,0 +1,59 @@ +// loaders.gl, MIT license + +import type {Loader} from '@loaders.gl/loader-utils'; + +export type Tile3DAttribution = { + title: string; + url: string; + logoUrl?: string; + height?: number; + bottom?: number; +}; + +/** + * Handles access token, metadata and attribution for a 3D tileset service. + */ +export abstract class Tile3DService { + static getServiceFromUrl(url: string, services: Tile3DService[]): Tile3DService | null { + return services.find((service) => url.includes(service.urlKey)) || null; + } + + /** id string */ + abstract readonly id: string; + /** Human readable name */ + abstract readonly name: string; + /** Identifies this service by matching a user supplied URL against this key */ + abstract readonly urlKey: string; + /** Default attribution per supported tileset provider. */ + abstract readonly attribution: Tile3DAttribution; + + /** Loader required by this particular service */ + abstract readonly loader: Loader; + + /** Base URL of this service */ + url: string; + /** Access token for this service */ + accessToken: string; + + /** + * + * @param url Base url to the service + * @param accessToken Access token for the service + */ + constructor(url: string, accessToken: string) { + this.url = url; + this.accessToken = accessToken; + } + + /** Queries metadata from a 3D tileset service. + * @param url Url of a 3D tileset. + * @param this.accessToken Optional access token. + * @returns Downloaded metadata. + */ + abstract getMetadata(): Promise; + + /** Test against map state - @todo outside of loaders.gl scope... */ + isSupported(mapState: any): boolean { + return true; + } +} diff --git a/modules/tiles/src/tile3d-service/tile3d-service.ts b/modules/tiles/src/tile3d-service/tile3d-service.ts deleted file mode 100644 index 05a4a975c1..0000000000 --- a/modules/tiles/src/tile3d-service/tile3d-service.ts +++ /dev/null @@ -1,142 +0,0 @@ -// loaders.gl, MIT license -import type {Loader} from '@loaders.gl/loader-utils'; -import {_getIonTilesetMetadata} from '@loaders.gl/3d-tiles'; - -// TODO - we can't import everything here, so mock for now -const Tiles3DLoader: Loader = {}; -const I3SLoader: Loader = {}; -const CesiumIONLoader: Loader = {}; - -export type Tile3DAttribution = { - title: string; - url: string; - logoUrl?: string; - height?: number; - bottom?: number; -}; - -abstract class Tile3DService { - static getServiceFromUrl(url: string, services: Tile3DService[]): Tile3DService | null { - return services.find((service) => url.includes(service.urlKey)) || null; - } - - abstract readonly id: string; - abstract readonly name: string; - abstract readonly urlKey: string; - /** Default attribution per supported tileset provider. */ - abstract readonly attribution: Tile3DAttribution; - - abstract readonly loader: Loader; - - url: string; - accessToken: string; - - constructor(url: string, accessToken: string) { - this.url = url; - this.accessToken = accessToken; - } - - /** Queries metadata from a 3D tileset service. - * @param url Url of a 3D tileset. - * @param this.accessToken Optional access token. - * @returns Downloaded metadata. - */ - abstract getMetadata(): Promise; - - isSupported(mapState: any): boolean { - return true; - } -} - -/** - * @see https://developers.google.com/maps/documentation/tile/policies - * attribution shouldn't be hidden (right now dataset attribution is only partly shown and expanded on hover). - * attribution should be visible (right now displayed with a grayish color, unnoticeable with Google 3d tiles) - * Google logo should be displayed on the bottom left side (where FSQ watermark is located). - */ -export class Google3DTilesService extends Tile3DService { - readonly id = 'google'; - readonly name = 'Google 3D Tiles'; - readonly urlKey = 'google'; - // Also, attribution for Google 3D tiles is collected from visible tiles. - readonly attribution = { - title: 'Built with Google Maps.', - url: '', - logoUrl: - 'https://developers.google.com/static/maps/documentation/images/google_on_non_white.png', - height: 16 - }; - - readonly loader = Tiles3DLoader; - - isSupported(mapState: any): boolean { - return mapState.globe?.enabled || mapState.zoom < 8; - } - - async getMetadata(): Promise { - if (!this.accessToken) { - throw new Error(`No access token for ${this.name}`); - } - const response = await fetch(`${url}?key=${this.accessToken}`); - if (!response.ok) { - throw new Error(`Failed to fetch ${this.name} ${response.status}`); - } - return {name: this.name, ...(await response.json())}; - } -} - -export class CesiumIONService extends Tile3DService { - readonly id = 'cesium'; - readonly name = 'Cesium ion'; - readonly urlKey = 'ion.cesium'; - readonly attribution = { - title: 'Cesium.', - url: 'https://cesium.com/', - height: 16, - bottom: -2 - }; - - readonly loader = CesiumIONLoader; - - async getMetadata(): Promise { - if (!this.accessToken) { - throw new Error(`No access token for ${this.name}`); - } - const matched = this.url.match(/\/([0-9]+)\/tileset.json/); - const assetId = matched && matched[1]; - const response = await _getIonTilesetMetadata(this.accessToken, assetId); - if (response.status !== 'COMPLETE') { - throw new Error(`Failed to fetch ${this.name} ${response.status}`); - } - return {name: this.name, ...response}; - } - - getUrl(tileUrl: string): string { - return `${tileUrl}?key=${this.accessToken}`; - } - - getLoadOptions() { - return {fetch: {headers: {'X-GOOG-API-KEY': this.accessToken}}}; - } -} - -export class ArcGISI3SService extends Tile3DService { - id = 'arcgis'; - name = 'ArcGIS'; - urlKey = 'arcgis'; - attribution = { - title: 'Powered by Esri.', - url: 'https://arcgis.com/', - height: 16 - }; - - loader = I3SLoader; - - async getMetadata(): Promise { - const response = await fetch(this.url); - if (!response.ok) { - throw new Error(`Failed to fetch ${this.name} ${response.status}`); - } - return {name: this.name, ...(await response.json())}; - } -} From 2ede2bed1ef6c62bc138609c8ff208507442da27 Mon Sep 17 00:00:00 2001 From: Ib Green Date: Sat, 9 Sep 2023 09:56:24 -0400 Subject: [PATCH 3/4] wip --- modules/3d-tiles/src/index.ts | 4 +- .../src/services/cesium-ion-service.ts | 117 +++++++++++++++--- .../src/services/google-3d-tiles-service.ts | 31 ++++- .../src/static/google_on_non_white.png | Bin 0 -> 9824 bytes modules/i3s/src/index.ts | 2 +- .../i3s/src/services/arcgis-i3s-service.ts | 7 +- ...{tile3d-service.ts => tiles-3d-service.ts} | 12 +- 7 files changed, 141 insertions(+), 32 deletions(-) create mode 100644 modules/3d-tiles/src/static/google_on_non_white.png rename modules/tiles/src/services/{tile3d-service.ts => tiles-3d-service.ts} (82%) diff --git a/modules/3d-tiles/src/index.ts b/modules/3d-tiles/src/index.ts index 4d0350f9c5..026c0e8f44 100644 --- a/modules/3d-tiles/src/index.ts +++ b/modules/3d-tiles/src/index.ts @@ -30,5 +30,5 @@ export type { export type {Tiles3DLoaderOptions} from './tiles-3d-loader'; // EXPERIMENTAL SERVICES -export {Google3DTilesService} from './services/google-3d-tiles-service'; -export {CesiumIONService} from './services/cesium-ion-service'; +export {Google3DTilesService as _Google3DTilesService} from './services/google-3d-tiles-service'; +export {CesiumIONService as _CesiumIONService} from './services/cesium-ion-service'; diff --git a/modules/3d-tiles/src/services/cesium-ion-service.ts b/modules/3d-tiles/src/services/cesium-ion-service.ts index b67bbb20d6..02c400deb7 100644 --- a/modules/3d-tiles/src/services/cesium-ion-service.ts +++ b/modules/3d-tiles/src/services/cesium-ion-service.ts @@ -1,8 +1,37 @@ // loaders.gl, MIT license import {Tile3DService} from '@loaders.gl/tiles'; -import {getIonTilesetMetadata} from '../lib/ion/ion'; import {CesiumIonLoader} from '../cesium-ion-loader'; +import {fetchFile} from '@loaders.gl/core'; + +const CESIUM_ION_URL = 'https://api.cesium.com/v1/assets'; + +export type IONAsset = { + type: '3DTILES' | 'GLTF' | 'IMAGERY' | 'TERRAIN' | 'KML' | 'GEOJSON'; + id: string; + name: string; + description: string; + attribution: string; +}; + +export type IONAssetMetadata = { + type: '3DTILES' | 'GLTF' | 'IMAGERY' | 'TERRAIN' | 'KML' | 'GEOJSON'; + + // Asset info + id: string; + name: string; + description: string; + attribution: string; + + // Endpoint info + url: string; + /** Resource specific access token valid for ~1 hour. Re-request metadata to refresh */ + accessToken: string; + attributions: { + html: string; + collapsible?: boolean; + }[]; +}; /** * Attribution for Cesium ion. @@ -14,32 +43,92 @@ export class CesiumIONService extends Tile3DService { readonly urlKey = 'ion.cesium'; readonly attribution = { title: 'Cesium.', - url: 'https://cesium.com/', - height: 16, - bottom: -2 + url: 'https://cesium.com/' }; /** @todo remove CesiumIONLoader, integrate into service? */ readonly loader = CesiumIonLoader; - async getMetadata(): Promise { + async getAssetCatalog(): Promise { + if (!this.accessToken) { + throw new Error(`No access token for ${this.name}`); + } + + const url = CESIUM_ION_URL; + const response = await fetchFile(url, {headers: {Authorization: `Bearer ${this.accessToken}`}}); + if (!response.ok) { + throw new Error(response.statusText); + } + const assetJson = await response.json(); + + const assets = assetJson.items; + // Remove any pending or errored assets + return assets.filter((asset) => asset.status === 'COMPLETE') as IONAsset[]; + } + + /** + * Retrieves metadata information about a specific ION asset. + * @param assetId + * @returns {url, headers, type, attributions} for an ion tileset + */ + async getAssetMetadata(assetId: string): Promise { if (!this.accessToken) { throw new Error(`No access token for ${this.name}`); } + + // Retrieves metadata information about a specific asset. + // @see https://cesium.com/docs/rest-api/#operation/getAsset + const url = `${CESIUM_ION_URL}/${assetId}`; + let response = await fetchFile(`${url}`, { + headers: {Authorization: `Bearer ${this.accessToken}`} + }); + if (!response.ok) { + throw new Error(response.statusText); + } + let metadata = await response.json(); + if (metadata.status !== 'COMPLETE') { + throw new Error(`Incomplete ION asset ${assetId}`); + } + + // Retrieves information and credentials that allow you to access the tiled asset data for visualization and analysis. + // https://cesium.com/docs/rest-api/#operation/getAssetEndpoint + response = await fetchFile(`${url}/endpoint`, { + headers: {Authorization: `Bearer ${this.accessToken}`} + }); + if (!response.ok) { + throw new Error(response.statusText); + } + const tilesetInfo = await response.json(); + + // extract dataset description + metadata = { + ...metadata, + ...tilesetInfo, + headers: { + Authorization: `Bearer ${tilesetInfo.accessToken}` + } + }; + + return metadata as IONAssetMetadata; + } + + async getMetadataForUrl(url: string): Promise { + const parsedUrl = this.parseUrl(url); + if (!parsedUrl) { + throw new Error(`Invalid url ${url}`); + } + const assetMetadata = await this.getAssetMetadata(parsedUrl.assetId); + // return {name: this.name, ...assetMetadata}; + return assetMetadata; + } + + parseUrl(url: string): {assetId: string; resource: string} | null { const matched = /\/([0-9]+)\/tileset.json/.exec(this.url); const assetId = matched && matched[1]; - const response = await getIonTilesetMetadata(this.accessToken, assetId); - if (response.status !== 'COMPLETE') { - throw new Error(`Failed to fetch ${this.name} ${response.status}`); - } - return {name: this.name, ...response}; + return assetId ? {assetId, resource: 'tileset.json'} : null; } getUrl(tileUrl: string): string { return `${tileUrl}?key=${this.accessToken}`; } - - getLoadOptions() { - return {fetch: {headers: {'X-GOOG-API-KEY': this.accessToken}}}; - } } diff --git a/modules/3d-tiles/src/services/google-3d-tiles-service.ts b/modules/3d-tiles/src/services/google-3d-tiles-service.ts index 070abcf90c..9db908adcc 100644 --- a/modules/3d-tiles/src/services/google-3d-tiles-service.ts +++ b/modules/3d-tiles/src/services/google-3d-tiles-service.ts @@ -3,6 +3,20 @@ import {Tile3DService} from '@loaders.gl/tiles'; import {Tiles3DLoader} from '../tiles-3d-loader'; +export type GoogleAsset = { + id: string; + url: string; + name: string; +}; + +const ASSET_CATALOG: GoogleAsset[] = [ + { + id: '1', + url: 'https://tile.googleapis.com/v1/3dtiles/root.json', + name: 'Google 3D Tiles' + } +]; + /** * @see https://developers.google.com/maps/documentation/tile/policies * attribution shouldn't be hidden (right now dataset attribution is only partly shown and expanded on hover). @@ -18,17 +32,21 @@ export class Google3DTilesService extends Tile3DService { title: 'Built with Google Maps.', url: '', logoUrl: - 'https://developers.google.com/static/maps/documentation/images/google_on_non_white.png', - height: 16 + 'https://developers.google.com/static/maps/documentation/images/google_on_non_white.png' }; readonly loader = Tiles3DLoader; + readonly minZoom = 8; + + getLoadOptions() { + return {fetch: {headers: {'X-GOOG-API-KEY': this.accessToken}}}; + } - isSupported(mapState: any): boolean { - return mapState.globe?.enabled || mapState.zoom < 8; + async getAssetCatalog(): Promise { + return ASSET_CATALOG; } - async getMetadata(): Promise { + async getAssetMetadata(assetId: string): Promise { if (!this.accessToken) { throw new Error(`No access token for ${this.name}`); } @@ -36,6 +54,7 @@ export class Google3DTilesService extends Tile3DService { if (!response.ok) { throw new Error(`Failed to fetch ${this.name} ${response.status}`); } - return {name: this.name, ...(await response.json())}; + const json = await response.json(); + return {name: this.name, ...json}; } } diff --git a/modules/3d-tiles/src/static/google_on_non_white.png b/modules/3d-tiles/src/static/google_on_non_white.png new file mode 100644 index 0000000000000000000000000000000000000000..f650e980012a5b7b11a76061eb33a20cae877307 GIT binary patch literal 9824 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 { + return []; + } + async getMetadata(): Promise { const response = await fetch(this.url); if (!response.ok) { diff --git a/modules/tiles/src/services/tile3d-service.ts b/modules/tiles/src/services/tiles-3d-service.ts similarity index 82% rename from modules/tiles/src/services/tile3d-service.ts rename to modules/tiles/src/services/tiles-3d-service.ts index 463fd6ad04..c1d9aaa7cf 100644 --- a/modules/tiles/src/services/tile3d-service.ts +++ b/modules/tiles/src/services/tiles-3d-service.ts @@ -2,19 +2,17 @@ import type {Loader} from '@loaders.gl/loader-utils'; -export type Tile3DAttribution = { +export type Tiles3DAttribution = { title: string; url: string; logoUrl?: string; - height?: number; - bottom?: number; }; /** * Handles access token, metadata and attribution for a 3D tileset service. */ -export abstract class Tile3DService { - static getServiceFromUrl(url: string, services: Tile3DService[]): Tile3DService | null { +export abstract class Tiles3DService { + static getServiceFromUrl(url: string, services: Tiles3DService[]): Tiles3DService | null { return services.find((service) => url.includes(service.urlKey)) || null; } @@ -25,7 +23,7 @@ export abstract class Tile3DService { /** Identifies this service by matching a user supplied URL against this key */ abstract readonly urlKey: string; /** Default attribution per supported tileset provider. */ - abstract readonly attribution: Tile3DAttribution; + abstract readonly attribution: Tiles3DAttribution; /** Loader required by this particular service */ abstract readonly loader: Loader; @@ -50,7 +48,7 @@ export abstract class Tile3DService { * @param this.accessToken Optional access token. * @returns Downloaded metadata. */ - abstract getMetadata(): Promise; + abstract getAssetCatalog(): Promise; /** Test against map state - @todo outside of loaders.gl scope... */ isSupported(mapState: any): boolean { From 39ba2d813ea8afb32c78259ac31c634a22075434 Mon Sep 17 00:00:00 2001 From: Ib Green Date: Sat, 9 Sep 2023 09:59:28 -0400 Subject: [PATCH 4/4] wip --- modules/3d-tiles/src/services/cesium-ion-service.ts | 4 ++-- modules/3d-tiles/src/services/google-3d-tiles-service.ts | 4 ++-- modules/i3s/src/services/arcgis-i3s-service.ts | 4 ++-- modules/tiles/src/index.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/3d-tiles/src/services/cesium-ion-service.ts b/modules/3d-tiles/src/services/cesium-ion-service.ts index 02c400deb7..bfe0f87ec4 100644 --- a/modules/3d-tiles/src/services/cesium-ion-service.ts +++ b/modules/3d-tiles/src/services/cesium-ion-service.ts @@ -1,6 +1,6 @@ // loaders.gl, MIT license -import {Tile3DService} from '@loaders.gl/tiles'; +import {Tiles3DService} from '@loaders.gl/tiles'; import {CesiumIonLoader} from '../cesium-ion-loader'; import {fetchFile} from '@loaders.gl/core'; @@ -37,7 +37,7 @@ export type IONAssetMetadata = { * Attribution for Cesium ion. * @see https://cesium.com/legal/terms-of-service/ */ -export class CesiumIONService extends Tile3DService { +export class CesiumIONService extends Tiles3DService { readonly id = 'cesium'; readonly name = 'Cesium ion'; readonly urlKey = 'ion.cesium'; diff --git a/modules/3d-tiles/src/services/google-3d-tiles-service.ts b/modules/3d-tiles/src/services/google-3d-tiles-service.ts index 9db908adcc..ef94d21d00 100644 --- a/modules/3d-tiles/src/services/google-3d-tiles-service.ts +++ b/modules/3d-tiles/src/services/google-3d-tiles-service.ts @@ -1,6 +1,6 @@ // loaders.gl, MIT license -import {Tile3DService} from '@loaders.gl/tiles'; +import {Tiles3DService} from '@loaders.gl/tiles'; import {Tiles3DLoader} from '../tiles-3d-loader'; export type GoogleAsset = { @@ -23,7 +23,7 @@ const ASSET_CATALOG: GoogleAsset[] = [ * attribution should be visible (right now displayed with a grayish color, unnoticeable with Google 3d tiles) * Google logo should be displayed on the bottom left side (where FSQ watermark is located). */ -export class Google3DTilesService extends Tile3DService { +export class Google3DTilesService extends Tiles3DService { readonly id = 'google'; readonly name = 'Google 3D Tiles'; readonly urlKey = 'google'; diff --git a/modules/i3s/src/services/arcgis-i3s-service.ts b/modules/i3s/src/services/arcgis-i3s-service.ts index c527981e9d..d8788db4cc 100644 --- a/modules/i3s/src/services/arcgis-i3s-service.ts +++ b/modules/i3s/src/services/arcgis-i3s-service.ts @@ -1,6 +1,6 @@ // loaders.gl, MIT license -import {Tile3DService} from '@loaders.gl/tiles'; +import {Tiles3DService} from '@loaders.gl/tiles'; import {I3SLoader} from '../i3s-loader'; /** @@ -9,7 +9,7 @@ import {I3SLoader} from '../i3s-loader'; * Custom layout guidelines for ArcGIS attribution. * @see https://developers.arcgis.com/documentation/mapping-apis-and-services/deployment/basemap-attribution/#layout-and-design-guidelines */ -export class ArcGISI3SService extends Tile3DService { +export class ArcGISI3SService extends Tiles3DService { id = 'arcgis'; name = 'ArcGIS'; urlKey = 'arcgis'; diff --git a/modules/tiles/src/index.ts b/modules/tiles/src/index.ts index febb0a865c..3faf184055 100644 --- a/modules/tiles/src/index.ts +++ b/modules/tiles/src/index.ts @@ -22,4 +22,4 @@ export { } from './constants'; // EXPERIMENTAL EXPORTS -export {Tile3DService} from './services/tile3d-service'; +export {Tiles3DService} from './services/tiles-3d-service';