terriajs
Version:
Geospatial data visualization platform.
202 lines (173 loc) • 5.95 kB
text/typescript
import i18next from "i18next";
import { makeObservable, override, runInAction } from "mobx";
import Rectangle from "terriajs-cesium/Source/Core/Rectangle";
import Resource from "terriajs-cesium/Source/Core/Resource";
import defined from "terriajs-cesium/Source/Core/defined";
import {
Category,
SearchAction
} from "../../Core/AnalyticEvents/analyticEvents";
import loadJson from "../../Core/loadJson";
import { applyTranslationIfExists } from "../../Language/languageHelpers";
import LocationSearchProviderMixin, {
getMapCenter
} from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin";
import BingMapsSearchProviderTraits from "../../Traits/SearchProviders/BingMapsSearchProviderTraits";
import CreateModel from "../Definition/CreateModel";
import Terria from "../Terria";
import CommonStrata from "./../Definition/CommonStrata";
import SearchProviderResults from "./SearchProviderResults";
import SearchResult from "./SearchResult";
export default class BingMapsSearchProvider extends LocationSearchProviderMixin(
CreateModel(BingMapsSearchProviderTraits)
) {
static readonly type = "bing-maps-search-provider";
get type() {
return BingMapsSearchProvider.type;
}
constructor(uniqueId: string | undefined, terria: Terria) {
super(uniqueId, terria);
makeObservable(this);
runInAction(() => {
if (this.terria.configParameters.bingMapsKey) {
this.setTrait(
CommonStrata.defaults,
"key",
this.terria.configParameters.bingMapsKey
);
}
});
}
override showWarning() {
if (!this.key || this.key === "") {
console.warn(
`The ${applyTranslationIfExists(this.name, i18next)}(${
this.type
}) geocoder will always return no results because a Bing Maps key has not been provided. Please get a Bing Maps key from bingmapsportal.com and add it to parameters.bingMapsKey in config.json.`
);
}
}
protected logEvent(searchText: string) {
this.terria.analytics?.logEvent(
Category.search,
SearchAction.bing,
searchText
);
}
protected doSearch(
searchText: string,
searchResults: SearchProviderResults
): Promise<void> {
searchResults.results.length = 0;
searchResults.message = undefined;
const searchQuery = new Resource({
url: this.url + "REST/v1/Locations",
queryParameters: {
culture: this.culture,
query: searchText,
key: this.key,
maxResults: this.maxResults
}
});
if (this.mapCenter) {
const mapCenter = getMapCenter(this.terria);
searchQuery.appendQueryParameters({
userLocation: `${mapCenter.latitude}, ${mapCenter.longitude}`
});
}
const promise: Promise<any> = loadJson(searchQuery);
return promise
.then((result) => {
if (searchResults.isCanceled) {
// A new search has superseded this one, so ignore the result.
return;
}
if (result.resourceSets.length === 0) {
searchResults.message = {
content: "translate#viewModels.searchNoLocations"
};
return;
}
const resourceSet = result.resourceSets[0];
if (resourceSet.resources.length === 0) {
searchResults.message = {
content: "translate#viewModels.searchNoLocations"
};
return;
}
runInAction(() => {
const locations = this.sortByPriority(resourceSet.resources);
searchResults.results.push(...locations.primaryCountry);
searchResults.results.push(...locations.other);
});
if (searchResults.results.length === 0) {
searchResults.message = {
content: "translate#viewModels.searchNoLocations"
};
}
})
.catch(() => {
if (searchResults.isCanceled) {
// A new search has superseded this one, so ignore the result.
return;
}
searchResults.message = {
content: "translate#viewModels.searchErrorOccurred"
};
});
}
protected sortByPriority(resources: any[]) {
const primaryCountryLocations: any[] = [];
const otherLocations: any[] = [];
// Locations in the primary country go on top, locations elsewhere go undernearth and we add
// the country name to them.
for (let i = 0; i < resources.length; ++i) {
const resource = resources[i];
let name = resource.name;
if (!defined(name)) {
continue;
}
let list = primaryCountryLocations;
let isImportant = true;
const country = resource.address
? resource.address.countryRegion
: undefined;
if (defined(this.primaryCountry) && country !== this.primaryCountry) {
// Add this location to the list of other locations.
list = otherLocations;
isImportant = false;
// Add the country to the name, if it's not already there.
if (
defined(country) &&
name.lastIndexOf(country) !== name.length - country.length
) {
name += ", " + country;
}
}
list.push(
new SearchResult({
name: name,
isImportant: isImportant,
clickAction: createZoomToFunction(this, resource),
location: {
latitude: resource.point.coordinates[0],
longitude: resource.point.coordinates[1]
}
})
);
}
return {
primaryCountry: primaryCountryLocations,
other: otherLocations
};
}
}
function createZoomToFunction(model: BingMapsSearchProvider, resource: any) {
const [south, west, north, east] = resource.bbox;
const rectangle = Rectangle.fromDegrees(west, south, east, north);
return function () {
const terria = model.terria;
terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds);
};
}