import HTTP from "../HTTP"; import { Dataset, Dataset3D, PointCloudDataset, Scene } from "./CloudApiModel"; import Resource from "../Resource"; import SceneLoader from "../SceneLoader"; import { DatasetResource, Dataset3DSceneResource, PointCloudDatasetResource, SceneResource } from "./CloudApiResource"; import GeoJSON from "../GeoJSON"; import { cfa_assert } from "../util/assertion"; /** * Mapray Cloudへアクセスするためのクラスです。 * * このクラスを利用するには事前にMapray Cloudアカウントを作成する必要があります。 * [https://cloud.mapray.com](https://cloud.mapray.com/) からサインアップすることができます。 * * 事前に下記の情報を調べておきます。 * * - User ID:
* Mapray Cloudの[ユーザー情報ページ](https://cloud.mapray.com/settings)から確認します。 * 右上メニューのAccountメニューからこのページを開くことができます。 * * - Token:
* Mapray Cloudの[Tokenページ](https://cloud.mapray.com/dashboard)でトークンを作成します。 * 上部のTokensタブからこのページを開くことができます。 * * - データセット等のID:
* Mapray Cloudへデータをアップロードし、そのデータを使用するには、[データセットページ](https://cloud.mapray.com/datasetslist) * からGeoJsonやglTFデータをアップロードしておき、アップロードしたデータのIDを確認します。 * * MaprayCloudバージョンごとに具象クラスが定義されています。 * 利用するバージョンのクラスを利用します。 * * | Version | Class | * |---------|-----------------| * | v1 | [mapray.cloud.CloudApiV1](mapray.cloud.CloudApiV1-1.html) | * | v2 | [mapray.cloud.CloudApiV2](mapray.cloud.CloudApiV2-1.html) | * * MaprayCloudへアクセスする関数は下記のように分類されます。 * * - `get***AsResource()`:
* Maprayの各種ローダは、Resourceクラスを受け取るようになっています。[[mapray.GeoJSONLoader]] * * - `load***()`:
* 適切なクラスのインスタンスを返却します。 * * - `get***()`:
* 最も低レベルのAPI呼び出しを行う。返却値はJSONです。 */ abstract class CloudApi { public readonly version: string; public readonly basePath: string; private _header_key: string; private _header_value: string; /** * @param version * @param basePath * @param header_key header key for cloud api * @param header_value header value for cloud api */ constructor( version: string, basePath: string | undefined, header_key: string, header_value:string ) { this.version = version; this.basePath = !basePath ? DEFAULT_BASE_PATH: basePath.endsWith("/") ? basePath.slice(0, -1): basePath; this._header_key = header_key; this._header_value = header_value; } // Dataset, 3DDataset, PointCloudDataset /** * データセットのリストを取得します。 * ページごとにデータセットリストを取得します。 * * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return データセットの配列 */ async loadDatasets( page: number = 1, limit: number = 5 ): Promise { const datasets_json = await this.getDatasets( page, limit ); return datasets_json.map( (dataset_json: Dataset.Json) => Dataset.createFromJson( this, dataset_json ) ); } /** * 指定したIDのデータセットを取得します。 * @param datasetId データセットのID * @return データセット */ async loadDataset( datasetId: string ): Promise { const dataset_json = await this.getDataset( datasetId ); return Dataset.createFromJson( this, dataset_json ); } /** * 3Dデータセットのリストを取得します。 * ページごとにデータセットリストを取得します。 * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return 3Dデータセットの配列 */ async load3DDatasets( page: number = 1, limit: number = 5 ): Promise { const datasets_json = await this.get3DDatasets( page, limit ); return datasets_json.map( (dataset_json: Dataset3D.Json) => Dataset3D.createFromJson( this, dataset_json ) ); } /** * 指定したIDの3Dデータセットを取得します。 * @param datasetId * @return 3Dデータセット */ async load3DDataset( datasetId: string ): Promise { const dataset_json = await this.get3DDataset( datasetId ); return Dataset3D.createFromJson( this, dataset_json ) } /** * 点群データセットのリストを取得します。 * ページごとにデータセットリストを取得します。 * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return 点群データセットの配列 */ async loadPointCloudDatasets( page: number = 1, limit: number = 5 ) { const datasets_json = await this.getPointCloudDatasets( page, limit ); return datasets_json.map( (dataset_json: PointCloudDataset.Json) => PointCloudDataset.createFromJson( this, dataset_json ) ); } /** * 指定したIDの点群データセットを取得します。 * @param datasetId データセットID * @return 点群データセット */ async loadPointCloudDataset( datasetId: string ) { const dataset_json = await this.getPointCloudDataset( datasetId ); return PointCloudDataset.createFromJson( this, dataset_json ); } /** * シーンのリストを取得します。 * ページごとにシーンリストを取得します。 * * *CloudApiV2でのみ対応しています。 * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return シーンの配列 */ async loadScenes( page: number = 1, limit: number = 5 ): Promise { const scenes_json = await this.getScenes( page, limit ); return scenes_json.map( (scene_json: Scene.Json) => Scene.createFromJson( this, scene_json ) ); } /** * 指定したIDのシーンを取得します。 * * *CloudApiV2でのみ対応しています。 * @param sceneId シーンのID * @return シーン */ async loadScene( sceneId: string ): Promise { const scene_json = await this.getScene( sceneId ); return Scene.createFromJson( this, scene_json ); } // Resources /** * 指定したIDのデータセットをリソースとして取得します。 * @param datasetId データセットID * @return データセットのリソース */ getDatasetAsResource( datasetId: string ): Resource { return new DatasetResource( this, datasetId ); } /** * 指定したIDの3Dデータセットのシーンファイルをリソースとして取得します。 * @param datasetId データセットIDのリスト * @return 3Dデータセットのリソース */ get3DDatasetAsResource( datasetIds: string[] ): Resource { return new Dataset3DSceneResource( this, datasetIds ); } /** * 指定したIDの点群データセットの定義ファイルをリソースとして取得します。 * @param datasetId データセットID * @return 点群データのリソース */ getPointCloudDatasetAsResource( datasetId: string ): Resource { return new PointCloudDatasetResource( this, datasetId ); } /** * 指定したIDのシーンのシーンファイルをリソースとして取得します。 * * *CloudApiV2でのみ対応しています。 * @param sceneId シーンID * @return シーンのリソース */ getSceneAsResource( sceneId: string ): Resource { return new SceneResource( this, sceneId ); } // RestAPI /** * データセットリストを取得します * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return json[] */ abstract getDatasets( page: number, limit: number ): Promise; /** * 登録されているデータセットの数を取得します * @returns json */ abstract countDatasets(): Promise; /** * get dataset * @param datasetId * @return json */ abstract getDataset( datasetId: string ): Promise; /** * @internal * データセットを作成します。 * @param name 名前 * @param description 説明 * @return json */ abstract createDataset( name: string, description: string ): Promise; /** * @internal * データセットを削除します。 */ abstract deleteDataset( datasetId: string/*, option={ wait: true }*/ ): Promise; /** * GeoJSONの内容を取得します。 * @param datasetId データセットID * @return json */ abstract getFeatures( datasetId: string ): Promise; /** * @internal * GeoJSON要素をアップロード(挿入)します。 * @param datasetId データセットID * @return json */ abstract insertFeature( datasetId: string, feature: CloudApi.FeatureRequestJson ): Promise; /** * @internal * GeoJSON要素を更新(上書き)します。 * @param featureId GeoJSON要素ID * @param feature GeoJSON要素 * @return json */ abstract updateFeature( featureId: string, feature: GeoJSON.FeatureJson ): Promise; /** * @internal * GeoJSON要素を作事をします。 * @param datasetId データセットID * @param featureId GeoJSON要素ID * @return json */ abstract deleteFeature( featureId: string ): Promise; /** * 3Dデータセットのリストを取得します。 * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return json */ abstract get3DDatasets( page: number, limit: number ): Promise; /** * 登録されている3Dデータセットの数を取得します * @returns json */ abstract count3DDatasets(): Promise; /** * @internal * 3D datastを作成します。 * @param name 名前 * @param description 説明 * @param option * @return json */ abstract create3DDataset( name: string, description: string, option: Dataset3D.Json ): Promise; /** * @internal * 3Dデータセットを更新します。 * @param datasetId データセットId * @param name 名前 * @param description 説明 * @return json */ abstract update3DDataset( datasetId: string, name: string, description: string, option: Dataset3D.RequestJson ): Promise; /** * @internal * 3Dデータセットアップロード用URLを取得します。 * @param datasetId データセットId * @return json */ abstract create3DDatasetUploadUrl( datasetId: string, fileInfo: Dataset3D.UploadFileInfo[] ): Promise; /** * @internal * 3DデータセットのConvertを開始します。 * @param datasetId データセットId * @return json */ abstract convert3DDataset( datasetId: string ): Promise; /** * @internal * 3DデータセットのConvertStatusを取得します。 * @param datasetId データセットId * @return json */ abstract retrieve3DDatasetConvertStatus( datasetId: string ): Promise; /** * @internal * 3Dデータセット情報を取得します。 * データセットが保持するデータにアクセスするには、get3DDatasetScene()を利用します。 * @param datasetId データセットId * @return json */ abstract get3DDataset( datasetId: string ): Promise; /** * @internal * 3Dデータセットを削除します。 * @param datasetId データセットId * @return json */ abstract delete3DDataset( datasetId: string ): Promise; /** * 3Dデータセットに含まれる scene情報 を取得します。 * @param datasetIds * @return シーンファイルの実体 */ abstract get3DDatasetScene( datasetIds: string | string[] ): Promise; /** * 点群データセットリストを取得します。 * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return json */ abstract getPointCloudDatasets( page: number, limit: number ): Promise; /** * 登録されている点群データセットの数を取得します * @returns json */ abstract countPointCloudDatasets(): Promise; /** * 点群データセットを取得します。 * @param datasetId データセットId * @return json */ abstract getPointCloudDataset( datasetId: string ): Promise; /** * シーンリストを取得します。 * * *CloudApiV2でのみ対応しています。 * @param page 取得する要素のページ番号 * @param limit 1ページに含まれる要素数。最大100まで指定することができます。 * @return json */ abstract getScenes( page: number, limit: number ): Promise; /** * シーンを取得します。 * * *CloudApiV2でのみ対応しています。 * @param sceneId シーンId * @return json */ abstract getScene( sceneId: string ): Promise; /** * シーンファイルを取得します。 * * *CloudApiV2でのみ対応しています。 * @param sceneId シーンId * @return json */ abstract getSceneContent( sceneId: string ): Promise /** * 低レベルAPI。このクラスの別関数から呼び出される。 * @return json */ protected async get( api: string, args: string[], query?: HTTP.Query, option={} ): Promise { return await this.fetchAPI( HTTP.METHOD.GET, api, args, query, undefined, option ); } /** * 低レベルAPI。このクラスの別関数から呼び出される。 * @return json */ protected async post( api: string, args: string[], query?: HTTP.Query, body?: HTTP.Body, option={} ): Promise { if ( typeof( body ) !== "string" ) { body = JSON.stringify(body); } return await this.fetchAPI( HTTP.METHOD.POST, api, args, query, body, option ); } /** * 低レベルAPI。このクラスの別関数から呼び出される。 * @return json */ protected async patch( api: string, args: string[], query?: HTTP.Query, body?: HTTP.Body, option={} ): Promise { if ( typeof( body ) !== "string" ) { body = JSON.stringify(body); } return await this.fetchAPI( HTTP.METHOD.PATCH, api, args, query, body, option ); } /** * 低レベルAPI。このクラスの別関数から呼び出される。 * @return json */ protected async put( api: string, args: string[], query?: HTTP.Query, body?: HTTP.Body, option={} ): Promise { if ( typeof( body ) !== "string" ) { body = JSON.stringify(body); } return await this.fetchAPI( HTTP.METHOD.PUT, api, args, query, body, option ); } /** * 低レベルAPI。このクラスの別関数から呼び出される。 * @return json */ protected async delete( api: string, args: string[], query?: HTTP.Query, option={} ): Promise { return await this.fetchAPI( HTTP.METHOD.DELETE, api, args, query, undefined, option ); } /** * 低レベルAPI。このクラスの別関数から呼び出される。 * @return json */ protected async fetchAPI( method: string, api: string, args: string[], query?: HTTP.Query, body?: HTTP.Body, option={} ): Promise { const url = this.basePath + "/" + api + "/" + this.version + (args.length > 0 ? "/" + args.join("/") : ""); // console.log( "CloudApi: " + method + " " + api + " (" + args.join("/") + ")" ); const response = await this.fetch( method, url, query, body, option ); return await response.json() as T; } /** * 低レベルAPI。このクラスの別関数から呼び出される。 * @return json */ protected async fetch( method: string, url: string, query?: HTTP.Query, body?: HTTP.Body, option: RequestInit = {} ): Promise { const headers = option.headers || (option.headers = {}); // @ts-ignore headers[this._header_key] = this._header_value; let response; try { response = await HTTP.fetch( method, url, query, body, option ); } catch( error: any ) { cfa_assert( error instanceof Error ); if ( error instanceof HTTP.FetchError && error.response ) { let errorResponseJson; try { errorResponseJson = await error.response.json(); } catch( additionalError ) { // Couldn't get additional info of the error. // throw original error. throw new CloudApiError( -1, "Failed to fetch", url, undefined, error ); } throw new CloudApiError( errorResponseJson.code, errorResponseJson.error, url, error.response, error ); } else { throw new CloudApiError( -1, "Failed to fetch", url, undefined, error ); } } return response; } } namespace CloudApi { export interface FeatureRequestJson { } export interface LoadDatasetsJson { } export enum TokenType { /** api key */ API_KEY = "@@_CloudApi.TokenType.API_KEY", /** access token */ ACCESS_TOKEN = "@@_CloudApi.TokenType.ACCESS_TOKEN", } } // namespace CloudApi /** * CloudApiへのアクセスに関わるエラー */ class CloudApiError extends HTTP.FetchError { /** エラーコード */ code: number; /** エラー名 */ name: string; /** レスポンスオブジェクト */ response?: Response; /** エラーの原因となったエラー */ cause?: Error; constructor( code: number, message: string, url: string, response?: Response, cause?: Error ) { super( message + " [" + code + "]", url ); if ( Error.captureStackTrace ) { Error.captureStackTrace( this, CloudApiError ); } this.name = "CloudApiError"; this.code = code; this.response = response; this.cause = cause; if ( cause ) { this.stack += "\nCaused-By: " + cause.stack; } } } const DEFAULT_BASE_PATH = "https://cloud.mapray.com"; export default CloudApi;