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

Add option to select which geocoder is used #12299

Merged
merged 19 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ <h1>Loading...</h1>
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
geocoder: Cesium.IonGeocodeProvider.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
Expand Down
1 change: 1 addition & 0 deletions Apps/Sandcastle/gallery/AEC Clipping.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
geocoder: Cesium.IonGeocodeProvider.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
Expand Down
1 change: 1 addition & 0 deletions Apps/Sandcastle/gallery/Bing Maps Labels Only.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
baseLayer: false,
baseLayerPicker: false,
infoBox: false,
geocoder: Cesium.IonGeocodeProvider.BING,
});

const layers = viewer.imageryLayers;
Expand Down
1 change: 1 addition & 0 deletions Apps/Sandcastle/gallery/Clamp Entities to Ground.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
timeline: false,
animation: false,
baseLayerPicker: false,
geocoder: Cesium.IonGeocodeProvider.GOOGLE,
});
const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true;
Expand Down
1 change: 1 addition & 0 deletions Apps/Sandcastle/gallery/Globe Interior.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
orderIndependentTranslucency: false,
geocoder: Cesium.IonGeocodeProvider.BING,
});

const scene = viewer.scene;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
geocoder: Cesium.IonGeocodeProvider.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
animation: false,
sceneModePicker: false,
baseLayerPicker: false,
geocoder: Cesium.IonGeocodeProvider.GOOGLE,
// The globe does not need to be displayed,
// since the Photorealistic 3D Tiles include terrain
globe: false,
Expand Down
4 changes: 3 additions & 1 deletion Apps/Sandcastle/gallery/Imagery Color To Alpha.html
Original file line number Diff line number Diff line change
Expand Up @@ -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.IonGeocodeProvider.BING,
});

const layers = viewer.scene.imageryLayers;

Expand Down
1 change: 1 addition & 0 deletions Apps/Sandcastle/gallery/Imagery Layers Manipulation.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayerPicker: false,
geocoder: Cesium.IonGeocodeProvider.BING,
});
const imageryLayers = viewer.imageryLayers;

Expand Down
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

### 1.124 - 2024-12-01

##### Additions :tada:

