UNPKG

terriajs-cesium

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

342 lines (305 loc) 11.2 kB
import DeveloperError from "../Core/DeveloperError.js"; import defined from "../Core/defined.js"; import Resource from "../Core/Resource.js"; import Cartesian3 from "../Core/Cartesian3.js"; import Cartographic from "../Core/Cartographic.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import CesiumMath from "../Core/Math.js"; import Transforms from "../Core/Transforms.js"; import Frozen from "../Core/Frozen.js"; import Ellipsoid from "../Core/Ellipsoid.js"; import GoogleMaps from "../Core/GoogleMaps.js"; import CubeMapPanorama from "./CubeMapPanorama.js"; const DEFAULT_TILE_SIZE = 600; /** * <div class="notice"> * This object is normally not instantiated directly, use {@link GoogleStreetViewCubeMapPanoramaProvider.fromUrl}. * </div> * * * Creates a {@link PanoramaProvider} which provides imagery from {@link https://developers.google.com/maps/documentation/streetview|Google Street View Static API} to be displayed in a panorama. * * @alias GoogleStreetViewCubeMapPanoramaProvider * @constructor * * @see CubeMapPanorama * * @demo {@link https://sandcastle.cesium.com/index.html?id=google-streetview-panorama|Cesium Sandcastle Google Streetview Panorama} * */ function GoogleStreetViewCubeMapPanoramaProvider(options) { options = options ?? Frozen.EMPTY_OBJECT; this._key = defined(options.key) ? options.key : GoogleMaps.defaultApiKey; this._baseResource = Resource.createIfNeeded( options.url ?? GoogleMaps.streetViewStaticApiEndpoint, ); this._metadataResource = this._baseResource.getDerivedResource({ url: "streetview/metadata", }); this._tileSize = options.tileSize ?? DEFAULT_TILE_SIZE; } Object.defineProperties(GoogleStreetViewCubeMapPanoramaProvider.prototype, {}); /** * Gets the panorama primitive for a requested position and orientation. * @param {object} options Object with the following properties: * @param {Cartographic} options.cartographic The position to place the panorama in the scene. * @param {string} [options.panoId] The panoramaId identifier for the image in the Google API. If not provided this will be looked up for the provided cartographic location. * @param {number} [options.tileSize] - Optional tile size override (square). * @param {string} [options.signature] - Optional signature for signed URLs. See {@link https://developers.google.com/maps/documentation/streetview/digital-signature} for more information. * @param {Credit|string} [options.credit] A credit for the data source, which is displayed on the canvas. * * @returns {CubeMapPanorama} The panorama primitive textured with imagery. * * @example * * const provider = await Cesium.GoogleStreetViewCubeMapPanoramaProvider.fromUrl({ * key: 'your Google Streetview Static api key' * }) * * const panoIdObject = provider.getNearestPanoId(position); * const position = Cartographic.fromDegrees(panoIdObject.location.lng, panoIdObject.location.lat, 0); * * const primitive = await provider.loadPanorama({ * cartographic: position, * panoId: panoIdObject.panoId * }); * viewer.scene.primitive.add(primitive); * */ GoogleStreetViewCubeMapPanoramaProvider.prototype.loadPanorama = async function (options) { const cartographic = options.cartographic; //>>includeStart('debug', pragmas.debug); if (!defined(options.cartographic)) { throw new DeveloperError("options.cartographic is required."); } //>>includeEnd('debug'); const tileSize = options.tileSize ?? this._tileSize; const tileSizeString = `${tileSize}x${tileSize}`; const signature = options.signature; let { panoId } = options; if (!defined(panoId)) { const panoIdObject = await this.getNearestPanoId(cartographic); panoId = panoIdObject.panoId; } const credit = GoogleMaps.getDefaultCredit(); const posObj = Cartesian3.fromDegrees( CesiumMath.toDegrees(cartographic.longitude), CesiumMath.toDegrees(cartographic.latitude), 0, ); const facePromises = [ this._loadFaceImage({ heading: 0, pitch: 0, tileSizeString, panoId, signature, }), this._loadFaceImage({ heading: 180, pitch: 0, tileSizeString, panoId, signature, }), this._loadFaceImage({ heading: -90, pitch: -90, tileSizeString, panoId, signature, }), this._loadFaceImage({ heading: -90, pitch: 90, tileSizeString, panoId, signature, }), this._loadFaceImage({ heading: 270, pitch: 0, tileSizeString, panoId, signature, }), this._loadFaceImage({ heading: 90, pitch: 0, tileSizeString, panoId, signature, }), ]; const [positiveX, negativeX, positiveY, negativeY, positiveZ, negativeZ] = await Promise.all(facePromises); const northDownEastToFixedFrameTransform = Transforms.localFrameToFixedFrameGenerator("north", "down"); const transform = Matrix4.getMatrix3( northDownEastToFixedFrameTransform(posObj, Ellipsoid.default), new Matrix3(), ); const panorama = new CubeMapPanorama({ sources: { positiveX, negativeX, positiveY, negativeY, positiveZ, negativeZ, }, transform: transform, credit, }); return panorama; }; /** * Gets the panoIds for the given cartographic location. See {@link https://developers.google.com/maps/documentation/tile/streetview#panoid-search}. * @param {Cartographic} position The position to search for the nearest panoId. * @param {number} [radius=50] The radius in meters to search for the nearest panoId. * * @returns {Object} an object containing a panoId, latitude, and longitude of the closest panorama * * @example * * const provider = await Cesium.GoogleStreetViewCubeMapPanoramaProvider.fromUrl({ * key: 'your Google Streetview Static api key' * }) * const panoIds = provider.getNearestPanoId(position); */ GoogleStreetViewCubeMapPanoramaProvider.prototype.getNearestPanoId = async function (position, radius) { //>>includeStart('debug', pragmas.debug); if (!defined(position)) { throw new DeveloperError("position is required."); } //>>includeEnd('debug'); const pos = [ CesiumMath.toDegrees(position.latitude), CesiumMath.toDegrees(position.longitude), ]; const posString = pos.join(","); const resource = this._metadataResource.getDerivedResource({ queryParameters: { key: this._key, location: posString, radius: radius ?? 50, }, }); const panoIdObject = await resource.fetchJson(); if (panoIdObject.status !== "OK") { throw new DeveloperError( `GoogleStreetViewCubeMapPanoramaProvider metadata error: ${panoIdObject.status}`, ); } return { panoId: panoIdObject.pano_id, latitude: panoIdObject.location.lat, longitude: panoIdObject.location.lng, }; }; /** * Gets metadata for panoId. See {@link https://developers.google.com/maps/documentation/tile/streetview#metadata_response} for response object. * @param {string} panoId * * @returns {object} object containing metadata for the panoId. * * @example * const panoIdObject = provider.getNearestPanoId(position); * const panoIdMetadata = provider.getPanoIdMetadata(panoIdObject.panoId); * */ GoogleStreetViewCubeMapPanoramaProvider.prototype.getPanoIdMetadata = async function (panoId) { const resource = this._metadataResource.getDerivedResource({ queryParameters: { key: this._key, pano: panoId, }, }); const panoIdObject = await resource.fetchJson(); if (panoIdObject.status !== "OK") { throw new DeveloperError( `GoogleStreetViewCubeMapPanoramaProvider metadata error: ${panoIdObject.status}`, ); } return panoIdObject; }; GoogleStreetViewCubeMapPanoramaProvider.prototype._loadFaceImage = async function (options) { const url = this._buildFaceUrl(options); try { return await Resource.fetchImage({ url, preferImageBitmap: true, flipY: true, }); } catch (error) { throw new DeveloperError(`Failed to load Street View face image: ${url}`); } }; GoogleStreetViewCubeMapPanoramaProvider.prototype._buildFaceUrl = function ( options, ) { const { heading, pitch, tileSizeString, panoId, signature } = options; const resource = this._baseResource.getDerivedResource({ queryParameters: { size: tileSizeString, pano: panoId, heading, pitch, key: this._key, ...(defined(signature) && { signature }), }, }); return resource.url; }; /** * Creates a {@link PanoramaProvider} which provides cube face images from the {@link https://developers.google.com/maps/documentation/streetview|Google Street View Static API}. * @param {object} options Object with the following properties: * @param {string} [options.key=GoogleMaps.defaultStreetViewStaticApiKey] Your API key to access Google Street View Static API. See {@link https://developers.google.com/maps/documentation/javascript/get-api-key} for instructions on how to create your own key. If undefined, defaults to {@link GoogleMaps.defaultStreetViewStaticApiKey}. If that value is unavailable, falls back to {@link GoogleMaps.defaultApiKey}. * @param {string|Resource} [options.url=GoogleMaps.streetViewStaticApiEndpoint] The URL to access Google Street View Static API. See {@link https://developers.google.com/maps/documentation/streetview/overview} for more information. * @param {number} [options.tileSize=600] Default width and height (in pixels) of each square tile. * @param {Credit|string} [options.credit] A credit for the data source, which is displayed on the canvas. * * @returns {Promise<GoogleStreetViewCubeMapPanoramaProvider>} A promise that resolves to the created GoogleStreetViewCubeMapPanoramaProvider.' * * @example * const provider = await Cesium.GoogleStreetViewCubeMapPanoramaProvider.fromUrl({ * key: 'your Google Streetview Static api key' * }) */ GoogleStreetViewCubeMapPanoramaProvider.fromUrl = async function (options) { options = options ?? {}; options.key = options.key ?? GoogleMaps.defaultStreetViewStaticApiKey ?? GoogleMaps.defaultApiKey; if ( !defined(options.key) && !defined(GoogleMaps.defaultStreetViewStaticApiKey) && !defined(GoogleMaps.defaultApiKey) ) { throw new DeveloperError( "options.key, GoogleMaps.defaultStreetViewStaticApiKey or GoogleMaps.defaultApiKey is required.", ); } return new GoogleStreetViewCubeMapPanoramaProvider({ ...options, }); }; GoogleStreetViewCubeMapPanoramaProvider._parseMetadata = function ( panoIdMetadata, ) { const cartographic = Cartographic.fromDegrees( panoIdMetadata.location.lng, panoIdMetadata.location.lat, 0, ); return { cartographic, }; }; export default GoogleStreetViewCubeMapPanoramaProvider;