diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html
new file mode 100644
index 000000000000..40f008469ef3
--- /dev/null
+++ b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html
@@ -0,0 +1,1351 @@
+
+
+
+
+
+
+
+
+ Cesium Demo
+
+
+
+
+
+
+
+
+
+
Loading...
+
+
+
+
+
diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.jpg b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.jpg
new file mode 100644
index 000000000000..5b2daea52ce0
Binary files /dev/null and b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.jpg differ
diff --git a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html
index 4e636b2f001c..41f67a60a1b6 100644
--- a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html
+++ b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html
@@ -78,6 +78,7 @@ Loading...
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
+ geocoder: Cesium.IonGeocodeProviderType.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
@@ -104,7 +105,10 @@ Loading...
// Add Photorealistic 3D Tiles
try {
- const tileset = await Cesium.createGooglePhotorealistic3DTileset();
+ const tileset = await Cesium.createGooglePhotorealistic3DTileset({
+ // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE.
+ onlyUsingWithGoogleGeocoder: true,
+ });
scene.primitives.add(tileset);
} catch (error) {
console.log(`Error loading Photorealistic 3D Tiles tileset.
diff --git a/Apps/Sandcastle/gallery/AEC Clipping.html b/Apps/Sandcastle/gallery/AEC Clipping.html
index 0fa60bf14964..d373bd2a589a 100644
--- a/Apps/Sandcastle/gallery/AEC Clipping.html
+++ b/Apps/Sandcastle/gallery/AEC Clipping.html
@@ -32,6 +32,7 @@
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
+ geocoder: Cesium.IonGeocodeProviderType.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
@@ -48,7 +49,10 @@
// Add Photorealistic 3D Tiles
let googleTileset;
try {
- googleTileset = await Cesium.createGooglePhotorealistic3DTileset();
+ googleTileset = await Cesium.createGooglePhotorealistic3DTileset({
+ // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE.
+ onlyUsingWithGoogleGeocoder: true,
+ });
viewer.scene.primitives.add(googleTileset);
} catch (error) {
console.log(`Error loading Photorealistic 3D Tiles tileset.
diff --git a/Apps/Sandcastle/gallery/Bing Maps Labels Only.html b/Apps/Sandcastle/gallery/Bing Maps Labels Only.html
index f4c12739f24f..7f92188196ba 100644
--- a/Apps/Sandcastle/gallery/Bing Maps Labels Only.html
+++ b/Apps/Sandcastle/gallery/Bing Maps Labels Only.html
@@ -67,6 +67,7 @@
baseLayer: false,
baseLayerPicker: false,
infoBox: false,
+ geocoder: Cesium.IonGeocodeProviderType.BING,
});
const layers = viewer.imageryLayers;
diff --git a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html
index 0a048fbe0d93..e38e8709c464 100644
--- a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html
+++ b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html
@@ -41,6 +41,7 @@
timeline: false,
animation: false,
baseLayerPicker: false,
+ geocoder: Cesium.IonGeocodeProviderType.GOOGLE,
});
const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true;
@@ -56,7 +57,10 @@
let worldTileset;
try {
- worldTileset = await Cesium.createGooglePhotorealistic3DTileset();
+ worldTileset = await Cesium.createGooglePhotorealistic3DTileset({
+ // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE.
+ onlyUsingWithGoogleGeocoder: true,
+ });
viewer.scene.primitives.add(worldTileset);
} catch (error) {
console.log(`Error loading Photorealistic 3D Tiles tileset.
diff --git a/Apps/Sandcastle/gallery/Clipping Regions.html b/Apps/Sandcastle/gallery/Clipping Regions.html
index 97dba26b3ce9..b7116fc29f17 100644
--- a/Apps/Sandcastle/gallery/Clipping Regions.html
+++ b/Apps/Sandcastle/gallery/Clipping Regions.html
@@ -43,6 +43,7 @@
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
+ geocoder: Cesium.IonGeocoderProviderType.GOOGLE,
});
const scene = viewer.scene;
@@ -60,7 +61,10 @@
let worldTileset;
try {
- worldTileset = await Cesium.createGooglePhotorealistic3DTileset();
+ worldTileset = await Cesium.createGooglePhotorealistic3DTileset({
+ // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE.
+ onlyUsingWithGoogleGeocoder: true,
+ });
scene.primitives.add(worldTileset);
} catch (error) {
console.log(`Error loading Photorealistic 3D Tiles tileset.
diff --git a/Apps/Sandcastle/gallery/Entity tracking.html b/Apps/Sandcastle/gallery/Entity tracking.html
index f5f21aa22a47..6e894c642e9d 100644
--- a/Apps/Sandcastle/gallery/Entity tracking.html
+++ b/Apps/Sandcastle/gallery/Entity tracking.html
@@ -94,6 +94,13 @@
drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.VELOCITY;
},
},
+ {
+ text: "Tracking reference frame: East-North-Up",
+ onselect: function () {
+ satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.ENU;
+ drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.ENU;
+ },
+ },
]);
//Sandcastle_End
};
diff --git a/Apps/Sandcastle/gallery/Globe Interior.html b/Apps/Sandcastle/gallery/Globe Interior.html
index 0deb07a51e34..9a4ff467d784 100644
--- a/Apps/Sandcastle/gallery/Globe Interior.html
+++ b/Apps/Sandcastle/gallery/Globe Interior.html
@@ -34,6 +34,7 @@
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
orderIndependentTranslucency: false,
+ geocoder: Cesium.IonGeocodeProviderType.BING,
});
const scene = viewer.scene;
diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html
index 6437a2fb7c19..a75c993a8afa 100644
--- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html
+++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html
@@ -32,6 +32,7 @@
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
+ geocoder: Cesium.IonGeocodeProviderType.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
@@ -42,7 +43,10 @@
// Add Photorealistic 3D Tiles
try {
- const googleTileset = await Cesium.createGooglePhotorealistic3DTileset();
+ const googleTileset = await Cesium.createGooglePhotorealistic3DTileset({
+ // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE.
+ onlyUsingWithGoogleGeocoder: true,
+ });
viewer.scene.primitives.add(googleTileset);
} catch (error) {
console.log(`Error loading Photorealistic 3D Tiles tileset.
diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html
index d4d68c1c97d6..2f9da653dc9c 100644
--- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html
+++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html
@@ -32,6 +32,7 @@
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
+ geocoder: Cesium.IonGeocodeProviderType.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
@@ -42,7 +43,10 @@
// Add Photorealistic 3D Tiles
try {
- const tileset = await Cesium.createGooglePhotorealistic3DTileset();
+ const tileset = await Cesium.createGooglePhotorealistic3DTileset({
+ // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE.
+ onlyUsingWithGoogleGeocoder: true,
+ });
viewer.scene.primitives.add(tileset);
} catch (error) {
console.log(`Error loading Photorealistic 3D Tiles tileset.
diff --git a/Apps/Sandcastle/gallery/Imagery Color To Alpha.html b/Apps/Sandcastle/gallery/Imagery Color To Alpha.html
index 8fa3ed032883..c28533a3c4a9 100644
--- a/Apps/Sandcastle/gallery/Imagery Color To Alpha.html
+++ b/Apps/Sandcastle/gallery/Imagery Color To Alpha.html
@@ -51,7 +51,9 @@
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
- const viewer = new Cesium.Viewer("cesiumContainer");
+ const viewer = new Cesium.Viewer("cesiumContainer", {
+ geocoder: Cesium.IonGeocodeProviderType.BING,
+ });
const layers = viewer.scene.imageryLayers;
diff --git a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html
index 4e309a339dd7..9b1f026cdbab 100644
--- a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html
+++ b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html
@@ -106,6 +106,7 @@
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayerPicker: false,
+ geocoder: false,
});
const imageryLayers = viewer.imageryLayers;
diff --git a/Apps/Sandcastle/gallery/Imagery Layers Split.html b/Apps/Sandcastle/gallery/Imagery Layers Split.html
index 37927116fc29..15375661bb5f 100644
--- a/Apps/Sandcastle/gallery/Imagery Layers Split.html
+++ b/Apps/Sandcastle/gallery/Imagery Layers Split.html
@@ -58,6 +58,7 @@
),
baseLayerPicker: false,
infoBox: false,
+ geocoder: false,
});
const layers = viewer.imageryLayers;
diff --git a/Apps/Sandcastle/gallery/Interpolation.html b/Apps/Sandcastle/gallery/Interpolation.html
index 567ec12627ea..f21a5dd259c3 100644
--- a/Apps/Sandcastle/gallery/Interpolation.html
+++ b/Apps/Sandcastle/gallery/Interpolation.html
@@ -158,7 +158,7 @@
{
text: "Tracking reference frame: East-North-Up",
onselect: function () {
- entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.EAST_NORTH_UP;
+ entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.ENU;
},
},
{
diff --git a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html
index 10c3b2367a66..ce77a1cc5f35 100644
--- a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html
+++ b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html
@@ -34,6 +34,7 @@
animation: false,
baseLayerPicker: false,
globe: false,
+ geocoder: false,
});
const scene = viewer.scene;
@@ -44,7 +45,10 @@
onselect: async () => {
scene.primitives.remove(tileset);
try {
- tileset = await Cesium.createGooglePhotorealistic3DTileset();
+ tileset = await Cesium.createGooglePhotorealistic3DTileset({
+ // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE.
+ onlyUsingWithGoogleGeocoder: true,
+ });
scene.primitives.add(tileset);
} catch (error) {
console.log(error);
diff --git a/CHANGES.md b/CHANGES.md
index 3509bc126b3a..98047d22162c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,7 +7,12 @@
##### Additions :tada:
- Added `getSample` to `SampledProperty` to get the time of samples. [#12253](https://github.com/CesiumGS/cesium/pull/12253)
-- Added `Entity.trackingReferenceFrame` property to allow tracking entities in their own inertial reference frame. [#12194](https://github.com/CesiumGS/cesium/pull/12194)
+- Added `Entity.trackingReferenceFrame` property to allow tracking entities in various reference frames. [#12194](https://github.com/CesiumGS/cesium/pull/12194), [#12314](https://github.com/CesiumGS/cesium/pull/12314)
+ - `TrackingReferenceFrame.AUTODETECT` (default): uses either VVLH or ENU dependeding on entity's dynamic. Use `TrackingReferenceFrame.ENU` if your camera orientation flips abruptly from time to time.
+ - `TrackingReferenceFrame.ENU`: uses the entity's local East-North-Up reference frame.
+ - `TrackingReferenceFrame.INERTIAL`: uses the entity's inertial reference frame.
+ - `TrackingReferenceFrame.VELOCITY`: uses entity's `VelocityOrientationProperty` as orientation.
+- Added `GoogleGeocoderService` for standalone usage of Google geocoder. [#12299](https://github.com/CesiumGS/cesium/pull/12299)
##### Fixes :wrench:
@@ -15,8 +20,16 @@
- Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323)
- Fix point cloud filtering performance on certain hardware [#12317](https://github.com/CesiumGS/cesium/pull/12317)
+##### Deprecated :hourglass_flowing_sand:
+
+- `createGooglePhotorealistic3DTileset(key)` has been deprecated. Use `createGooglePhotorealistic3DTileset({key})` instead. It will be removed in 1.126.
+
#### @cesium/widgets
+##### Additions :tada:
+
+- Added the ability to choose between Bing and Google geocoders. Updated `Viewer` constructor to also accept `IonGeocoderProvider` [#12299](https://github.com/CesiumGS/cesium/pull/12299)
+
##### Fixes :wrench:
- Added a `DeveloperError` when `globe` is set to `false` and a `baseLayer` is provided in `Viewer` options. This prevents errors caused by attempting to use a `baseLayer` without a globe. [#12274](https://github.com/CesiumGS/cesium/pull/12274)
diff --git a/packages/engine/Source/Core/BingMapsGeocoderService.js b/packages/engine/Source/Core/BingMapsGeocoderService.js
index 647658fcd2d3..bd299d874026 100644
--- a/packages/engine/Source/Core/BingMapsGeocoderService.js
+++ b/packages/engine/Source/Core/BingMapsGeocoderService.js
@@ -10,6 +10,8 @@ const url = "https://dev.virtualearth.net/REST/v1/Locations";
/**
* Provides geocoding through Bing Maps.
+ *
+ * @see {@link https://www.microsoft.com/en-us/maps/bing-maps/product|Microsoft Bing Maps Platform APIs Terms Of Use}
* @alias BingMapsGeocoderService
* @constructor
*
diff --git a/packages/engine/Source/Core/GoogleGeocoderService.js b/packages/engine/Source/Core/GoogleGeocoderService.js
new file mode 100644
index 000000000000..67326e496e1c
--- /dev/null
+++ b/packages/engine/Source/Core/GoogleGeocoderService.js
@@ -0,0 +1,110 @@
+import Check from "./Check.js";
+import Credit from "./Credit.js";
+import defaultValue from "./defaultValue.js";
+import Rectangle from "./Rectangle.js";
+import Resource from "./Resource.js";
+import defined from "./defined.js";
+import DeveloperError from "./DeveloperError.js";
+import RuntimeError from "./RuntimeError.js";
+
+const API_URL = "https://maps.googleapis.com/maps/api/geocode/json";
+const CREDIT_HTML = ``;
+
+/**
+ * Provides geocoding through Google.
+ *
+ * @see {@link https://developers.google.com/maps/documentation/geocoding/policies|Google Geocoding Policies}
+ * @alias GoogleGeocoderService
+ * @constructor
+ *
+ * @param {object} options Object with the following properties:
+ * @param {string} options.key An API key to use with the Google geocoding service
+ */
+function GoogleGeocoderService(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+ const key = options.key;
+ //>>includeStart('debug', pragmas.debug);
+ if (!defined(key)) {
+ throw new DeveloperError("options.key is required.");
+ }
+ //>>includeEnd('debug');
+
+ this._resource = new Resource({
+ url: API_URL,
+ queryParameters: { key },
+ });
+
+ this._credit = new Credit(CREDIT_HTML, true);
+}
+
+Object.defineProperties(GoogleGeocoderService.prototype, {
+ /**
+ * Gets the credit to display after a geocode is performed. Typically this is used to credit
+ * the geocoder service.
+ * @memberof GoogleGeocoderService.prototype
+ * @type {Credit|undefined}
+ * @readonly
+ */
+ credit: {
+ get: function () {
+ return this._credit;
+ },
+ },
+});
+
+/**
+ * Get a list of possible locations that match a search string.
+ *
+ * @function
+ *
+ * @param {string} query The query to be sent to the geocoder service
+ * @returns {Promise}
+ * @throws {RuntimeError} If the services returns a status other than OK
or ZERO_RESULTS
+ */
+GoogleGeocoderService.prototype.geocode = async function (query) {
+ // See API documentation at https://developers.google.com/maps/documentation/geocoding/requests-geocoding
+
+ //>>includeStart('debug', pragmas.debug);
+ Check.typeOf.string("query", query);
+ //>>includeEnd('debug');
+
+ const resource = this._resource.getDerivedResource({
+ queryParameters: {
+ address: query,
+ },
+ });
+
+ const response = await resource.fetchJson();
+
+ if (response.status === "ZERO_RESULTS") {
+ return [];
+ }
+
+ if (response.status !== "OK") {
+ throw new RuntimeError(
+ `GoogleGeocoderService got a bad response ${response.status}: ${response.error_message}`,
+ );
+ }
+
+ const results = response.results.map((result) => {
+ const southWest = result.geometry.viewport.southwest;
+ const northEast = result.geometry.viewport.northeast;
+ return {
+ displayName: result.formatted_address,
+ destination: Rectangle.fromDegrees(
+ southWest.lng,
+ southWest.lat,
+ northEast.lng,
+ northEast.lat,
+ ),
+ attribution: {
+ html: CREDIT_HTML,
+ collapsible: false,
+ },
+ };
+ });
+
+ return results;
+};
+
+export default GoogleGeocoderService;
diff --git a/packages/engine/Source/Core/IonGeocodeProviderType.js b/packages/engine/Source/Core/IonGeocodeProviderType.js
new file mode 100644
index 000000000000..2dc9bf35d775
--- /dev/null
+++ b/packages/engine/Source/Core/IonGeocodeProviderType.js
@@ -0,0 +1,33 @@
+/**
+ * Underlying geocoding services that can be used via Cesium ion.
+ *
+ * @enum {string}
+ */
+const IonGeocodeProviderType = {
+ /**
+ * Google geocoder, for use with Google data.
+ *
+ * @type {string}
+ * @constant
+ */
+ GOOGLE: "GOOGLE",
+
+ /**
+ * Bing geocoder, for use with Bing data.
+ *
+ * @type {string}
+ * @constant
+ */
+ BING: "BING",
+
+ /**
+ * Use the default geocoder as set on the server. Used when neither Bing or
+ * Google data is used.
+ *
+ * @type {string}
+ * @constant
+ */
+ DEFAULT: "DEFAULT",
+};
+
+export default Object.freeze(IonGeocodeProviderType);
diff --git a/packages/engine/Source/Core/IonGeocoderService.js b/packages/engine/Source/Core/IonGeocoderService.js
index 81da96b7dbba..f473d80005b3 100644
--- a/packages/engine/Source/Core/IonGeocoderService.js
+++ b/packages/engine/Source/Core/IonGeocoderService.js
@@ -2,10 +2,45 @@ import Check from "./Check.js";
import Credit from "./Credit.js";
import defaultValue from "./defaultValue.js";
import defined from "./defined.js";
+import DeveloperError from "./DeveloperError.js";
import Ion from "./Ion.js";
+import IonGeocodeProviderType from "./IonGeocodeProviderType.js";
import PeliasGeocoderService from "./PeliasGeocoderService.js";
import Resource from "./Resource.js";
+/**
+ * @param {*} geocodeProviderType
+ * @throws {DeveloperError}
+ * @private
+ */
+function validateIonGeocodeProviderType(geocodeProviderType) {
+ if (
+ !Object.values(IonGeocodeProviderType).some(
+ (value) => value === geocodeProviderType,
+ )
+ ) {
+ throw new DeveloperError(
+ `Invalid geocodeProviderType: "${geocodeProviderType}"`,
+ );
+ }
+}
+
+const providerToParameterMap = Object.freeze({
+ [IonGeocodeProviderType.GOOGLE]: "google",
+ [IonGeocodeProviderType.BING]: "bing",
+ [IonGeocodeProviderType.DEFAULT]: undefined,
+});
+
+function providerToQueryParameter(provider) {
+ return providerToParameterMap[provider];
+}
+
+function queryParameterToProvider(parameter) {
+ return Object.entries(providerToParameterMap).find(
+ (entry) => entry[1] === parameter,
+ )[0];
+}
+
/**
* Provides geocoding through Cesium ion.
* @alias IonGeocoderService
@@ -15,6 +50,7 @@ import Resource from "./Resource.js";
* @param {Scene} options.scene The scene
* @param {string} [options.accessToken=Ion.defaultAccessToken] The access token to use.
* @param {string|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server.
+ * @param {IonGeocodeProviderType} [options.geocodeProviderType=IonGeocodeProviderType.DEFAULT] The geocoder the Cesium ion API server should use to fulfill this request.
*
* @see Ion
*/
@@ -25,6 +61,14 @@ function IonGeocoderService(options) {
Check.typeOf.object("options.scene", options.scene);
//>>includeEnd('debug');
+ const geocodeProviderType = defaultValue(
+ options.geocodeProviderType,
+ IonGeocodeProviderType.DEFAULT,
+ );
+ //>>includeStart('debug', pragmas.debug);
+ validateIonGeocodeProviderType(geocodeProviderType);
+ //>>includeEnd('debug');
+
const accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken);
const server = Resource.createIfNeeded(
defaultValue(options.server, Ion.defaultServer),
@@ -49,6 +93,9 @@ function IonGeocoderService(options) {
this._accessToken = accessToken;
this._server = server;
this._pelias = new PeliasGeocoderService(searchEndpoint);
+ // geocoderProviderType isn't stored here directly but instead relies on the
+ // query parameters of this._pelias.url. Use the setter logic to update value.
+ this.geocodeProviderType = geocodeProviderType;
}
Object.defineProperties(IonGeocoderService.prototype, {
@@ -64,6 +111,31 @@ Object.defineProperties(IonGeocoderService.prototype, {
return undefined;
},
},
+ /**
+ * The geocoding service that Cesium ion API server should use to fulfill geocding requests.
+ * @memberof IonGeocoderService.prototype
+ * @type {IonGeocodeProviderType}
+ * @default IonGeocodeProviderType.DEFAULT
+ */
+ geocodeProviderType: {
+ get: function () {
+ return queryParameterToProvider(
+ this._pelias.url.queryParameters["geocoder"],
+ );
+ },
+ set: function (geocodeProviderType) {
+ validateIonGeocodeProviderType(geocodeProviderType);
+ const query = {
+ ...this._pelias.url.queryParameters,
+ geocoder: providerToQueryParameter(geocodeProviderType),
+ };
+ // Delete the geocoder parameter to prevent sending &geocoder=undefined in the query
+ if (!defined(query.geocoder)) {
+ delete query.geocoder;
+ }
+ this._pelias.url.setQueryParameters(query);
+ },
+ },
});
/**
diff --git a/packages/engine/Source/Core/TrackingReferenceFrame.js b/packages/engine/Source/Core/TrackingReferenceFrame.js
index 4ad418e96b7a..d16be08b853b 100644
--- a/packages/engine/Source/Core/TrackingReferenceFrame.js
+++ b/packages/engine/Source/Core/TrackingReferenceFrame.js
@@ -16,25 +16,30 @@ const TrackingReferenceFrame = {
*/
AUTODETECT: 0,
+ /**
+ * The entity's local East-North-Up reference frame.
+ *
+ * @type {number}
+ * @constant
+ */
+ ENU: 1,
+
/**
* The entity's inertial reference frame. If entity has no defined orientation
- * property, a {@link VelocityOrientationProperty} is used instead, thus
- * falling back to TrackingReferenceFrame.VELOCITY
.
- * When selected, the auto-detect algorithm is overridden.
+ * property, it falls back to auto-detect algorithm.
*
* @type {number}
* @constant
*/
- INERTIAL: 1,
+ INERTIAL: 2,
/**
* The entity's inertial reference frame with orientation fixed to its
* {@link VelocityOrientationProperty}, ignoring its own orientation.
- * When selected, the auto-detect algorithm is overridden.
*
* @type {number}
* @constant
*/
- VELOCITY: 2,
+ VELOCITY: 3,
};
export default Object.freeze(TrackingReferenceFrame);
diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js
index bc759b53dbd6..9e7ae33234b5 100644
--- a/packages/engine/Source/DataSources/EntityView.js
+++ b/packages/engine/Source/DataSources/EntityView.js
@@ -269,7 +269,12 @@ function updateTransform(
rotationScratch,
);
Matrix4.fromRotationTranslation(rotation, cartesian, transform);
- } else if (hasBasis) {
+ } else if (
+ trackingReferenceFrame === TrackingReferenceFrame.ENU ||
+ !hasBasis
+ ) {
+ Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform);
+ } else {
transform[0] = xBasis.x;
transform[1] = xBasis.y;
transform[2] = xBasis.z;
@@ -286,9 +291,6 @@ function updateTransform(
transform[13] = cartesian.y;
transform[14] = cartesian.z;
transform[15] = 0.0;
- } else {
- // Stationary or slow-moving, low-altitude objects use East-North-Up.
- Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform);
}
camera._setTransform(transform);
diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js
index f762d748dc36..9c7bad99e30d 100644
--- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js
+++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js
@@ -4,23 +4,37 @@ import defined from "../Core/defined.js";
import IonResource from "../Core/IonResource.js";
import GoogleMaps from "../Core/GoogleMaps.js";
import Resource from "../Core/Resource.js";
+import oneTimeWarning from "../Core/oneTimeWarning.js";
+import deprecationWarning from "../Core/deprecationWarning.js";
/**
- * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset.
+ * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D
+ * Tiles tileset.
+ *
+ * Google Photorealistic 3D Tiles can only be used with the Google geocoder. To
+ * confirm that you are aware of this restriction pass
+ * `usingOnlyWithGoogleGeocoder: true` to the apiOptions. Otherwise a one time
+ * warning will be displayed when this function is called.
*
* @function
*
- * @param {string} [key=GoogleMaps.defaultApiKey] Your API key to access Google Photorealistic 3D Tiles. See {@link https://developers.google.com/maps/documentation/javascript/get-api-key} for instructions on how to create your own key.
- * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options.
+ * @param {object} [apiOptions]
+ * @param {string} [apiOptions.key=GoogleMaps.defaultApiKey] Your API key to access Google Photorealistic 3D Tiles. See {@link https://developers.google.com/maps/documentation/javascript/get-api-key} for instructions on how to create your own key.
+ * @param {true} [apiOptions.onlyUsingWithGoogleGeocoder] Confirmation that this tileset will only be used with the Google geocoder.
+ * @param {Cesium3DTileset.ConstructorOptions} [tilesetOptions] An object describing initialization options.
* @returns {Promise}
*
* @see GoogleMaps
*
* @example
- * const viewer = new Cesium.Viewer("cesiumContainer");
+ * const viewer = new Cesium.Viewer("cesiumContainer", {
+ * geocoder: Cesium.IonGeocodeProviderType.GOOGLE
+ * });
*
* try {
- * const tileset = await Cesium.createGooglePhotorealistic3DTileset();
+ * const tileset = await Cesium.createGooglePhotorealistic3DTileset({
+ * onlyUsingWithGoogleGeocoder: true,
+ * });
* viewer.scene.primitives.add(tileset));
* } catch (error) {
* console.log(`Error creating tileset: ${error}`);
@@ -30,27 +44,52 @@ import Resource from "../Core/Resource.js";
* // Use your own Google Maps API key
* Cesium.GoogleMaps.defaultApiKey = "your-api-key";
*
- * const viewer = new Cesium.Viewer("cesiumContainer");
+ * const viewer = new Cesium.Viewer("cesiumContainer". {
+ * geocoder: Cesium.IonGeocodeProviderType.GOOGLE
+ * });
*
* try {
- * const tileset = await Cesium.createGooglePhotorealistic3DTileset();
+ * const tileset = await Cesium.createGooglePhotorealistic3DTileset({
+ * onlyUsingWithGoogleGeocoder: true,
+ * });
* viewer.scene.primitives.add(tileset));
* } catch (error) {
* console.log(`Error creating tileset: ${error}`);
* }
*/
-async function createGooglePhotorealistic3DTileset(key, options) {
- options = defaultValue(options, {});
- options.cacheBytes = defaultValue(options.cacheBytes, 1536 * 1024 * 1024);
- options.maximumCacheOverflowBytes = defaultValue(
- options.maximumCacheOverflowBytes,
+async function createGooglePhotorealistic3DTileset(apiOptions, tilesetOptions) {
+ tilesetOptions = defaultValue(tilesetOptions, {});
+ tilesetOptions.cacheBytes = defaultValue(
+ tilesetOptions.cacheBytes,
+ 1536 * 1024 * 1024,
+ );
+ tilesetOptions.maximumCacheOverflowBytes = defaultValue(
+ tilesetOptions.maximumCacheOverflowBytes,
1024 * 1024 * 1024,
);
- options.enableCollision = defaultValue(options.enableCollision, true);
+ tilesetOptions.enableCollision = defaultValue(
+ tilesetOptions.enableCollision,
+ true,
+ );
- key = defaultValue(key, GoogleMaps.defaultApiKey);
+ apiOptions = defaultValue(apiOptions, defaultValue.EMPTY_OBJECT);
+ if (typeof apiOptions === "string") {
+ deprecationWarning(
+ "createGooglePhotorealistic3DTileset(key)",
+ "createGooglePhotorealistic3DTileset(key) has been deprecated. It is replaced by createGooglePhotorealistic3DTileset({key}). It will be removed in Cesium 1.126.",
+ );
+ apiOptions = { key: apiOptions };
+ }
+ if (!apiOptions.onlyUsingWithGoogleGeocoder) {
+ oneTimeWarning(
+ "google-tiles-with-google-geocoder",
+ "Only the Google geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of Viewer constructor options. You can set additionalOptions.onlyUsingWithGoogleGeocoder to hide this warning once you have configured the geocoder.",
+ );
+ }
+
+ const key = defaultValue(apiOptions.key, GoogleMaps.defaultApiKey);
if (!defined(key)) {
- return requestCachedIonTileset(options);
+ return requestCachedIonTileset(tilesetOptions);
}
let credits;
@@ -67,7 +106,7 @@ async function createGooglePhotorealistic3DTileset(key, options) {
credits: credits,
});
- return Cesium3DTileset.fromUrl(resource, options);
+ return Cesium3DTileset.fromUrl(resource, tilesetOptions);
}
const metadataCache = {};
diff --git a/packages/engine/Specs/Core/GoogleGeocoderServicesSpec.js b/packages/engine/Specs/Core/GoogleGeocoderServicesSpec.js
new file mode 100644
index 000000000000..5260dc265742
--- /dev/null
+++ b/packages/engine/Specs/Core/GoogleGeocoderServicesSpec.js
@@ -0,0 +1,84 @@
+import {
+ createGuid,
+ GeocoderService,
+ GoogleGeocoderService,
+ Resource,
+ Rectangle,
+} from "../../index.js";
+
+describe("Core/GoogleGeocoderService", function () {
+ it("conforms to GeocoderService interface", function () {
+ expect(GoogleGeocoderService).toConformToInterface(GeocoderService);
+ });
+
+ it("constructor throws without key", function () {
+ expect(function () {
+ return new GoogleGeocoderService({});
+ }).toThrowDeveloperError();
+ });
+
+ it("constructor sets key on _resource", function () {
+ const key = createGuid();
+ const service = new GoogleGeocoderService({ key });
+ expect(service._resource.toString()).toEqual(
+ `https://maps.googleapis.com/maps/api/geocode/json?key=${key}`,
+ );
+ });
+
+ it("geocode returns results for status=OK", async function () {
+ const key = createGuid();
+ const query = createGuid();
+ const service = new GoogleGeocoderService({ key });
+
+ spyOn(Resource.prototype, "fetchJson").and.resolveTo({
+ results: [
+ {
+ formatted_address:
+ "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
+ geometry: {
+ viewport: {
+ northeast: {
+ lat: 37.4237349802915,
+ lng: -122.083183169709,
+ },
+ southwest: {
+ lat: 37.4210370197085,
+ lng: -122.085881130292,
+ },
+ },
+ },
+ },
+ ],
+ status: "OK",
+ });
+
+ const results = await service.geocode(query);
+
+ expect(results).toEqual([
+ {
+ displayName: "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
+ destination: Rectangle.fromDegrees(
+ -122.085881130292,
+ 37.4210370197085,
+ -122.083183169709,
+ 37.4237349802915,
+ ),
+ attribution: {
+ html: ``,
+ collapsible: false,
+ },
+ },
+ ]);
+ });
+
+ it("returns empty array for status=ZERO_RESULTS", async function () {
+ const service = new GoogleGeocoderService({ key: "key" });
+
+ spyOn(Resource.prototype, "fetchJson").and.resolveTo({
+ status: "ZERO_RESULTS",
+ });
+
+ const results = await service.geocode("test");
+ expect(results).toEqual([]);
+ });
+});
diff --git a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js
index 17951c615c02..719a762daffc 100644
--- a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js
+++ b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js
@@ -1,7 +1,9 @@
import {
+ DeveloperError,
GeocoderService,
GeocodeType,
Ion,
+ IonGeocodeProviderType,
IonGeocoderService,
} from "../../index.js";
@@ -26,20 +28,24 @@ describe("Core/IonGeocoderService", function () {
expect(service._accessToken).toEqual(Ion.defaultAccessToken);
expect(service._server.url).toEqual(Ion.defaultServer.url);
+ expect(service.geocodeProviderType).toEqual(IonGeocodeProviderType.DEFAULT);
});
it("creates with specified parameters", function () {
const accessToken = "123456";
const server = "http://not.ion.invalid/";
+ const geocodeProviderType = IonGeocodeProviderType.GOOGLE;
const service = new IonGeocoderService({
accessToken: accessToken,
server: server,
scene: scene,
+ geocodeProviderType,
});
expect(service._accessToken).toEqual(accessToken);
expect(service._server.url).toEqual(server);
+ expect(service.geocodeProviderType).toEqual(geocodeProviderType);
});
it("calls inner geocoder and returns result", async function () {
@@ -64,4 +70,47 @@ describe("Core/IonGeocoderService", function () {
expect(service.credit).toBeUndefined();
});
+
+ it("setting geocodeProviderType updates _pelias.url for GOOGLE", function () {
+ const service = new IonGeocoderService({
+ scene,
+ geocoder: IonGeocodeProviderType.DEFAULT,
+ });
+
+ service.geocodeProviderType = IonGeocodeProviderType.GOOGLE;
+ expect(service._pelias.url.queryParameters["geocoder"]).toEqual("google");
+ });
+
+ it("setting geocodeProviderType updates _pelias.url for BING", function () {
+ const service = new IonGeocoderService({
+ scene,
+ geocoder: IonGeocodeProviderType.DEFAULT,
+ });
+
+ service.geocodeProviderType = IonGeocodeProviderType.BING;
+ expect(service._pelias.url.queryParameters["geocoder"]).toEqual("bing");
+ });
+
+ it("setting geocodeProviderType updates _pelias.url for DEFAULT", function () {
+ const service = new IonGeocoderService({
+ scene,
+ geocoder: IonGeocodeProviderType.GOOGLE,
+ });
+
+ service.geocodeProviderType = IonGeocodeProviderType.DEFAULT;
+ const queryParameters = service._pelias.url.queryParameters;
+ expect(queryParameters.geocoder).toBeUndefined();
+ // Make sure that it isn't 'geocoder: undefined'
+ expect(queryParameters.hasOwnProperty("geocoder")).toBeFalse();
+ });
+
+ it("throws if setting invalid geocodeProviderType", function () {
+ expect(
+ () => new IonGeocoderService({ scene, geocodeProviderType: "junk" }),
+ ).toThrowError(DeveloperError, /Invalid geocodeProviderType/);
+ expect(() => {
+ const service = new IonGeocoderService({ scene });
+ service.geocodeProviderType = "junk";
+ }).toThrowError(DeveloperError, /Invalid geocodeProviderType/);
+ });
});
diff --git a/packages/engine/Specs/DataSources/EntityViewSpec.js b/packages/engine/Specs/DataSources/EntityViewSpec.js
index 2aa12913f9e5..61223cc1dd0b 100644
--- a/packages/engine/Specs/DataSources/EntityViewSpec.js
+++ b/packages/engine/Specs/DataSources/EntityViewSpec.js
@@ -83,6 +83,10 @@ describe(
entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY;
view.update(JulianDate.now());
expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10);
+
+ entity.trackingReferenceFrame = TrackingReferenceFrame.ENU;
+ view.update(JulianDate.now());
+ expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10);
});
it("uses entity bounding sphere", function () {
@@ -115,6 +119,13 @@ describe(
new BoundingSphere(new Cartesian3(3, 4, 5), 6),
);
expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10);
+
+ entity.trackingReferenceFrame = TrackingReferenceFrame.ENU;
+ view.update(
+ JulianDate.now(),
+ new BoundingSphere(new Cartesian3(3, 4, 5), 6),
+ );
+ expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10);
});
it("uses entity viewFrom if available and boundingsphere is supplied", function () {
@@ -140,6 +151,10 @@ describe(
entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY;
view.update(JulianDate.now());
expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10);
+
+ entity.trackingReferenceFrame = TrackingReferenceFrame.ENU;
+ view.update(JulianDate.now());
+ expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10);
});
it("update throws without time parameter", function () {
diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js
index 41f76c1bb495..b00646389215 100644
--- a/packages/widgets/Source/Viewer/Viewer.js
+++ b/packages/widgets/Source/Viewer/Viewer.js
@@ -18,6 +18,7 @@ import {
Math as CesiumMath,
Property,
ScreenSpaceEventType,
+ IonGeocoderService,
} from "@cesium/engine";
import Animation from "../Animation/Animation.js";
import AnimationViewModel from "../Animation/AnimationViewModel.js";
@@ -280,7 +281,7 @@ function enableVRUI(viewer, enabled) {
* @property {boolean} [baseLayerPicker=true] If set to false, the BaseLayerPicker widget will not be created.
* @property {boolean} [fullscreenButton=true] If set to false, the FullscreenButton widget will not be created.
* @property {boolean} [vrButton=false] If set to true, the VRButton widget will be created.
- * @property {boolean|GeocoderService[]} [geocoder=true] If set to false, the Geocoder widget will not be created.
+ * @property {boolean|IonGeocodeProviderType|GeocoderService[]} [geocoder=IonGeocodeProviderType.DEFAULT] The geocoding service or services to use when searching with the Geocoder widget. If set to false, the Geocoder widget will not be created.
* @property {boolean} [homeButton=true] If set to false, the HomeButton widget will not be created.
* @property {boolean} [infoBox=true] If set to false, the InfoBox widget will not be created.
* @property {boolean} [sceneModePicker=true] If set to false, the SceneModePicker widget will not be created.
@@ -567,7 +568,17 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
geocoderContainer.className = "cesium-viewer-geocoderContainer";
toolbar.appendChild(geocoderContainer);
let geocoderService;
- if (defined(options.geocoder) && typeof options.geocoder !== "boolean") {
+ if (typeof options.geocoder === "string") {
+ geocoderService = [
+ new IonGeocoderService({
+ scene,
+ geocodeProviderType: options.geocoder,
+ }),
+ ];
+ } else if (
+ defined(options.geocoder) &&
+ typeof options.geocoder !== "boolean"
+ ) {
geocoderService = Array.isArray(options.geocoder)
? options.geocoder
: [options.geocoder];
diff --git a/packages/widgets/Specs/Viewer/ViewerSpec.js b/packages/widgets/Specs/Viewer/ViewerSpec.js
index a967a34289e1..822df41c034b 100644
--- a/packages/widgets/Specs/Viewer/ViewerSpec.js
+++ b/packages/widgets/Specs/Viewer/ViewerSpec.js
@@ -18,6 +18,8 @@ import {
ImageryLayerCollection,
SceneMode,
ShadowMode,
+ IonGeocodeProviderType,
+ IonGeocoderService,
} from "@cesium/engine";
import {
@@ -354,6 +356,19 @@ describe(
expect(viewer.geocoder.viewModel._geocoderServices.length).toBe(1);
});
+ it("constructs geocoder with IonGeocodeProviderType", function () {
+ viewer = createViewer(container, {
+ geocoder: IonGeocodeProviderType.GOOGLE,
+ });
+ expect(viewer.geocoder).toBeDefined();
+ expect(viewer.geocoder.viewModel._geocoderServices.length).toBe(1);
+ const geocoderService = viewer.geocoder.viewModel._geocoderServices[0];
+ expect(geocoderService).toBeInstanceOf(IonGeocoderService);
+ expect(geocoderService.geocodeProviderType).toEqual(
+ IonGeocodeProviderType.GOOGLE,
+ );
+ });
+
it("constructs geocoder with geocoder service option", function () {
const service = new CartographicGeocoderService();
viewer = createViewer(container, {