Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tiles): 3DTile services #2635

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/3d-tiles/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ export type {
ImplicitTilingExensionData
} from './types';
export type {Tiles3DLoaderOptions} from './tiles-3d-loader';

// EXPERIMENTAL SERVICES
export {Google3DTilesService as _Google3DTilesService} from './services/google-3d-tiles-service';
export {CesiumIONService as _CesiumIONService} from './services/cesium-ion-service';
1 change: 1 addition & 0 deletions modules/3d-tiles/src/lib/ion/ion.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// loaders.gl, MIT license
// Minimal support to load tilsets from the Cesium ION services

import {fetchFile} from '@loaders.gl/core';
Expand Down
134 changes: 134 additions & 0 deletions modules/3d-tiles/src/services/cesium-ion-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// loaders.gl, MIT license

import {Tiles3DService} from '@loaders.gl/tiles';
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.
* @see https://cesium.com/legal/terms-of-service/
*/
export class CesiumIONService extends Tiles3DService {
readonly id = 'cesium';
readonly name = 'Cesium ion';
readonly urlKey = 'ion.cesium';
readonly attribution = {
title: 'Cesium.',
url: 'https://cesium.com/'
};

/** @todo remove CesiumIONLoader, integrate into service? */
readonly loader = CesiumIonLoader;

async getAssetCatalog(): Promise<IONAsset[]> {
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<IONAssetMetadata> {
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<any> {
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];
return assetId ? {assetId, resource: 'tileset.json'} : null;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need getLoadOptions here?
loadOptions: {'cesium-ion': {accessToken, worker: true}}

getUrl(tileUrl: string): string {
return `${tileUrl}?key=${this.accessToken}`;
}
}
60 changes: 60 additions & 0 deletions modules/3d-tiles/src/services/google-3d-tiles-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// loaders.gl, MIT license

import {Tiles3DService} 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).
* 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 Tiles3DService {
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'
};

readonly loader = Tiles3DLoader;
readonly minZoom = 8;

getLoadOptions() {
return {fetch: {headers: {'X-GOOG-API-KEY': this.accessToken}}};
}

async getAssetCatalog(): Promise<GoogleAsset[]> {
return ASSET_CATALOG;
}

async getAssetMetadata(assetId: string): Promise<any> {
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}`);
}
const json = await response.json();
return {name: this.name, ...json};
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions modules/i3s/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 as _ArcGISI3SService} from './services/arcgis-i3s-service';
34 changes: 34 additions & 0 deletions modules/i3s/src/services/arcgis-i3s-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// loaders.gl, MIT license

import {Tiles3DService} 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 Tiles3DService {
id = 'arcgis';
name = 'ArcGIS';
urlKey = 'arcgis';
attribution = {
title: 'Powered by Esri.',
url: 'https://arcgis.com/'
};

loader = I3SLoader;

async getAssetCatalog(): Promise<unknown[]> {
return [];
}

async getMetadata(): Promise<any> {
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())};
}
}
3 changes: 3 additions & 0 deletions modules/tiles/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ export {
TILESET_TYPE,
LOD_METRIC_TYPE
} from './constants';

// EXPERIMENTAL EXPORTS
export {Tiles3DService} from './services/tiles-3d-service';
57 changes: 57 additions & 0 deletions modules/tiles/src/services/tiles-3d-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// loaders.gl, MIT license

import type {Loader} from '@loaders.gl/loader-utils';

export type Tiles3DAttribution = {
title: string;
url: string;
logoUrl?: string;
};

/**
* Handles access token, metadata and attribution for a 3D tileset service.
*/
export abstract class Tiles3DService {
static getServiceFromUrl(url: string, services: Tiles3DService[]): Tiles3DService | 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: Tiles3DAttribution;

/** 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 getAssetCatalog(): Promise<unknown[]>;

/** Test against map state - @todo outside of loaders.gl scope... */
isSupported(mapState: any): boolean {
return true;
}
}
Loading