- Ability to choose between Bing and Google geocoders. Adds `GoogleGeocoderService` for standalone usage of Google geocoder. Updates `Viewer` constructor to also accept `IonGeocoderProvider` [#12299](https://github.com/CesiumGS/cesium/pull/12299)
angrycat9000 marked this conversation as resolved.
Show resolved Hide resolved

### 1.123.1 - 2024-11-07

#### @cesium/engine
Expand Down
107 changes: 107 additions & 0 deletions packages/engine/Source/Core/GoogleGeocoderService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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";

const API_URL = "https://maps.googleapis.com/maps/api/geocode/json";
const CREDIT_HTML = `<img alt="Google" src="https://assets.ion.cesium.com/google-credit.png" style="vertical-align:-5px">`;

/**
* Provides geocoding through Google.
*
* @see {@link https://developers.google.com/maps/documentation/geocoding/policies|Google Geocoding Policies}
Copy link
Contributor

Choose a reason for hiding this comment

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

Great that you included the link! Can we call out explicitly the "TLDR"– that Google Geocoding should only be used when using Google data in the scene? And is there a similar disclaimer we should put in BingMapsGeocoderService?

* @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;
},
},
});

/**
* @function
*
ggetz marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} query The query to be sent to the geocoder service
* @returns {Promise<GeocoderService.Result[]>}
* @throws {Error} If the services returns a status other than <code>OK</code> or <code>ZERO_RESULTS</code>
*/
GoogleGeocoderService.prototype.geocode = async function (query) {
// See API documentation at https://developers.google.com/maps/documentation/geocoding/requests-geocoding
ggetz marked this conversation as resolved.
Show resolved Hide resolved

//>>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 Error(
angrycat9000 marked this conversation as resolved.
Show resolved Hide resolved
`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;
33 changes: 33 additions & 0 deletions packages/engine/Source/Core/IonGeocodeProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Underlying geocoding services that can be used via Cesium ion.
*
* @enum {string}
*/
const IonGeocodeProvider = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const IonGeocodeProvider = {
const IonGeocodeProviderType = {

This may be a bit nit-picky, but type may help distinguish this from from the *GeocoderService instance. I got a bit confused when looking through the specs which was which.

/**
* 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(IonGeocodeProvider);
64 changes: 64 additions & 0 deletions packages/engine/Source/Core/IonGeocoderService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,43 @@ 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 IonGeocodeProvider from "./IonGeocodeProvider.js";
import PeliasGeocoderService from "./PeliasGeocoderService.js";
import Resource from "./Resource.js";

/**
* @param {*} geocodeProvider
* @throws {DeveloperError}
* @private
*/
function validateIonGeocodeProvider(geocodeProvider) {
if (
!Object.values(IonGeocodeProvider).some(
(value) => value === geocodeProvider,
)
) {
throw new DeveloperError(`Invalid geocodeProvider: "${geocodeProvider}"`);
}
}

const providerToParameterMap = Object.freeze({
[IonGeocodeProvider.GOOGLE]: "google",
[IonGeocodeProvider.BING]: "bing",
[IonGeocodeProvider.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
Expand All @@ -15,6 +48,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 {IonGeocodeProvider} [options.geocodeProvider=IonGeocodeProvider.DEFAULT] The geocoder the Cesium ion API server should use to fulfill this request.
*
* @see Ion
*/
Expand All @@ -25,6 +59,12 @@ function IonGeocoderService(options) {
Check.typeOf.object("options.scene", options.scene);
//>>includeEnd('debug');

const geocodeProvider = defaultValue(
options.geocodeProvider,
IonGeocodeProvider.DEFAULT,
);
validateIonGeocodeProvider(geocodeProvider);

const accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken);
const server = Resource.createIfNeeded(
defaultValue(options.server, Ion.defaultServer),
Expand All @@ -49,6 +89,7 @@ function IonGeocoderService(options) {
this._accessToken = accessToken;
this._server = server;
this._pelias = new PeliasGeocoderService(searchEndpoint);
this.geocodeProvider = geocodeProvider;
}

Object.defineProperties(IonGeocoderService.prototype, {
Expand All @@ -64,6 +105,29 @@ Object.defineProperties(IonGeocoderService.prototype, {
return undefined;
},
},
/**
* @memberof IonGeocoderService.prototype
* @type {IonGeocodeProvider}
*/
angrycat9000 marked this conversation as resolved.
Show resolved Hide resolved
geocodeProvider: {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
geocodeProvider: {
geocodeProviderType: {

This may be a bit nit-picky, but type may help distinguish this from from the *GeocoderService instance. I got a bit confused when looking through the specs which was which.

get: function () {
return queryParameterToProvider(
this._pelias.url.queryParameters["geocoder"],
);
},
set: function (geocodeProvider) {
validateIonGeocodeProvider(geocodeProvider);
const query = {
...this._pelias.url.queryParameters,
geocoder: providerToQueryParameter(geocodeProvider),
};
// Delete the geocoder parameter to prevent sending &geocoder=undefined in the query
if (!defined(query.geocoder)) {
delete query.geocoder;
}
this._pelias.url.setQueryParameters(query);
},
},
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import Resource from "../Core/Resource.js";
* @see GoogleMaps
*
* @example
* const viewer = new Cesium.Viewer("cesiumContainer");
* const viewer = new Cesium.Viewer("cesiumContainer", {
ggetz marked this conversation as resolved.
Show resolved Hide resolved
* geocoder: Cesium.IonGeocodeProvider.GOOGLE
* });
*
* try {
* const tileset = await Cesium.createGooglePhotorealistic3DTileset();
Expand All @@ -30,7 +32,9 @@ 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.IonGeocodeProvider.GOOGLE
* });
*
* try {
* const tileset = await Cesium.createGooglePhotorealistic3DTileset();
Expand Down
Loading
Loading