Skip to content

Commit

Permalink
Merge pull request #6957 from TerriaJS/use-cesium-ion-geocoder
Browse files Browse the repository at this point in the history
Use cesium ion geocoder
  • Loading branch information
ljowen authored Nov 14, 2023
2 parents c6e0d51 + a6748e2 commit 0a5025d
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 6 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- See [ASGS 2021](https://www.abs.gov.au/statistics/standards/australian-statistical-geography-standard-asgs-edition-3/jul2021-jun2026/access-and-downloads/digital-boundary-files)
- Added [Melbourne CLUE blocks](https://data.melbourne.vic.gov.au/pages/clue/) to region mapping.
- Fix WMS `GetMap`/`GetFeatureInfo` requests not having `styles` parameter (will use empty string instead of `undefined`)
- [The next improvement]
- Add CesiumIon geocoder

#### 8.3.7 - 2023-10-26

Expand Down
2 changes: 1 addition & 1 deletion buildprocess/ci-deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ npm install -g yarn@^1.19.0

# Clone and build TerriaMap, using this version of TerriaJS
TERRIAJS_COMMIT_HASH=$(git rev-parse HEAD)
git clone -b main https://github.com/TerriaJS/TerriaMap.git
git clone -b use-cesium-ion-geocoder https://github.com/TerriaJS/TerriaMap.git
cd TerriaMap
TERRIAMAP_COMMIT_HASH=$(git rev-parse HEAD)
sed -i -e 's@"terriajs": ".*"@"terriajs": "'$GITHUB_REPOSITORY'#'${GITHUB_BRANCH}'"@g' package.json
Expand Down
3 changes: 2 additions & 1 deletion lib/Core/AnalyticEvents/analyticEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export enum SearchAction {
bing = "Bing",
catalog = "Catalog",
gazetteer = "Gazetteer",
nominatim = "nominatim"
nominatim = "nominatim",
cesium = "Cesium"
}

export enum LaunchAction {
Expand Down
6 changes: 3 additions & 3 deletions lib/Core/loadJson.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Resource from "terriajs-cesium/Source/Core/Resource";

export default function loadJson(
export default function loadJson<T = any>(
urlOrResource: any,
headers?: any,
body?: any,
asForm: boolean = false
): Promise<any> {
): Promise<T> {
let responseType: XMLHttpRequestResponseType = "json";

let jsonPromise: Promise<any>;
let jsonPromise: Promise<T>;
let params: any = {
url: urlOrResource,
headers: headers
Expand Down
124 changes: 124 additions & 0 deletions lib/Models/SearchProviders/CesiumIonSearchProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import SearchProvider from "./SearchProvider";
import { observable, makeObservable, runInAction } from "mobx";
import Rectangle from "terriajs-cesium/Source/Core/Rectangle";
import defaultValue from "terriajs-cesium/Source/Core/defaultValue";
import i18next from "i18next";
import Terria from "../Terria";
import SearchProviderResults from "./SearchProviderResults";
import SearchResult from "./SearchResult";
import loadJson from "../../Core/loadJson";
import {
Category,
SearchAction
} from "../../Core/AnalyticEvents/analyticEvents";

interface CesiumIonSearchProviderOptions {
terria: Terria;
url?: string;
key: string;
flightDurationSeconds?: number;
}

interface CesiumIonGeocodeResultFeature {
bbox: [number, number, number, number];
properties: { label: string };
}

interface CesiumIonGeocodeResult {
features: CesiumIonGeocodeResultFeature[];
}

export default class CesiumIonSearchProvider extends SearchProvider {
readonly terria: Terria;
@observable key: string | undefined;
@observable flightDurationSeconds: number;
@observable url: string;

constructor(options: CesiumIonSearchProviderOptions) {
super();

makeObservable(this);

this.terria = options.terria;
this.name = i18next.t("viewModels.searchLocations");
this.url = defaultValue(
options.url,
"https://api.cesium.com/v1/geocode/search"
);
this.key = options.key;
this.flightDurationSeconds = defaultValue(
options.flightDurationSeconds,
1.5
);

if (!this.key) {
console.warn(
"The " +
this.name +
" geocoder will always return no results because a CesiumIon key has not been provided. Please get a CesiumIon key from ion.cesium.com, ensure it has geocoding permission and add it to parameters.cesiumIonAccessToken in config.json."
);
}
}

protected async doSearch(
searchText: string,
searchResults: SearchProviderResults
): Promise<void> {
if (searchText === undefined || /^\s*$/.test(searchText)) {
return Promise.resolve();
}

this.terria.analytics?.logEvent(
Category.search,
SearchAction.cesium,
searchText
);

let response: CesiumIonGeocodeResult;
try {
response = await loadJson<CesiumIonGeocodeResult>(
`${this.url}?text=${searchText}&access_token=${this.key}`
);
} catch (e) {
runInAction(() => {
searchResults.message = i18next.t("viewModels.searchErrorOccurred");
});
return;
}

runInAction(() => {
if (!response.features) {
searchResults.message = i18next.t("viewModels.searchNoLocations");
return;
}

if (response.features.length === 0) {
searchResults.message = i18next.t("viewModels.searchNoLocations");
}

searchResults.results = response.features.map<SearchResult>((feature) => {
const [w, s, e, n] = feature.bbox;
const rectangle = Rectangle.fromDegrees(w, s, e, n);

return new SearchResult({
name: feature.properties.label,
clickAction: createZoomToFunction(this, rectangle),
location: {
latitude: (s + n) / 2,
longitude: (e + w) / 2
}
});
});
});
}
}

function createZoomToFunction(
model: CesiumIonSearchProvider,
rectangle: Rectangle
) {
return function () {
const terria = model.terria;
terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds);
};
}
59 changes: 59 additions & 0 deletions test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import CesiumIonSearchProvider from "../../../lib/Models/SearchProviders/CesiumIonSearchProvider";
import Terria from "../../../lib/Models//Terria";
import * as loadJson from "../../../lib/Core/loadJson";

const fixture = {
features: [
{
properties: {
label: "West End, Australia"
},
bbox: [
152.99620056152344, -27.490509033203125, 153.0145721435547,
-27.474090576171875
]
}
]
};

describe("CesiumIonSearchProvider", () => {
const searchProvider = new CesiumIonSearchProvider({
key: "testkey",
url: "api.test.com",
terria: {
currentViewer: {
zoomTo: () => {}
}
} as unknown as Terria
});
it("Handles valid results", async () => {
spyOn(loadJson, "default").and.returnValue(
new Promise((resolve) => resolve(fixture))
);

const result = await searchProvider.search("test");
expect(loadJson.default).toHaveBeenCalledWith(
"api.test.com?text=test&access_token=testkey"
);
expect(result.results.length).toBe(1);
expect(result.results[0].name).toBe("West End, Australia");
expect(result.results[0].location?.latitude).toBe(-27.4822998046875);
});

it("Handles empty result", async () => {
spyOn(loadJson, "default").and.returnValue(
new Promise((resolve) => resolve([]))
);
const result = await searchProvider.search("test");
console.log(result);
expect(result.results.length).toBe(0);
expect(result.message).toBe("viewModels.searchNoLocations");
});

it("Handles error", async () => {
spyOn(loadJson, "default").and.throwError("error");
const result = await searchProvider.search("test");
expect(result.results.length).toBe(0);
expect(result.message).toBe("viewModels.searchErrorOccurred");
});
});

0 comments on commit 0a5025d

Please sign in to comment.