From 1beeec0d339d28d8d05a14c336a24d91799a09a9 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Tue, 19 Jan 2021 13:24:52 +0100 Subject: [PATCH 001/129] convert search providers to utilise the new model architecture - add SearchProviderMixin to connect searchProviders with model system - write architecture decission record - port existing search providers to proposed model --- .../0007-configurable-search-providers.md | 35 ++ lib/ModelMixins/SearchProviderMixin.ts | 92 +++++ .../WebFeatureServiceSearchProviderMixin.ts | 222 ++++++++++++ lib/Models/BingMapsSearchProvider.ts | 201 ----------- lib/Models/SearchProvider.ts | 27 -- .../AustralianGazetteerSearchProvider.ts | 44 +-- .../SearchProvider/BingMapsSearchProvider.ts | 189 +++++++++++ .../CatalogSearchProvider.ts | 34 +- .../SearchProvider/SearchProviderFactory.ts | 4 + .../SearchProviderResults.ts | 6 +- .../{ => SearchProvider}/SearchResult.ts | 8 +- .../SearchProvider/StubSearchProvider.ts | 31 ++ .../createStubSearchProvider.ts | 27 ++ .../SearchProvider/registerSearchProviders.ts | 15 + .../upsertSearchProviderFromJson.ts | 57 ++++ lib/Models/Terria.ts | 129 ++++++- lib/Models/WebFeatureServiceSearchProvider.ts | 221 ------------ lib/ReactViewModels/SearchState.ts | 24 +- lib/ReactViews/Mobile/MobileHeader.jsx | 44 +-- lib/ReactViews/SidePanel/SidePanel.jsx | 8 +- .../BingMapsSearchProviderTraits.ts | 35 ++ .../CatalogSearchProviderTraits.ts | 14 + .../LocationSearchProviderTraits.ts | 37 ++ .../SearchProvider/SearchProviderTraits.ts | 32 ++ .../WebFeatureServiceSearchProviderTraits.ts | 18 + .../CatalogItemNameSearchProviderViewModel.js | 317 ------------------ .../GazetteerSearchProviderViewModel.js | 227 ------------- lib/ViewModels/GnafSearchProviderViewModel.js | 119 ------- .../NominatimSearchProviderViewModel.js | 199 ----------- .../AustralianGazetteerSearchProviderSpec.ts | 4 +- ...alogItemNameSearchProviderViewModelSpec.js | 280 ---------------- 31 files changed, 1023 insertions(+), 1677 deletions(-) create mode 100644 architecture/0007-configurable-search-providers.md create mode 100644 lib/ModelMixins/SearchProviderMixin.ts create mode 100644 lib/ModelMixins/WebFeatureServiceSearchProviderMixin.ts delete mode 100644 lib/Models/BingMapsSearchProvider.ts delete mode 100644 lib/Models/SearchProvider.ts rename lib/Models/{ => SearchProvider}/AustralianGazetteerSearchProvider.ts (84%) create mode 100644 lib/Models/SearchProvider/BingMapsSearchProvider.ts rename lib/Models/{ => SearchProvider}/CatalogSearchProvider.ts (89%) create mode 100644 lib/Models/SearchProvider/SearchProviderFactory.ts rename lib/Models/{ => SearchProvider}/SearchProviderResults.ts (74%) rename lib/Models/{ => SearchProvider}/SearchResult.ts (87%) create mode 100644 lib/Models/SearchProvider/StubSearchProvider.ts create mode 100644 lib/Models/SearchProvider/createStubSearchProvider.ts create mode 100644 lib/Models/SearchProvider/registerSearchProviders.ts create mode 100644 lib/Models/SearchProvider/upsertSearchProviderFromJson.ts delete mode 100644 lib/Models/WebFeatureServiceSearchProvider.ts create mode 100644 lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts create mode 100644 lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts create mode 100644 lib/Traits/SearchProvider/LocationSearchProviderTraits.ts create mode 100644 lib/Traits/SearchProvider/SearchProviderTraits.ts create mode 100644 lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts delete mode 100644 lib/ViewModels/CatalogItemNameSearchProviderViewModel.js delete mode 100644 lib/ViewModels/GazetteerSearchProviderViewModel.js delete mode 100644 lib/ViewModels/GnafSearchProviderViewModel.js delete mode 100644 lib/ViewModels/NominatimSearchProviderViewModel.js delete mode 100644 test/ViewModels/CatalogItemNameSearchProviderViewModelSpec.js diff --git a/architecture/0007-configurable-search-providers.md b/architecture/0007-configurable-search-providers.md new file mode 100644 index 00000000000..87fe3660d02 --- /dev/null +++ b/architecture/0007-configurable-search-providers.md @@ -0,0 +1,35 @@ +# 7. Configuration of search providers + +Date: 2021-01-19 + +## Status + +Proposed + +## Context + +Ticket. +https://github.com/TerriaJS/terriajs/issues/5141. + +### Intro + +The existing approach to the definition of SearchProviders requires the development team's involvement and rebuild of the application, which can be undesired behavior in highly dynamic environments. +It's much better to enable the administrators to maintain the search providers. + +## Proposal +- SearchProviders could greatly use the benefits of the new model system used for Catalog. +- Create a simple base Mixin (`SearchProviderMixin`) to attach SearchProviders to the Model system and enable easier creation of new search providers. +- Make SearchProviders configurable from `config.json`. +- Provide sensible defaults for everything. +- Typescript everything. +- Make everything translateable (administrator can specify i18next keys for all names) + +## Benefits + +- Much easier to implement new search providers. +- Much easier to update existing search providers, `urls` and `keys`. +- Offer administrators an option to decide wheter they want to load group members using `CatalogSearchProvider`. + +## Consequences + +This is quite a large change and should be thoroughly tested to avoid the possible bugs in the search providers migration. \ No newline at end of file diff --git a/lib/ModelMixins/SearchProviderMixin.ts b/lib/ModelMixins/SearchProviderMixin.ts new file mode 100644 index 00000000000..b53a05bd2e3 --- /dev/null +++ b/lib/ModelMixins/SearchProviderMixin.ts @@ -0,0 +1,92 @@ +import { action, observable } from "mobx"; +import { fromPromise } from "mobx-utils"; +import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; +import CesiumMath from "terriajs-cesium/Source/Core/Math"; +import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; +import Constructor from "../Core/Constructor"; +import Model from "../Models/Model"; +import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; +import Terria from "../Models/Terria"; +import SearchProviderTraits from "../Traits/SearchProvider/SearchProviderTraits"; + +type SearchProvider = Model; + +function SearchProviderMixin>(Base: T) { + abstract class SearchProviderMixin extends Base { + abstract get type(): string; + @observable name = "Unknown"; + @observable isOpen = this.openByDefault; + + @action + toggleOpen() { + this.isOpen = !this.isOpen; + } + + @action + search(searchText: string): SearchProviderResults { + const result = new SearchProviderResults(Base); + result.resultsCompletePromise = fromPromise( + this.doSearch(searchText, result) + ); + return result; + } + + protected abstract doSearch( + searchText: string, + results: SearchProviderResults + ): Promise; + + shouldRunSearch(searchText: string) { + if ( + searchText === undefined || + /^\s*$/.test(searchText) || + (this.minCharacters && searchText.length < this.minCharacters) || + (this.minCharacters === undefined && + searchText.length < + this.terria.configParameters.searchBar.minCharacters) + ) { + return false; + } + return true; + } + + get hasSearchProviderMixin() { + return true; + } + } + return SearchProviderMixin; +} + +namespace SearchProviderMixin { + export interface SearchProviderMixin + extends InstanceType> {} + export function isMixedInto(model: any): model is SearchProviderMixin { + return model && model.hasSearchProviderMixin; + } +} + +export default SearchProviderMixin; + +interface MapCenter { + longitude: number; + latitude: number; +} + +export function getMapCenter(terria: Terria): MapCenter { + const view = terria.currentViewer.getCurrentCameraView(); + if (view.position !== undefined) { + const cameraPositionCartographic = Ellipsoid.WGS84.cartesianToCartographic( + view.position + ); + return { + longitude: CesiumMath.toDegrees(cameraPositionCartographic.longitude), + latitude: CesiumMath.toDegrees(cameraPositionCartographic.latitude) + }; + } else { + const center = Rectangle.center(view.rectangle); + return { + longitude: CesiumMath.toDegrees(center.longitude), + latitude: CesiumMath.toDegrees(center.latitude) + }; + } +} diff --git a/lib/ModelMixins/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/WebFeatureServiceSearchProviderMixin.ts new file mode 100644 index 00000000000..70a0c26bf9e --- /dev/null +++ b/lib/ModelMixins/WebFeatureServiceSearchProviderMixin.ts @@ -0,0 +1,222 @@ +import i18next from "i18next"; +import { runInAction } from "mobx"; +import Resource from "terriajs-cesium/Source/Core/Resource"; +import URI from "urijs"; +import Constructor from "../Core/Constructor"; +import makeRealPromise from "../Core/makeRealPromise"; +import zoomRectangleFromPoint from "../Map/zoomRectangleFromPoint"; +import Model from "../Models/Model"; +import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; +import SearchResult from "../Models/SearchProvider/SearchResult"; +import Terria from "../Models/Terria"; +import xml2json from "../ThirdParty/xml2json"; +import WebFeatureServiceSearchProviderTraits from "../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; +import SearchProviderMixin from "./SearchProviderMixin"; + +function WebFeatureServiceSearchProviderMixin< + T extends Constructor> +>(Base: T) { + abstract class WebFeatureServiceSearchProviderMixin extends SearchProviderMixin( + Base + ) { + protected abstract featureToSearchResultFunction: ( + feature: any + ) => SearchResult; + protected abstract transformSearchText: + | ((searchText: string) => string) + | undefined; + protected abstract searchResultFilterFunction: + | ((feature: any) => boolean) + | undefined; + protected abstract searchResultScoreFunction: + | ((feature: any, searchText: string) => number) + | undefined; + + cancelRequest?: () => void; + + private _waitingForResults: boolean = false; + + getXml(url: string): Promise { + const resource = new Resource({ url }); + this._waitingForResults = true; + const xmlPromise = resource.fetchXML(); + this.cancelRequest = resource.request.cancelFunction; + return makeRealPromise(xmlPromise).finally(() => { + this._waitingForResults = false; + }); + } + + protected doSearch( + searchText: string, + results: SearchProviderResults + ): Promise { + results.results.length = 0; + results.message = undefined; + + if (this._waitingForResults) { + // There's been a new search! Cancel the previous one. + if (this.cancelRequest !== undefined) { + this.cancelRequest(); + this.cancelRequest = undefined; + } + this._waitingForResults = false; + } + + const originalSearchText = searchText; + + searchText = searchText.trim(); + if (this.transformSearchText !== undefined) { + searchText = this.transformSearchText(searchText); + } + if (searchText.length < 2) { + return Promise.resolve(); + } + + // Support for matchCase="false" is patchy, but we try anyway + const filter = ` + ${this.searchPropertyName} + *${searchText}*`; + + const _wfsServiceUrl = new URI(this.url); + _wfsServiceUrl.setSearch({ + service: "WFS", + request: "GetFeature", + typeName: this.searchPropertyTypeName, + version: "1.1.0", + srsName: "urn:ogc:def:crs:EPSG::4326", // srsName must be formatted like this for correct lat/long order >:( + filter: filter + }); + + return this.getXml(_wfsServiceUrl.toString()) + .then((xml: any) => { + let json: any = xml2json(xml); + let features: any[]; + if (json === undefined) { + results.message = i18next.t("viewModels.searchErrorOccurred"); + return; + } + + if (json.member !== undefined) { + features = json.member; + } else if (json.featureMember !== undefined) { + features = json.featureMember; + } else { + results.message = i18next.t("viewModels.searchNoPlaceNames"); + return; + } + + // if there's only one feature, make it an array + if (!Array.isArray(features)) { + features = [features]; + } + + const resultSet = new Set(); + + runInAction(() => { + if (this.searchResultFilterFunction !== undefined) { + features = features.filter(this.searchResultFilterFunction); + } + + if (features.length === 0) { + results.message = i18next.t("viewModels.searchNoPlaceNames"); + return; + } + + if (this.searchResultScoreFunction !== undefined) { + features = features.sort( + (featureA, featureB) => + this.searchResultScoreFunction!( + featureB, + originalSearchText + ) - + this.searchResultScoreFunction!(featureA, originalSearchText) + ); + } + + let searchResults = features + .map(this.featureToSearchResultFunction) + .map(result => { + result.clickAction = createZoomToFunction( + this, + result.location + ); + return result; + }); + + // If we don't have a scoring function, sort the search results now + // We can't do this earlier because we don't know what the schema of the unprocessed feature looks like + if (this.searchResultScoreFunction === undefined) { + // Put shorter results first + // They have a larger percentage of letters that the user actually typed in them + searchResults = searchResults.sort( + (featureA, featureB) => + featureA.name.length - featureB.name.length + ); + } + + // Remove results that have the same name and are close to each other + searchResults = searchResults.filter(result => { + const hash = `${result.name},${result.location?.latitude.toFixed( + 1 + )},${result.location?.longitude.toFixed(1)}`; + if (resultSet.has(hash)) { + return false; + } + resultSet.add(hash); + return true; + }); + + // append new results to all results + results.results.push(...searchResults); + }); + }) + .catch(e => { + if (results.isCanceled) { + // A new search has superseded this one, so ignore the result. + return; + } + results.message = i18next.t("viewModels.searchErrorOccurred"); + }); + } + get isWebFeatureServiceSearchProviderMixin() { + return true; + } + } + + return WebFeatureServiceSearchProviderMixin; +} + +namespace WebFeatureServiceSearchProviderMixin { + export interface WebFeatureServiceSearchProviderMixin + extends InstanceType< + ReturnType + > {} + export function isMixedInto( + model: any + ): model is WebFeatureServiceSearchProviderMixin { + return model && model.isWebFeatureServiceSearchProviderMixin; + } +} +export default WebFeatureServiceSearchProviderMixin; + +function createZoomToFunction( + model: WebFeatureServiceSearchProviderMixin.WebFeatureServiceSearchProviderMixin, + location: any +) { + // Server does not return information of a bounding box, just a location. + // bboxSize is used to expand a point + var bboxSize = 0.2; + var rectangle = zoomRectangleFromPoint( + location.latitude, + location.longitude, + bboxSize + ); + + const flightDurationSeconds: number = + model.flightDurationSeconds || + model.terria.configParameters.searchBar.flightDurationSeconds; + + return function() { + model.terria.currentViewer.zoomTo(rectangle, flightDurationSeconds); + }; +} diff --git a/lib/Models/BingMapsSearchProvider.ts b/lib/Models/BingMapsSearchProvider.ts deleted file mode 100644 index 4acf5b53bd7..00000000000 --- a/lib/Models/BingMapsSearchProvider.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { observable, runInAction } from "mobx"; -import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; -import defined from "terriajs-cesium/Source/Core/defined"; -import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; -import CesiumMath from "terriajs-cesium/Source/Core/Math"; -import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; -import Resource from "terriajs-cesium/Source/Core/Resource"; -import loadJsonp from "../Core/loadJsonp"; -import SearchProvider from "./SearchProvider"; -import SearchResult from "./SearchResult"; -import Terria from "./Terria"; -import SearchProviderResults from "./SearchProviderResults"; -import i18next from "i18next"; - -interface BingMapsSearchProviderOptions { - terria: Terria; - url?: string; - key?: string; - flightDurationSeconds?: number; - primaryCountry?: string; - culture?: string; -} - -export default class BingMapsSearchProvider extends SearchProvider { - readonly terria: Terria; - @observable url: string; - @observable key: string | undefined; - @observable flightDurationSeconds: number; - @observable primaryCountry: string; - @observable culture: string; - - constructor(options: BingMapsSearchProviderOptions) { - super(); - - this.terria = options.terria; - this.name = i18next.t("viewModels.searchLocations"); - this.url = defaultValue(options.url, "https://dev.virtualearth.net/"); - if (this.url.length > 0 && this.url[this.url.length - 1] !== "/") { - this.url += "/"; - } - this.key = options.key; - this.flightDurationSeconds = defaultValue( - options.flightDurationSeconds, - 1.5 - ); - this.primaryCountry = defaultValue(options.primaryCountry, "Australia"); - this.culture = defaultValue(options.culture, "en-au"); - - if (!this.key) { - console.warn( - "The " + - this.name + - " 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 doSearch( - searchText: string, - searchResults: SearchProviderResults - ): Promise { - searchResults.results.length = 0; - searchResults.message = undefined; - - if (searchText === undefined || /^\s*$/.test(searchText)) { - return Promise.resolve(); - } - - this.terria.analytics.logEvent("search", "bing", searchText); - - let longitudeDegrees; - let latitudeDegrees; - - const view = this.terria.currentViewer.getCurrentCameraView(); - if (view.position !== undefined) { - const cameraPositionCartographic = Ellipsoid.WGS84.cartesianToCartographic( - view.position - ); - longitudeDegrees = CesiumMath.toDegrees( - cameraPositionCartographic.longitude - ); - latitudeDegrees = CesiumMath.toDegrees( - cameraPositionCartographic.latitude - ); - } else { - const center = Rectangle.center(view.rectangle); - longitudeDegrees = CesiumMath.toDegrees(center.longitude); - latitudeDegrees = CesiumMath.toDegrees(center.latitude); - } - - const promise: Promise = loadJsonp( - new Resource({ - url: - this.url + - "REST/v1/Locations?culture=" + - this.culture + - "&userLocation=" + - latitudeDegrees + - "," + - longitudeDegrees, - queryParameters: { - query: searchText, - key: this.key - } - }), - "jsonp" - ); - - 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 = i18next.t("viewModels.searchNoLocations"); - return; - } - - var resourceSet = result.resourceSets[0]; - if (resourceSet.resources.length === 0) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); - return; - } - - 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 < resourceSet.resources.length; ++i) { - const resource = resourceSet.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] - } - }) - ); - } - - runInAction(() => { - searchResults.results.push(...primaryCountryLocations); - searchResults.results.push(...otherLocations); - }); - - if (searchResults.results.length === 0) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); - } - }) - .catch(() => { - if (searchResults.isCanceled) { - // A new search has superseded this one, so ignore the result. - return; - } - - searchResults.message = i18next.t("viewModels.searchErrorOccurred"); - }); - } -} - -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); - }; -} diff --git a/lib/Models/SearchProvider.ts b/lib/Models/SearchProvider.ts deleted file mode 100644 index de29a899952..00000000000 --- a/lib/Models/SearchProvider.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { action, observable } from "mobx"; -import { fromPromise } from "mobx-utils"; -import SearchProviderResults from "./SearchProviderResults"; - -export default abstract class SearchProvider { - @observable name = "Unknown"; - @observable isOpen = true; - - @action - toggleOpen() { - this.isOpen = !this.isOpen; - } - - @action - search(searchText: string): SearchProviderResults { - const result = new SearchProviderResults(this); - result.resultsCompletePromise = fromPromise( - this.doSearch(searchText, result) - ); - return result; - } - - protected abstract doSearch( - searchText: string, - results: SearchProviderResults - ): Promise; -} diff --git a/lib/Models/AustralianGazetteerSearchProvider.ts b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts similarity index 84% rename from lib/Models/AustralianGazetteerSearchProvider.ts rename to lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts index b0f352ffdea..9d0dbf50954 100644 --- a/lib/Models/AustralianGazetteerSearchProvider.ts +++ b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts @@ -1,7 +1,7 @@ -import i18next from "i18next"; +import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; +import CreateModel from "../CreateModel"; +import WebFeatureServiceSearchProviderMixin from "./../../ModelMixins/WebFeatureServiceSearchProviderMixin"; import SearchResult from "./SearchResult"; -import Terria from "./Terria"; -import WebFeatureServiceSearchProvider from "./WebFeatureServiceSearchProvider"; const featureCodesToNamesMap = new Map([ ["AF", "Aviation"], @@ -220,23 +220,25 @@ const searchResultScoreFunction = function( return score; }; -const WFS_SERVICE_URL = - "http://services.ga.gov.au/gis/services/Australian_Gazetteer/MapServer/WFSServer"; -const SEARCH_PROPERTY_NAME = "Australian_Gazetteer:NameU"; -const SEARCH_PROPERTY_TYPE_NAME = "Australian_Gazetteer:Gazetteer_of_Australia"; - -export default function createAustralianGazetteerSearchProvider( - terria: Terria +export default class AustralianGazetteerSearchProvider extends WebFeatureServiceSearchProviderMixin( + CreateModel(WebFeatureServiceSearchProviderTraits) ) { - return new WebFeatureServiceSearchProvider({ - terria, - featureToSearchResultFunction, - wfsServiceUrl: WFS_SERVICE_URL, - searchPropertyName: SEARCH_PROPERTY_NAME, - searchPropertyTypeName: SEARCH_PROPERTY_TYPE_NAME, - transformSearchText: searchText => searchText.toUpperCase(), - name: i18next.t("viewModels.searchPlaceNames"), - searchResultFilterFunction: searchResultFilterFunction, - searchResultScoreFunction: searchResultScoreFunction - }); + static readonly type = "australian-gazetteer-search-provider"; + + get type(){ + return AustralianGazetteerSearchProvider.type; + } + + featureToSearchResultFunction: ( + feature: any + ) => SearchResult = featureToSearchResultFunction; + transformSearchText: + | ((searchText: string) => string) + | undefined = searchText => searchText.toUpperCase(); + searchResultFilterFunction: + | ((feature: any) => boolean) + | undefined = searchResultFilterFunction; + searchResultScoreFunction: + | ((feature: any, searchText: string) => number) + | undefined = searchResultScoreFunction; } diff --git a/lib/Models/SearchProvider/BingMapsSearchProvider.ts b/lib/Models/SearchProvider/BingMapsSearchProvider.ts new file mode 100644 index 00000000000..312c873c5e8 --- /dev/null +++ b/lib/Models/SearchProvider/BingMapsSearchProvider.ts @@ -0,0 +1,189 @@ +import i18next from "i18next"; +import { runInAction } from "mobx"; +import defined from "terriajs-cesium/Source/Core/defined"; +import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; +import Resource from "terriajs-cesium/Source/Core/Resource"; +import loadJsonp from "../../Core/loadJsonp"; +import SearchProviderMixin, { + getMapCenter +} from "../../ModelMixins/SearchProviderMixin"; +import BingMapsSearchProviderTraits from "../../Traits/SearchProvider/BingMapsSearchProviderTraits"; +import CreateModel from "../CreateModel"; +import SearchProviderResults from "./SearchProviderResults"; +import SearchResult from "./SearchResult"; +import CommonStrata from "./../CommonStrata"; +import Terria from "../Terria"; + +export default class BingMapsSearchProvider extends SearchProviderMixin( + CreateModel(BingMapsSearchProviderTraits) +) { + static readonly type = "bing-maps-search-provider"; + + get type() { + return BingMapsSearchProvider.type; + } + + constructor(uniqueId: string | undefined, terria: Terria) { + super(uniqueId, terria); + if ( + (!this.key || this.key === "") && + this.terria.configParameters.bingMapsKey + ) { + this.setTrait( + CommonStrata.defaults, + "key", + this.terria.configParameters.bingMapsKey + ); + } + this.showWarning(); + } + + showWarning() { + if (!this.key || this.key === "") { + console.warn( + "The " + + this.name + + " 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 doSearch( + searchText: string, + searchResults: SearchProviderResults + ): Promise { + console.log(this.key); + searchResults.results.length = 0; + searchResults.message = undefined; + + if (this.shouldRunSearch(searchText)) { + return Promise.resolve(); + } + + this.terria.analytics.logEvent("search", "bing", searchText); + + const searchQuery = new Resource({ + url: this.url + "REST/v1/Locations", + queryParameters: { + culture: this.culture, + query: searchText, + key: this.key + } + }); + + if (this.mapCenter) { + const mapCenter = getMapCenter(this.terria); + + searchQuery.appendQueryParameters({ + userLocation: `${mapCenter.latitude}, ${mapCenter.longitude}` + }); + } + + const promise: Promise = loadJsonp(searchQuery, "jsonp"); + + 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 = i18next.t("viewModels.searchNoLocations"); + return; + } + + var resourceSet = result.resourceSets[0]; + if (resourceSet.resources.length === 0) { + searchResults.message = i18next.t("viewModels.searchNoLocations"); + return; + } + + const locations = this.sortByPriority(resourceSet.resources); + + runInAction(() => { + searchResults.results.push(...locations.primaryCountry); + searchResults.results.push(...locations.other); + }); + + if (searchResults.results.length === 0) { + searchResults.message = i18next.t("viewModels.searchNoLocations"); + } + }) + .catch(() => { + if (searchResults.isCanceled) { + // A new search has superseded this one, so ignore the result. + return; + } + + searchResults.message = i18next.t("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 flightDurationSeconds: number = + model.flightDurationSeconds || + model.terria.configParameters.searchBar.flightDurationSeconds; + + const terria = model.terria; + terria.currentViewer.zoomTo(rectangle, flightDurationSeconds); + }; +} diff --git a/lib/Models/CatalogSearchProvider.ts b/lib/Models/SearchProvider/CatalogSearchProvider.ts similarity index 89% rename from lib/Models/CatalogSearchProvider.ts rename to lib/Models/SearchProvider/CatalogSearchProvider.ts index 07dc5658a50..1a1e85c9ba2 100644 --- a/lib/Models/CatalogSearchProvider.ts +++ b/lib/Models/SearchProvider/CatalogSearchProvider.ts @@ -1,14 +1,12 @@ import { autorun, observable, runInAction } from "mobx"; -import SearchProvider from "./SearchProvider"; -import SearchResult from "./SearchResult"; -import Terria from "./Terria"; +import GroupMixin from "../../ModelMixins/GroupMixin"; +import ReferenceMixin from "../../ModelMixins/ReferenceMixin"; +import CatalogSearchProviderTraits from "../../Traits/SearchProvider/CatalogSearchProviderTraits"; +import CreateModel from "../CreateModel"; +import Terria from "../Terria"; +import SearchProviderMixin from "./../../ModelMixins/SearchProviderMixin"; import SearchProviderResults from "./SearchProviderResults"; -import GroupMixin from "../ModelMixins/GroupMixin"; -import ReferenceMixin from "../ModelMixins/ReferenceMixin"; - -interface CatalogSearchProviderOptions { - terria: Terria; -} +import SearchResult from "./SearchResult"; type UniqueIdString = string; type ResultMap = Map; @@ -95,18 +93,18 @@ export function loadAndSearchCatalogRecursively( }); } -export default class CatalogSearchProvider extends SearchProvider { - readonly terria: Terria; - @observable isSearching: boolean = false; - @observable debounceDurationOnceLoaded: number = 300; - - constructor(options: CatalogSearchProviderOptions) { - super(); +export default class CatalogSearchProvider extends SearchProviderMixin( + CreateModel(CatalogSearchProviderTraits) +) { + static readonly type = "catalog-search-provider"; - this.terria = options.terria; - this.name = "Catalog Items"; + get type() { + return CatalogSearchProvider.type; } + @observable isSearching: boolean = false; + @observable debounceDurationOnceLoaded: number = 300; + protected doSearch( searchText: string, searchResults: SearchProviderResults diff --git a/lib/Models/SearchProvider/SearchProviderFactory.ts b/lib/Models/SearchProvider/SearchProviderFactory.ts new file mode 100644 index 00000000000..cc9058c0c6a --- /dev/null +++ b/lib/Models/SearchProvider/SearchProviderFactory.ts @@ -0,0 +1,4 @@ +import ModelFactory from "../ModelFactory"; + +const SearchProviderFactory = new ModelFactory(); +export default SearchProviderFactory; diff --git a/lib/Models/SearchProviderResults.ts b/lib/Models/SearchProvider/SearchProviderResults.ts similarity index 74% rename from lib/Models/SearchProviderResults.ts rename to lib/Models/SearchProvider/SearchProviderResults.ts index 92ddfd1ddca..751e7f7cd60 100644 --- a/lib/Models/SearchProviderResults.ts +++ b/lib/Models/SearchProvider/SearchProviderResults.ts @@ -1,7 +1,7 @@ import { observable } from "mobx"; import SearchResult from "./SearchResult"; import { IPromiseBasedObservable, fromPromise } from "mobx-utils"; -import SearchProvider from "./SearchProvider"; +import SearchProviderMixin from "./../../ModelMixins/SearchProviderMixin"; export default class SearchProviderResults { @observable results: SearchResult[] = []; @@ -11,7 +11,9 @@ export default class SearchProviderResults { Promise.resolve() ); - constructor(readonly searchProvider: SearchProvider) {} + constructor( + readonly searchProvider: SearchProviderMixin.SearchProviderMixin + ) {} get isSearching() { return this.resultsCompletePromise.state === "pending"; diff --git a/lib/Models/SearchResult.ts b/lib/Models/SearchProvider/SearchResult.ts similarity index 87% rename from lib/Models/SearchResult.ts rename to lib/Models/SearchProvider/SearchResult.ts index 6b7a2c906ce..39dbbb7e251 100644 --- a/lib/Models/SearchResult.ts +++ b/lib/Models/SearchProvider/SearchResult.ts @@ -1,9 +1,9 @@ -import { BaseModel } from "./Model"; -import { observable, action } from "mobx"; +import { action, observable } from "mobx"; import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; import defined from "terriajs-cesium/Source/Core/defined"; -import raiseErrorOnRejectedPromise from "./raiseErrorOnRejectedPromise"; -import GroupMixin from "../ModelMixins/GroupMixin"; +import GroupMixin from "../../ModelMixins/GroupMixin"; +import { BaseModel } from "../Model"; +import raiseErrorOnRejectedPromise from "../raiseErrorOnRejectedPromise"; export interface SearchResultOptions { name?: string; diff --git a/lib/Models/SearchProvider/StubSearchProvider.ts b/lib/Models/SearchProvider/StubSearchProvider.ts new file mode 100644 index 00000000000..50b19adb3db --- /dev/null +++ b/lib/Models/SearchProvider/StubSearchProvider.ts @@ -0,0 +1,31 @@ +import LocationSearchProviderTraits from "./../../Traits/SearchProvider/LocationSearchProviderTraits"; +import primitiveTrait from "./../../Traits/primitiveTrait"; +import SearchProviderMixin from "../../ModelMixins/SearchProviderMixin"; +import CreateModel from "../CreateModel"; +import SearchProviderResults from "./SearchProviderResults"; + +export class StubSearchProviderTraits extends LocationSearchProviderTraits { + @primitiveTrait({ + type: "boolean", + name: "Is experiencing issues", + description: + "Whether the search provider is experiencing issues which may cause search results to be unavailable" + }) + isExperiencingIssues: boolean = true; +} + +export default class StubSearchProvider extends SearchProviderMixin( + CreateModel(StubSearchProviderTraits) +) { + static readonly type = "stub-search-provider"; + get type(): string { + return StubSearchProvider.type; + } + + protected doSearch( + searchText: string, + results: SearchProviderResults + ): Promise { + return Promise.resolve(); + } +} diff --git a/lib/Models/SearchProvider/createStubSearchProvider.ts b/lib/Models/SearchProvider/createStubSearchProvider.ts new file mode 100644 index 00000000000..76434e11ba8 --- /dev/null +++ b/lib/Models/SearchProvider/createStubSearchProvider.ts @@ -0,0 +1,27 @@ +import Terria from "./../Terria"; +import StubSearchProvider from "./StubSearchProvider"; +import CommonStrata from "./../CommonStrata"; +import { BaseModel } from "../Model"; + +const getUniqueStubSearchProviderName = (terria: Terria) => { + const stubName = "[StubSearchProvider]"; + let uniqueId = stubName; + let idIncrement = 1; + while (terria.getModelById(BaseModel, uniqueId) !== undefined) { + uniqueId = stubName + " (" + idIncrement + ")"; + idIncrement++; + } + return uniqueId; +}; + +export default function createStubSearchProvider( + terria: Terria, + uniqueId?: string +): StubSearchProvider { + const idToUse = uniqueId || getUniqueStubSearchProviderName(terria); + const stub = new StubSearchProvider(idToUse, terria); + + stub.setTrait(CommonStrata.underride, "name", stub.uniqueId); + terria.addSearchProvider(stub); + return stub; +} diff --git a/lib/Models/SearchProvider/registerSearchProviders.ts b/lib/Models/SearchProvider/registerSearchProviders.ts new file mode 100644 index 00000000000..919a6da697c --- /dev/null +++ b/lib/Models/SearchProvider/registerSearchProviders.ts @@ -0,0 +1,15 @@ +import BingMapsSearchProvider from "./BingMapsSearchProvider"; +import AustralianGazetteerSearchProvider from "./AustralianGazetteerSearchProvider"; +import SearchProviderFactory from "./SearchProviderFactory"; + +export default function registerSearchProviders() { + SearchProviderFactory.register( + BingMapsSearchProvider.type, + BingMapsSearchProvider + ); + + SearchProviderFactory.register( + AustralianGazetteerSearchProvider.type, + AustralianGazetteerSearchProvider + ); +} diff --git a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts new file mode 100644 index 00000000000..c52dca54b74 --- /dev/null +++ b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts @@ -0,0 +1,57 @@ +import Terria from "./../Terria"; +import ModelFactory from "./../ModelFactory"; +import { BaseModel } from "../Model"; +import i18next from "i18next"; +import TerriaError from "../../Core/TerriaError"; +import CommonStrata from "./../CommonStrata"; +import updateModelFromJson from "../updateModelFromJson"; +import createStubSearchProvider from "./createStubSearchProvider"; + +export default function upsertSearchProviderFromJson( + factory: ModelFactory, + terria: Terria, + stratumName: string, + json: any +) { + let uniqueId = json.id; + if (uniqueId === undefined) { + const id = json.localId || json.name; + if (id === undefined) { + throw new TerriaError({ + title: i18next.t("models.catalog.idForMatchingErrorTitle"), + message: i18next.t("models.catalog.idForMatchingErrorMessage") + }); + } + uniqueId = id; + } + + let model = terria.getModelById(BaseModel, uniqueId); + + if (model === undefined) { + model = factory.create(json.type, uniqueId, terria); + if (model === undefined) { + console.log( + new TerriaError({ + title: i18next.t("models.catalog.unsupportedTypeTitle"), + message: i18next.t("models.catalog.unsupportedTypeMessage", { + type: json.type + }) + }) + ); + model = createStubSearchProvider(terria, uniqueId); + const stub = model; + stub.setTrait(CommonStrata.underride, "isExperiencingIssues", true); + stub.setTrait(CommonStrata.override, "name", `${uniqueId} (Stub)`); + } + + model?.terria.addSearchProvider(model); + } + + try { + updateModelFromJson(model, stratumName, json); + } catch (error) { + console.log(`Error updating search provider from JSON`); + console.log(error); + model?.setTrait(CommonStrata.underride, "isExperiencingIssues", true); + } +} diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index a14b7795f26..899e0ced372 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -1,6 +1,14 @@ import { convertCatalog, convertShare } from "catalog-converter"; import i18next from "i18next"; -import { action, computed, observable, runInAction, toJS, when } from "mobx"; +import { + action, + computed, + isObservableArray, + observable, + runInAction, + toJS, + when +} from "mobx"; import { createTransformer } from "mobx-utils"; import Clock from "terriajs-cesium/Source/Core/Clock"; import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; @@ -72,6 +80,8 @@ import Mappable, { isDataSource } from "./Mappable"; import { BaseModel } from "./Model"; import NoViewer from "./NoViewer"; import openGroup from "./openGroup"; +import SearchProviderFactory from "./SearchProvider/SearchProviderFactory"; +import upsertSearchProviderFromJson from "./SearchProvider/upsertSearchProviderFromJson"; import ShareDataService from "./ShareDataService"; import SplitItemReference from "./SplitItemReference"; import TimelineStack from "./TimelineStack"; @@ -117,7 +127,50 @@ interface ConfigParameters { helpContent?: HelpContentItem[]; helpContentTerms?: Term[]; languageConfiguration?: LanguageConfiguration; + /** + * Index of which brandBarElements to show for mobile header + */ displayOneBrand?: number; + /** + * The search bar allows requesting information from various search services at once. + */ + searchBar: SearchBar; +} + +interface SearchBar { + /** + * Input text field placeholder shown when no input has been given yet. The string is translateable. + * @default "search.placeholder" + */ + placeholder: string; + /** + * Maximum amount of entries in the suggestion list. + * @default 5 + */ + recommendedListLength: number; + /** + * Defines whether search results are to be sorted alphanumerically. + * @default true + */ + sortByName: boolean; + /** + * The duration of the camera flight to an entered location, in seconds. + * @default 1.5 + */ + flightDurationSeconds: number; + /** + * True if the geocoder should query as the user types to autocomplete. + * @default true + */ + autocomplete: boolean; + /** + * Minimum number of characters to start search. + */ + minCharacters: number; + /** + * Array of search providers to be used. + */ + searchProviders: any[]; } interface StartOptions { @@ -160,6 +213,7 @@ interface HomeCameraInit { export default class Terria { private models = observable.map(); + private locationSearchProviders = observable.map(); /** Map from share key -> id */ readonly shareKeysMap = observable.map(); /** Map from id -> share keys */ @@ -253,7 +307,35 @@ export default class Terria { helpContent: [], helpContentTerms: defaultTerms, languageConfiguration: undefined, - displayOneBrand: 0 // index of which brandBarElements to show for mobile header + displayOneBrand: 0, + searchBar: { + placeholder: "search.placeholder", + recommendedListLength: 5, + sortByName: true, + flightDurationSeconds: 1.5, + autocomplete: true, + minCharacters: 3, + searchProviders: [ + { + id: "search-provider/bing-maps", + type: "bing-maps-search-provider", + name: "search.bingMaps", + url: "https://dev.virtualearth.net/", + flightDurationSeconds: 1.5 + }, + { + id: "search-provider/australian-gazetteer", + type: "australian-gazetteer-search-provider", + name: "viewModels.searchPlaceNames", + url: + "http://services.ga.gov.au/gis/services/Australian_Gazetteer/MapServer/WFSServer", + searchPropertyName: "Australian_Gazetteer:NameU", + searchPropertyTypeName: "Australian_Gazetteer:Gazetteer_of_Australia", + flightDurationSeconds: 1.5, + minCharacters: 3 + } + ] + } }; @observable @@ -401,6 +483,32 @@ export default class Terria { shareKeys?.forEach(shareKey => this.addShareKey(model.uniqueId!, shareKey)); } + /** + * Add new SearchProvider to the list of SearchProviders. + */ + @action + addSearchProvider(model: BaseModel) { + if (model.uniqueId === undefined) { + throw new DeveloperError( + "A SearchProvider without a `uniqueId` cannot be added." + ); + } + + if (this.locationSearchProviders.has(model.uniqueId)) { + throw new RuntimeError( + "A SearchProvider with the specified ID already exists." + ); + } + + this.locationSearchProviders.set(model.uniqueId, model); + } + + get locationSearchProvidersArray() { + return [...this.locationSearchProviders.entries()].map(function(entry) { + return entry[1]; + }); + } + /** * Remove references to a model from Terria. */ @@ -533,6 +641,23 @@ export default class Terria { return this.updateApplicationUrl(options.applicationUrl.href); } }) + .then(() => { + let searchProviders = this.configParameters.searchBar.searchProviders; + if (!isObservableArray(searchProviders)) + throw new TerriaError({ + sender: SearchProviderFactory, + title: "SearchProviders", + message: "" + }); + searchProviders.forEach(searchProvider => { + upsertSearchProviderFromJson( + SearchProviderFactory, + this, + CommonStrata.definition, + searchProvider + ); + }); + }) .then(() => { this.loadPersistedMapSettings(); }); diff --git a/lib/Models/WebFeatureServiceSearchProvider.ts b/lib/Models/WebFeatureServiceSearchProvider.ts deleted file mode 100644 index 4b63b25328f..00000000000 --- a/lib/Models/WebFeatureServiceSearchProvider.ts +++ /dev/null @@ -1,221 +0,0 @@ -import i18next from "i18next"; -import { runInAction } from "mobx"; -import URI from "urijs"; -import zoomRectangleFromPoint from "../Map/zoomRectangleFromPoint"; -import xml2json from "../ThirdParty/xml2json"; -import SearchProvider from "./SearchProvider"; -import SearchProviderResults from "./SearchProviderResults"; -import SearchResult from "./SearchResult"; -import Terria from "./Terria"; -import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; -import Resource from "terriajs-cesium/Source/Core/Resource"; -import makeRealPromise from "../Core/makeRealPromise"; - -export interface WebFeatureServiceSearchProviderOptions { - /** Base url for the service */ - wfsServiceUrl: string; - /** Which property to look for the search text in */ - searchPropertyName: string; - /** Type of the properties to search */ - searchPropertyTypeName: string; - /** Convert a WFS feature to a search result */ - featureToSearchResultFunction: (feature: any) => SearchResult; - terria: Terria; - /** How long it takes to zoom in when a search result is clicked */ - flightDurationSeconds?: number; - /** Apply a function to search text before it gets passed to the service. Useful for changing case */ - transformSearchText?: (searchText: string) => string; - /** Return true if a feature should be included in search results */ - searchResultFilterFunction?: (feature: any) => boolean; - /** Return a score that gets used to sort results (in descending order) */ - searchResultScoreFunction?: (feature: any, searchText: string) => number; - /** name of the search provider */ - name: string; -} - -export default class WebFeatureServiceSearchProvider extends SearchProvider { - private _wfsServiceUrl: uri.URI; - private _searchPropertyName: string; - private _searchPropertyTypeName: string; - private _featureToSearchResultFunction: (feature: any) => SearchResult; - flightDurationSeconds: number; - readonly terria: Terria; - private _transformSearchText: ((searchText: string) => string) | undefined; - private _searchResultFilterFunction: ((feature: any) => boolean) | undefined; - private _searchResultScoreFunction: - | ((feature: any, searchText: string) => number) - | undefined; - cancelRequest?: () => void; - private _waitingForResults: boolean = false; - - constructor(options: WebFeatureServiceSearchProviderOptions) { - super(); - this._wfsServiceUrl = new URI(options.wfsServiceUrl); - this._searchPropertyName = options.searchPropertyName; - this._searchPropertyTypeName = options.searchPropertyTypeName; - this._featureToSearchResultFunction = options.featureToSearchResultFunction; - this.terria = options.terria; - this.flightDurationSeconds = defaultValue( - options.flightDurationSeconds, - 1.5 - ); - this._transformSearchText = options.transformSearchText; - this._searchResultFilterFunction = options.searchResultFilterFunction; - this._searchResultScoreFunction = options.searchResultScoreFunction; - this.name = options.name; - } - - getXml(): Promise { - const resource = new Resource({ url: this._wfsServiceUrl.toString() }); - this._waitingForResults = true; - const xmlPromise = resource.fetchXML(); - this.cancelRequest = resource.request.cancelFunction; - return makeRealPromise(xmlPromise).finally(() => { - this._waitingForResults = false; - }); - } - - protected doSearch( - searchText: string, - results: SearchProviderResults - ): Promise { - results.results.length = 0; - results.message = undefined; - - if (this._waitingForResults) { - // There's been a new search! Cancel the previous one. - if (this.cancelRequest !== undefined) { - this.cancelRequest(); - this.cancelRequest = undefined; - } - this._waitingForResults = false; - } - - const originalSearchText = searchText; - - searchText = searchText.trim(); - if (this._transformSearchText !== undefined) { - searchText = this._transformSearchText(searchText); - } - if (searchText.length < 2) { - return Promise.resolve(); - } - - // Support for matchCase="false" is patchy, but we try anyway - const filter = ` - ${this._searchPropertyName} - *${searchText}*`; - - this._wfsServiceUrl.setSearch({ - service: "WFS", - request: "GetFeature", - typeName: this._searchPropertyTypeName, - version: "1.1.0", - srsName: "urn:ogc:def:crs:EPSG::4326", // srsName must be formatted like this for correct lat/long order >:( - filter: filter - }); - - return this.getXml() - .then((xml: any) => { - let json: any = xml2json(xml); - let features: any[]; - if (json === undefined) { - results.message = i18next.t("viewModels.searchErrorOccurred"); - return; - } - - if (json.member !== undefined) { - features = json.member; - } else if (json.featureMember !== undefined) { - features = json.featureMember; - } else { - results.message = i18next.t("viewModels.searchNoPlaceNames"); - return; - } - - // if there's only one feature, make it an array - if (!Array.isArray(features)) { - features = [features]; - } - - const resultSet = new Set(); - - runInAction(() => { - if (this._searchResultFilterFunction !== undefined) { - features = features.filter(this._searchResultFilterFunction); - } - - if (features.length === 0) { - results.message = i18next.t("viewModels.searchNoPlaceNames"); - return; - } - - if (this._searchResultScoreFunction !== undefined) { - features = features.sort( - (featureA, featureB) => - this._searchResultScoreFunction!(featureB, originalSearchText) - - this._searchResultScoreFunction!(featureA, originalSearchText) - ); - } - - let searchResults = features - .map(this._featureToSearchResultFunction) - .map(result => { - result.clickAction = createZoomToFunction(this, result.location); - return result; - }); - - // If we don't have a scoring function, sort the search results now - // We can't do this earlier because we don't know what the schema of the unprocessed feature looks like - if (this._searchResultScoreFunction === undefined) { - // Put shorter results first - // They have a larger percentage of letters that the user actually typed in them - searchResults = searchResults.sort( - (featureA, featureB) => - featureA.name.length - featureB.name.length - ); - } - - // Remove results that have the same name and are close to each other - searchResults = searchResults.filter(result => { - const hash = `${result.name},${result.location?.latitude.toFixed( - 1 - )},${result.location?.longitude.toFixed(1)}`; - if (resultSet.has(hash)) { - return false; - } - resultSet.add(hash); - return true; - }); - - // append new results to all results - results.results.push(...searchResults); - }); - }) - .catch(e => { - if (results.isCanceled) { - // A new search has superseded this one, so ignore the result. - return; - } - results.message = i18next.t("viewModels.searchErrorOccurred"); - }); - } -} - -function createZoomToFunction( - model: WebFeatureServiceSearchProvider, - location: any -) { - // Server does not return information of a bounding box, just a location. - // bboxSize is used to expand a point - var bboxSize = 0.2; - var rectangle = zoomRectangleFromPoint( - location.latitude, - location.longitude, - bboxSize - ); - - return function() { - model.terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds); - }; -} diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index 639668806e7..bcd1fe31248 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -1,16 +1,15 @@ -// import CatalogItemNameSearchProviderViewModel from "../ViewModels/CatalogItemNameSearchProviderViewModel"; import { - observable, - reaction, - IReactionDisposer, + action, computed, - action + IReactionDisposer, + observable, + reaction } from "mobx"; -import Terria from "../Models/Terria"; -import SearchProviderResults from "../Models/SearchProviderResults"; -import SearchProvider from "../Models/SearchProvider"; import filterOutUndefined from "../Core/filterOutUndefined"; -import CatalogSearchProvider from "../Models/CatalogSearchProvider"; +import CatalogSearchProvider from "../Models/SearchProvider/CatalogSearchProvider"; +import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; +import Terria from "../Models/Terria"; +import SearchProviderMixin from "./../ModelMixins/SearchProviderMixin"; interface SearchStateOptions { terria: Terria; @@ -20,9 +19,10 @@ interface SearchStateOptions { export default class SearchState { @observable - catalogSearchProvider: SearchProvider | undefined; + catalogSearchProvider: SearchProviderMixin.SearchProviderMixin | undefined; - @observable locationSearchProviders: SearchProvider[]; + @observable + locationSearchProviders: SearchProviderMixin.SearchProviderMixin[]; @observable catalogSearchText: string = ""; @observable isWaitingToStartCatalogSearch: boolean = false; @@ -48,7 +48,7 @@ export default class SearchState { constructor(options: SearchStateOptions) { this.catalogSearchProvider = options.catalogSearchProvider || - new CatalogSearchProvider({ terria: options.terria }); + new CatalogSearchProvider("catalog-search-provider", options.terria); this.locationSearchProviders = options.locationSearchProviders || []; this._catalogSearchDisposer = reaction( diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index 5e0c64f1b94..372ee34aa6e 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -1,20 +1,21 @@ -import React from "react"; +import classNames from "classnames"; import createReactClass from "create-react-class"; +import { runInAction } from "mobx"; +import { observer } from "mobx-react"; import PropTypes from "prop-types"; -import SearchBox from "../Search/SearchBox"; -import MobileModalWindow from "./MobileModalWindow"; -import Branding from "../SidePanel/Branding"; -import Styles from "./mobile-header.scss"; -import Icon, { StyledIcon } from "../Icon"; -import MobileMenu from "./MobileMenu"; -import classNames from "classnames"; -import { removeMarker } from "../../Models/LocationMarkerUtils"; +import React from "react"; import { withTranslation } from "react-i18next"; import { withTheme } from "styled-components"; -import { observer } from "mobx-react"; -import { runInAction } from "mobx"; +import { useTranslationIfExists } from "../../Language/languageHelpers"; +import { removeMarker } from "../../Models/LocationMarkerUtils"; import Box from "../../Styled/Box"; import { RawButton } from "../../Styled/Button"; +import Icon, { StyledIcon } from "../Icon"; +import SearchBox from "../Search/SearchBox"; +import Branding from "../SidePanel/Branding"; +import Styles from "./mobile-header.scss"; +import MobileMenu from "./MobileMenu"; +import MobileModalWindow from "./MobileModalWindow"; const MobileHeader = observer( createReactClass({ @@ -141,10 +142,10 @@ const MobileHeader = observer( render() { const searchState = this.props.viewState.searchState; const displayOne = this.props.terria.configParameters.displayOneBrand; - const { t } = this.props; + const { t, terria } = this.props; const nowViewingLength = - this.props.terria.workbench.items !== undefined - ? this.props.terria.workbench.items.length + terria.workbench.items !== undefined + ? terria.workbench.items.length : 0; return ( @@ -193,7 +194,7 @@ const MobileHeader = observer( /> @@ -240,7 +241,9 @@ const MobileHeader = observer( searchText={searchState.locationSearchText} onSearchTextChanged={this.changeLocationSearchText} onDoSearch={this.searchLocations} - placeholder={t("search.placeholder")} + placeholder={useTranslationIfExists( + terria.configParameters.searchBar.placeholder + )} alwaysShowClear={true} onClear={this.closeLocationSearch} autoFocus={true} @@ -266,13 +269,10 @@ const MobileHeader = observer( menuLeftItems={this.props.menuLeftItems} viewState={this.props.viewState} allBaseMaps={this.props.allBaseMaps} - terria={this.props.terria} - showFeedback={!!this.props.terria.configParameters.feedbackUrl} - /> - + ); } diff --git a/lib/ReactViews/SidePanel/SidePanel.jsx b/lib/ReactViews/SidePanel/SidePanel.jsx index 83b49332499..daa5c7e51aa 100644 --- a/lib/ReactViews/SidePanel/SidePanel.jsx +++ b/lib/ReactViews/SidePanel/SidePanel.jsx @@ -4,13 +4,13 @@ import PropTypes from "prop-types"; import React from "react"; import { withTranslation } from "react-i18next"; import styled, { withTheme } from "styled-components"; +import { useTranslationIfExists } from "../../Language/languageHelpers"; +import { useRefForTerria } from "../Hooks/useRefForTerria"; import Icon, { StyledIcon } from "../Icon"; import SearchBoxAndResults from "../Search/SearchBoxAndResults"; import Workbench from "../Workbench/Workbench"; import FullScreenButton from "./FullScreenButton"; -import { useRefForTerria } from "../Hooks/useRefForTerria"; - import Box from "../../Styled/Box"; import Spacing from "../../Styled/Spacing"; import Text from "../../Styled/Text"; @@ -171,7 +171,9 @@ const SidePanel = observer( diff --git a/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts b/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts new file mode 100644 index 00000000000..f39d28a1227 --- /dev/null +++ b/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts @@ -0,0 +1,35 @@ +import mixTraits from "../mixTraits"; +import primitiveTrait from "../primitiveTrait"; +import LocationSearchProviderTraits, { + SearchProviderMapCenterTraits +} from "./LocationSearchProviderTraits"; + +export default class BingMapsSearchProviderTraits extends mixTraits( + LocationSearchProviderTraits, + SearchProviderMapCenterTraits +) { + url: string = "https://dev.virtualearth.net/"; + + @primitiveTrait({ + type: "string", + name: "Key", + description: "The Bing Maps key." + }) + key?: string; + + @primitiveTrait({ + type: "string", + name: "Primary country", + description: "Name of the country to prioritize the search results." + }) + primaryCountry: string = "Australia"; + + @primitiveTrait({ + type: "string", + name: "Culture", + description: `Use the culture parameter to specify a culture for your request. + The culture parameter provides the result in the language of the culture. + For a list of supported cultures, see [Supported Culture Codes](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes)` + }) + culture: string = "en-au"; +} diff --git a/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts b/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts new file mode 100644 index 00000000000..7808d9d81b4 --- /dev/null +++ b/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts @@ -0,0 +1,14 @@ +import mixTraits from "../mixTraits"; +import SearchProviderTraits from "./SearchProviderTraits"; +import primitiveTrait from "../primitiveTrait"; + +export default class CatalogSearchProviderTraits extends mixTraits( + SearchProviderTraits +) { + @primitiveTrait({ + type: "string", + name: "Name", + description: "Name of the search provider." + }) + name: string = "Catalog items"; +} diff --git a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts new file mode 100644 index 00000000000..b7285613ff5 --- /dev/null +++ b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts @@ -0,0 +1,37 @@ +import ModelTraits from "../ModelTraits"; +import primitiveTrait from "../primitiveTrait"; +import SearchProviderTraits from "./SearchProviderTraits"; + +export default class LocationSearchProviderTraits extends SearchProviderTraits { + @primitiveTrait({ + type: "string", + name: "URL", + description: "The URL of search provider." + }) + url: string = ""; + + @primitiveTrait({ + type: "boolean", + name: "Open by default", + description: + "True if the geocoder should query as the user types to autocomplete." + }) + autocomplete?: boolean; + + @primitiveTrait({ + type: "number", + name: "URL", + description: "Time to move to the result location." + }) + flightDurationSeconds?: number; +} + +export class SearchProviderMapCenterTraits extends ModelTraits { + @primitiveTrait({ + type: "boolean", + name: "Map center", + description: + "Whether the current location of the map center is supplied with search request" + }) + mapCenter: boolean = true; +} diff --git a/lib/Traits/SearchProvider/SearchProviderTraits.ts b/lib/Traits/SearchProvider/SearchProviderTraits.ts new file mode 100644 index 00000000000..0c2ac76e391 --- /dev/null +++ b/lib/Traits/SearchProvider/SearchProviderTraits.ts @@ -0,0 +1,32 @@ +import ModelTraits from "../ModelTraits"; +import primitiveTrait from "../primitiveTrait"; + +export default class SearchProviderTraits extends ModelTraits { + @primitiveTrait({ + type: "string", + name: "Name", + description: "Name of the search provider." + }) + name: string = "unknown"; + + @primitiveTrait({ + type: "string", + name: "ID", + description: "Unique id of the search provider." + }) + id?: string; + + @primitiveTrait({ + type: "boolean", + name: "Open by default", + description: "Wheter are this search provider results open by default" + }) + openByDefault: boolean = true; + + @primitiveTrait({ + type: "number", + name: "Minimum characters", + description: "Minimum number of characters required for search to start" + }) + minCharacters?: number; +} diff --git a/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts b/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts new file mode 100644 index 00000000000..c95945ff74c --- /dev/null +++ b/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts @@ -0,0 +1,18 @@ +import primitiveTrait from "../primitiveTrait"; +import LocationSearchProviderTraits from "./LocationSearchProviderTraits"; + +export default class WebFeatureServiceSearchProviderTraits extends LocationSearchProviderTraits { + @primitiveTrait({ + type: "string", + name: "Search property name", + description: "Which property to look for the search text in" + }) + searchPropertyName?: string; + + @primitiveTrait({ + type: "string", + name: "Search property type name", + description: "Type of the properties to search" + }) + searchPropertyTypeName?: string; +} diff --git a/lib/ViewModels/CatalogItemNameSearchProviderViewModel.js b/lib/ViewModels/CatalogItemNameSearchProviderViewModel.js deleted file mode 100644 index 91be2973e47..00000000000 --- a/lib/ViewModels/CatalogItemNameSearchProviderViewModel.js +++ /dev/null @@ -1,317 +0,0 @@ -"use strict"; - -/*global require*/ -var inherit = require("../Core/inherit"); -var runLater = require("../Core/runLater"); -var SearchProvider = require("../Models/SearchProvider").default; -var SearchResult = require("../Models/SearchResult").default; - -var defaultValue = require("terriajs-cesium/Source/Core/defaultValue").default; -var defined = require("terriajs-cesium/Source/Core/defined").default; -var when = require("terriajs-cesium/Source/ThirdParty/when").default; -const i18next = require("i18next").default; - -var CatalogItemNameSearchProviderViewModel = function(options) { - SearchProvider.call(this); - - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - - this.terria = options.terria; - this._searchInProgress = undefined; - - this.name = i18next.t("viewModels.searchCatalogueItem"); - this.maxResults = defaultValue(options.maxResults, 10000); -}; - -inherit(SearchProvider, CatalogItemNameSearchProviderViewModel); - -CatalogItemNameSearchProviderViewModel.prototype.search = function(searchText) { - function parseSearchFilters() { - /* Filter search by type, eg 'is:wms fish' or '-is:wfs house'. Multiple 'is' clauses are or-ed. */ - var isRE = /(^|\s)(-?)is:([a-zA-Z0-9_-]+)\b/i; - while (searchText.match(isRE)) { - if (!searchText.match(isRE)[2]) { - searchFilters.typeIs.push(searchText.match(isRE)[3].toLowerCase()); - } else { - searchFilters.typeIsNot.push(searchText.match(isRE)[3].toLowerCase()); - } - searchText = searchText.replace(isRE, "").trim(); - } - /* Change number of search results: 'show:20' or 'show:all' */ - var showRE = /\bshow:(all|[0-9]+)\b/i; - while (searchText.match(showRE)) { - searchFilters.maxResults = searchText.match(showRE)[1].toLowerCase(); - if (searchFilters.maxResults === "all") { - searchFilters.maxResults = 10000; - } else { - searchFilters.maxResults = parseInt(searchFilters.maxResults, 10); - } - searchText = searchText.replace(showRE, "").trim(); - } - - /* Filter by URL: 'url:landgate.wa' or '-url:geoserver.nicta.com.au' */ - var urlRE = /(^|\s)(-?)url:([^ ]+)(\b|$)/i; - while (searchText.match(urlRE)) { - if (!searchText.match(urlRE)[2]) { - searchFilters.urlMatches.push(searchText.match(urlRE)[3].toLowerCase()); - } else { - searchFilters.urlDoesNotMatch.push( - searchText.match(urlRE)[3].toLowerCase() - ); - } - searchText = searchText.replace(urlRE, "").trim(); - } - } - - if (this._searchInProgress) { - this._searchInProgress.cancel = true; - this._searchInProgress = undefined; - } - - if (!defined(searchText) || /^\s*$/.test(searchText)) { - this.isSearching = false; - this.searchResults.removeAll(); - return; - } - - var searchFilters = { - typeIs: [], - typeIsNot: [], - urlMatches: [], - urlDoesNotMatch: [] - }; - parseSearchFilters(); - - this.isSearching = true; - this.searchResults.removeAll(); - this.searchMessage = undefined; - - this.terria.analytics.logEvent("search", "catalogue", searchText); - - var searchInProgress = (this._searchInProgress = { - cancel: false - }); - - var path = []; - var topLevelGroup = this.terria.catalog.group; - var promise = findMatchingItemsRecursively( - this, - searchInProgress, - new RegExp(searchText, "i"), - topLevelGroup, - path, - undefined, - searchFilters - ); - - var that = this; - return when(promise).then(function() { - that.isSearching = false; - - if (searchInProgress.cancel) { - return; - } - - if (that.searchResults.length === 0) { - that.searchMessage = i18next.t("viewModels.searchNoCatalogueItem"); - } - }); -}; - -function itemMatchesFilters(item, searchFilters) { - if ( - searchFilters.typeIs.length > 0 && - searchFilters.typeIs.indexOf(item.type.toLowerCase()) < 0 - ) { - return false; - } - if ( - searchFilters.typeIsNot.length > 0 && - searchFilters.typeIsNot.indexOf(item.type.toLowerCase()) >= 0 - ) { - return false; - } - if (!item.url) { - // if no URL, it can't match any positive filters, and can't fail by matching any negative ones. - return searchFilters.urlMatches.length === 0; - } - - var r = true; - // multiple -url: filters are and-ed - searchFilters.urlDoesNotMatch.forEach(function(e) { - // we just do simple string matching, not regex - if (item.url.toLowerCase().indexOf(e) >= 0) { - r = false; - } - }); - if (!r) { - return false; - } - if (searchFilters.urlMatches.length === 0) { - return true; - } - - r = false; - // multiple url: filters are or-ed - searchFilters.urlMatches.forEach(function(e) { - // we just do simple string matching, not regex - if (item.url.toLowerCase().indexOf(e) >= 0) { - r = true; - } - }); - - return r; -} - -function findMatchingItemsRecursively( - viewModel, - searchInProgress, - searchExpression, - group, - path, - promise, - searchFilters -) { - path.push(group); - if (!defined(searchFilters)) { - searchFilters = {}; - } - var items = group.items; - var maxResults = defined(searchFilters.maxResults) - ? searchFilters.maxResults - : viewModel.maxResults; - for ( - var i = 0; - !searchInProgress.cancel && - viewModel.searchResults.length < maxResults && - i < items.length; - ++i - ) { - var item = items[i]; - - if (item.isHidden) { - continue; - } - - // Match non-top-level items whose name contain the search text. - if ( - searchExpression.test(item.name) && - itemMatchesFilters(item, searchFilters) - ) { - addSearchResult(viewModel.searchResults, item, path); - } - - if (defined(item.items)) { - promise = loadAndSearchGroup( - viewModel, - item, - searchInProgress, - searchExpression, - path, - promise, - searchFilters - ); - } - } - - path.pop(); - - return promise; -} - -function loadAndSearchGroup( - viewModel, - group, - searchInProgress, - searchExpression, - path, - promise, - searchFilters -) { - path = path.slice(); - - // Let a previous load (represented by 'promise') finish first. - return when(promise).then(function() { - if (searchInProgress.cancel) { - return; - } - return runLater(function() { - if (searchInProgress.cancel) { - return; - } - var loadPromise = group.load(); - if (defined(loadPromise) && group.isLoading) { - return loadPromise - .then(function() { - return findMatchingItemsRecursively( - viewModel, - searchInProgress, - searchExpression, - group, - path, - undefined, - searchFilters - ); - }) - .otherwise(ignoreLoadErrors); - } else { - return findMatchingItemsRecursively( - viewModel, - searchInProgress, - searchExpression, - group, - path, - undefined, - searchFilters - ); - } - }); - }); -} - -function ignoreLoadErrors() {} - -function addSearchResult(searchResults, item, path) { - // Get the index of an existing search result that refers to the same catalog item (or -1) - var index = -1; - for (var j = 0; j < searchResults.length; ++j) { - if (item === searchResults[j].catalogItem) { - index = j; - break; - } - } - - // If a search result for item already exists, modify the tooltip of that search result - var prefix = i18next.t("viewModels.inMultipleLocations"); - if (index >= 0) { - if (searchResults[index].tooltip.indexOf(prefix) !== 0) { - searchResults[index].tooltip = searchResults[index].tooltip.replace( - /^In /, - prefix - ); - } - } else { - // Otherwise, create a new search result - searchResults.push( - new SearchResult({ - name: item.name, - isImportant: true, - catalogItem: item, - tooltip: pathToTooltip(path) - }) - ); - } -} - -function pathToTooltip(path) { - var result = i18next.t("viewModels.inDataCatalogue"); - - // Start at 1 to skip "Root Group" - for (var i = 1; i < path.length; ++i) { - result += " -> " + path[i].name; - } - - return result; -} - -module.exports = CatalogItemNameSearchProviderViewModel; diff --git a/lib/ViewModels/GazetteerSearchProviderViewModel.js b/lib/ViewModels/GazetteerSearchProviderViewModel.js deleted file mode 100644 index c2866d8c39d..00000000000 --- a/lib/ViewModels/GazetteerSearchProviderViewModel.js +++ /dev/null @@ -1,227 +0,0 @@ -"use strict"; - -/*global require*/ -var inherit = require("../Core/inherit"); -var SearchProviderViewModel = require("./SearchProviderViewModel"); -var SearchResultViewModel = require("../Models/SearchResultViewModel"); -var zoomRectangleFromPoint = require("../Map/zoomRectangleFromPoint"); - -var defaultValue = require("terriajs-cesium/Source/Core/defaultValue").default; -var defined = require("terriajs-cesium/Source/Core/defined").default; -const i18next = require("i18next").default; -var loadJson = require("../Core/loadJson").default; - -var GazetteerSearchProviderViewModel = function(options) { - SearchProviderViewModel.call(this); - - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - - this.terria = options.terria; - this._geocodeInProgress = undefined; - - this.name = i18next.t("viewModels.searchPlaceNames"); - this.url = defaultValue( - options.url, - "http://www.ga.gov.au/gazetteer-search/gazetteer2012/select/" - ); - this.forceUseOfProxy = defaultValue(options.forceUseOfProxy, true); - this.flightDurationSeconds = defaultValue(options.flightDurationSeconds, 1.5); -}; - -inherit(SearchProviderViewModel, GazetteerSearchProviderViewModel); - -GazetteerSearchProviderViewModel.prototype.search = function(searchText) { - if (!defined(searchText) || /^\s*$/.test(searchText)) { - this.isSearching = false; - this.searchResults.removeAll(); - return; - } - - this.isSearching = true; - this.searchResults.removeAll(); - this.searchMessage = undefined; - - this.terria.analytics.logEvent("search", "gazetteer", searchText); - - // If there is already a search in progress, cancel it. - if (defined(this._geocodeInProgress)) { - this._geocodeInProgress.cancel = true; - this._geocodeInProgress = undefined; - } - - // I don't know how to get server-side sorting, so we have to retrieve lots of rows, then filter. - // Retrieving only 10 rows (default) means "Sydney" fails to actually retrieve Sydney... - - // Worth considering using "fq=class_code:(R%20X)", which would only return towns, states etc - - var url = this.url + "?q=name:*" + searchText + "*"; - // filter out bores, built structures, caves, landmarks, trig stations - url += "%20-class_code:(D%20E%20G%20J%20T)%20-feature_code:PRS"; - url += "&rows=200"; - - if (this.forceUseOfProxy || this.terria.corsProxy.shouldUseProxy(url)) { - url = this.terria.corsProxy.getURL(url); - } - - var promise = loadJson(url); - - var that = this; - var geocodeInProgress = (this._geocodeInProgress = promise - .then(function(solrQueryResponse) { - if (geocodeInProgress.cancel) { - return; - } - that.isSearching = false; - - if ( - defined(solrQueryResponse.response) && - solrQueryResponse.response.numFound > 0 - ) { - var results = solrQueryResponse.response.docs.sort(function(a, b) { - return sortResults(a, b, searchText); - }); - results = stripDuplicates(results); - results.slice(0, 10).forEach(function(result) { - var locLat = result.location.split(",")[0]; - var locLng = result.location.split(",")[1]; - - that.searchResults.push( - new SearchResultViewModel({ - name: - result.name + - (result.state_id !== "N/A" ? " (" + result.state_id + ")" : ""), - isImportant: !!result.feature_code.match( - "^(CNTY|CONT|DI|PRSH|STAT|LOCB|LOCU|SUB|URBN)$" - ), - clickAction: createZoomToFunction(that, locLat, locLng), - location: { - latitude: locLat, - longitude: locLng - } - }) - ); - }); - } else { - that.searchMessage = i18next.t("viewModels.searchNoPlaceNames"); - } - - that.isSearching = false; - }) - .otherwise(function() { - if (geocodeInProgress.cancel) { - return; - } - - that.isSearching = false; - that.searchMessage = i18next.t("viewModels.searchErrorOccurred"); - })); - - return geocodeInProgress; -}; - -// Given a list of results sorted in decreasing importance, strip results that are close to another result with the same name -function stripDuplicates(results) { - var i; - var placeshash = {}; - var stripped = []; - for (i = 0; i < results.length; i++) { - var lat = Number(results[i].location.split(",")[0]).toFixed(1); - var lng = Number(results[i].location.split(",")[1]).toFixed(1); - - var hash = results[i].name + "_" + lat + " " + lng; - if (!defined(placeshash[hash])) { - placeshash[hash] = results[i]; - stripped.push(results[i]); - } - } - return stripped; -} - -function featureScore(a, searchText) { - // could be further refined using http://www.ga.gov.au/image_cache/GA19367.pdf - // feature_code is defined on p24 - // class_code (A-X) matches a row in the table on p23 (eg, 'C' is 'Bays & Gulfs') - var featureTypes = [ - "CONT", - "STAT", - "URBN", - "LOCB", - "LOCU", - "SUB", - "DI", - "CNTY", - "DI" - ]; - featureTypes.push( - "HBR", - "CAPE", - "PEN", - "PT", - "BAY", - "PORT", - "GULF", - "BGHT", - "COVE", - "MT", - "HILL", - "PEAK", - "OCEN", - "SEA", - "RESV", - "LAKE", - "RES", - "STRM" - ); - featureTypes.push("PLN", "REEF", "VAL", "PRSH"); - - var aScore = 10000 - (featureTypes.indexOf(a.feature_code) + 1) * 100; - if (aScore === 10000) { - aScore = 0; - } - - if (a.name.toUpperCase() === searchText.toUpperCase()) { - // Bonus for exact match - // More testing required to choose this value. Should "Geelong" (parish in Queensland) outrank "North Geelong" (suburb in Vic)? - aScore += 10 * 100; - } else if (a.name.match(new RegExp("^" + searchText + "\\b", "i"))) { - // bonus for first word match ('Steve Bay' better than 'West Steve Bay') - aScore += 8 * 100; - } else if (a.name.match(new RegExp("\\b" + searchText + "\\b", "i"))) { - // bonus for word-boundary match ('Steve' better than 'Steveville') - aScore += 4 * 100; - } else if (a.name.match(new RegExp("^" + searchText, "i"))) { - // bonus for word-boundary match ('Steventon' better than 'Nosteve Town') - aScore += 2 * 100; - } - if (a.state_id === "N/A") { - // seems to be an indicator of a low quality result - aScore -= 10 * 100; - } - if (a.status === "U") { - // Not official? H=historical, U=unofficial. Bleh. - aScore -= 5 * 100; - } - if (a.status === "H") { - aScore -= 10 * 100; - } - - return aScore; -} - -function sortResults(a, b, searchText) { - return featureScore(b, searchText) - featureScore(a, searchText); -} - -function createZoomToFunction(viewModel, locLat, locLng) { - // Server does not return information of a bounding box, just a location. - // bboxSize is used to expand a point - var bboxSize = 0.2; - var rectangle = zoomRectangleFromPoint(locLat, locLng, bboxSize); - - return function() { - var terria = viewModel.terria; - terria.currentViewer.zoomTo(rectangle, viewModel.flightDurationSeconds); - }; -} - -module.exports = GazetteerSearchProviderViewModel; diff --git a/lib/ViewModels/GnafSearchProviderViewModel.js b/lib/ViewModels/GnafSearchProviderViewModel.js deleted file mode 100644 index 1b8412f32cf..00000000000 --- a/lib/ViewModels/GnafSearchProviderViewModel.js +++ /dev/null @@ -1,119 +0,0 @@ -"use strict"; - -/*global require*/ -var inherit = require("../Core/inherit"); -var SearchProviderViewModel = require("./SearchProviderViewModel"); -var SearchResultViewModel = require("../Models/SearchResultViewModel"); -var zoomRectangleFromPoint = require("../Map/zoomRectangleFromPoint"); -var GnafApi = require("../Models/GnafApi"); - -var defaultValue = require("terriajs-cesium/Source/Core/defaultValue").default; -var defined = require("terriajs-cesium/Source/Core/defined").default; -const i18next = require("i18next").default; - -/** - * Search provider that uses the Data61 Elastic Search GNAF service to look up addresses. - * - * @param options.terria Terria instance - * @param [options.gnafApi] The GnafApi object to query - if none is provided one will be created using terria.corsProxy - * and the default settings. - * @param [options.flightDurationSeconds] The number of seconds for the flight animation when zooming to new results. - * @constructor - */ -var GnafSearchProviderViewModel = function(options) { - SearchProviderViewModel.call(this); - - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - this.terria = options.terria; - - var url = defaultValue( - options.url, - this.terria.configParameters.gnafSearchUrl - ); - - this.name = i18next.t("viewModels.searchAddresses"); - this.gnafApi = defaultValue( - options.gnafApi, - new GnafApi(this.terria.corsProxy, url) - ); - this._geocodeInProgress = undefined; - this.flightDurationSeconds = defaultValue(options.flightDurationSeconds, 1.5); -}; - -inherit(SearchProviderViewModel, GnafSearchProviderViewModel); - -GnafSearchProviderViewModel.prototype.search = function(searchText) { - this.isSearching = true; - this.searchResults.removeAll(); - - if (!defined(searchText) || /^\s*$/.test(searchText)) { - return; - } - - this.searchMessage = undefined; - this.terria.analytics.logEvent("search", "gnaf", searchText); - - // If there is already a search in progress, cancel it. - if (defined(this._geocodeInProgress)) { - this._geocodeInProgress.cancel = true; - this._geocodeInProgress = undefined; - } - - var thisGeocode = this.gnafApi - .geoCode(searchText) - .then( - function(hits) { - if (thisGeocode.cancel) { - return; - } - - this.isSearching = false; - - if (hits.length === 0) { - this.searchMessage = i18next.t("viewModels.searchNoLocations"); - return; - } - - this.searchResults = hits.map( - function(hit) { - return new SearchResultViewModel({ - name: hit.name, - isImportant: hit.locational, - clickAction: createZoomToFunction( - this.terria, - hit.location, - this.flightDurationSeconds - ), - location: hit.location - }); - }.bind(this) - ); - }.bind(this) - ) - .otherwise( - function() { - if (thisGeocode.cancel) { - return; - } - - this.isSearching = false; - this.searchMessage = i18next.t("viewModels.searchErrorOccurred"); - }.bind(this) - ); - - this._geocodeInProgress = thisGeocode; -}; - -function createZoomToFunction(terria, location, duration) { - var rectangle = zoomRectangleFromPoint( - location.latitude, - location.longitude, - 0.01 - ); - - return function() { - terria.currentViewer.zoomTo(rectangle, duration); - }; -} - -module.exports = GnafSearchProviderViewModel; diff --git a/lib/ViewModels/NominatimSearchProviderViewModel.js b/lib/ViewModels/NominatimSearchProviderViewModel.js deleted file mode 100644 index 71e248956c1..00000000000 --- a/lib/ViewModels/NominatimSearchProviderViewModel.js +++ /dev/null @@ -1,199 +0,0 @@ -"use strict"; - -/*global require*/ -var inherit = require("../Core/inherit"); -var SearchProviderViewModel = require("./SearchProviderViewModel"); -var SearchResultViewModel = require("../Models/SearchResultViewModel"); - -var CesiumMath = require("terriajs-cesium/Source/Core/Math").default; -var Cartesian2 = require("terriajs-cesium/Source/Core/Cartesian2").default; -var defaultValue = require("terriajs-cesium/Source/Core/defaultValue").default; -var defined = require("terriajs-cesium/Source/Core/defined").default; -var Ellipsoid = require("terriajs-cesium/Source/Core/Ellipsoid").default; -var loadJson = require("../Core/loadJson").default; -var Rectangle = require("terriajs-cesium/Source/Core/Rectangle").default; -var when = require("terriajs-cesium/Source/ThirdParty/when").default; -const i18next = require("i18next").default; - -var NominatimSearchProviderViewModel = function(options) { - SearchProviderViewModel.call(this); - - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - - this.terria = options.terria; - this.countryCodes = defined(options.countryCodes) - ? "&countrycodes=" + options.countryCodes - : ""; - - this._geocodeInProgress = undefined; - - this.name = "Nominatim"; - this.url = defaultValue(options.url, "//nominatim.openstreetmap.org/"); - if (this.url.length > 0 && this.url[this.url.length - 1] !== "/") { - this.url += "/"; - } - this.flightDurationSeconds = defaultValue(options.flightDurationSeconds, 1.5); - this.limitBounded = 2; - this.limitOthers = 2; -}; - -inherit(SearchProviderViewModel, NominatimSearchProviderViewModel); - -NominatimSearchProviderViewModel.prototype.search = function(searchText) { - if (!defined(searchText) || /^\s*$/.test(searchText)) { - this.isSearching = false; - this.searchResults.removeAll(); - return; - } - - this.isSearching = true; - this.searchResults.removeAll(); - this.searchMessage = undefined; - - this.terria.analytics.logEvent("search", "nominatim", searchText); - - // If there is already a search in progress, cancel it. - if (defined(this._geocodeInProgress)) { - this._geocodeInProgress.cancel = true; - this._geocodeInProgress = undefined; - } - - var bboxStr = ""; - - if (defined(this.terria.cesium)) { - var viewer = this.terria.cesium.viewer; - var posUL = viewer.camera.pickEllipsoid( - new Cartesian2(0, 0), - Ellipsoid.WGS84 - ); - var posLR = viewer.camera.pickEllipsoid( - new Cartesian2(viewer.canvas.width, viewer.canvas.height), - Ellipsoid.WGS84 - ); - if (defined(posUL) && defined(posLR)) { - posUL = Ellipsoid.WGS84.cartesianToCartographic(posUL); - posLR = Ellipsoid.WGS84.cartesianToCartographic(posLR); - bboxStr = - "&viewbox=" + - CesiumMath.toDegrees(posUL.longitude) + - "," + - CesiumMath.toDegrees(posUL.latitude) + - "," + - CesiumMath.toDegrees(posLR.longitude) + - "," + - CesiumMath.toDegrees(posLR.latitude); - } else { - bboxStr = ""; - } - } else if (defined(this.terria.leaflet)) { - var bbox = this.terria.leaflet.map.getBounds(); - bboxStr = - "&viewbox=" + - bbox.getWest() + - "," + - bbox.getNorth() + - "," + - bbox.getEast() + - "," + - bbox.getSouth(); - } - var promiseBounded = loadJson( - this.url + - "search?q=" + - searchText + - bboxStr + - "&bounded=1&format=json" + - this.countryCodes + - "&limit=" + - this.limitBounded - ); - var promiseOthers = loadJson( - this.url + - "search?q=" + - searchText + - "&format=json" + - this.countryCodes + - "&limit=" + - this.limitOthers - ); - - var that = this; - var geocodeInProgress = (this._geocodeInProgress = { - cancel: false - }); - - return when - .all([promiseBounded, promiseOthers]) - .then(function(result) { - if (geocodeInProgress.cancel) { - return; - } - that.isSearching = false; - - if (result.length === 0) { - return; - } - - var locations = []; - - // Locations in the bounded query go on top, locations elsewhere go undernearth - var findDbl = function(elts, id) { - return elts.filter(function(elt) { - return elt.id === id; - })[0]; - }; - - for (var i = 0; i < result.length; ++i) { - for (var j = 0; j < result[i].length; ++j) { - var resource = result[i][j]; - - var name = resource.display_name; - if (!defined(name)) { - continue; - } - - if (!findDbl(locations, resource.place_id)) { - locations.push( - new SearchResultViewModel({ - id: resource.place_id, - name: name, - isImportant: true, - clickAction: createZoomToFunction(that, resource) - }) - ); - } - } - } - - that.searchResults.push.apply(that.searchResults, locations); - - if (that.searchResults.length === 0) { - that.searchMessage = i18next.t("viewModels.searchNoLocations"); - } - }) - .otherwise(function() { - if (geocodeInProgress.cancel) { - return; - } - - that.isSearching = false; - that.searchMessage = i18next.t("viewModels.searchErrorOccurred"); - }); -}; - -function createZoomToFunction(viewModel, resource) { - var bbox = resource.boundingbox; - var south = bbox[0]; - var west = bbox[2]; - var north = bbox[1]; - var east = bbox[3]; - - var rectangle = Rectangle.fromDegrees(west, south, east, north); - - return function() { - var terria = viewModel.terria; - terria.currentViewer.zoomTo(rectangle, viewModel.flightDurationSeconds); - }; -} - -module.exports = NominatimSearchProviderViewModel; diff --git a/test/Models/AustralianGazetteerSearchProviderSpec.ts b/test/Models/AustralianGazetteerSearchProviderSpec.ts index b31b8ba790f..33a4a48047c 100644 --- a/test/Models/AustralianGazetteerSearchProviderSpec.ts +++ b/test/Models/AustralianGazetteerSearchProviderSpec.ts @@ -1,7 +1,7 @@ import { configure } from "mobx"; -import createAustralianGazetteerSearchProvider from "../../lib/Models/AustralianGazetteerSearchProvider"; +import createAustralianGazetteerSearchProvider from "../../lib/Models/SearchProvider/AustralianGazetteerSearchProvider"; import Terria from "../../lib/Models/Terria"; -import WebFeatureServiceSearchProvider from "../../lib/Models/WebFeatureServiceSearchProvider"; +import WebFeatureServiceSearchProvider from "../../lib/Models/SearchProvider/WebFeatureServiceSearchProvider"; const wfsResponseXml = require("raw-loader!../../wwwroot/test/WFS/getWithFilter.xml"); diff --git a/test/ViewModels/CatalogItemNameSearchProviderViewModelSpec.js b/test/ViewModels/CatalogItemNameSearchProviderViewModelSpec.js deleted file mode 100644 index b672bbcfae5..00000000000 --- a/test/ViewModels/CatalogItemNameSearchProviderViewModelSpec.js +++ /dev/null @@ -1,280 +0,0 @@ -"use strict"; - -/*global require,describe,it,expect,beforeEach*/ -var CatalogGroup = require("../../lib/Models/CatalogGroup"); -var CatalogItem = require("../../lib/Models/CatalogItem"); -var WebMapServiceCatalogItem = require("../../lib/Models/WebMapServiceCatalogItem"); -var GeoJsonCatalogItem = require("../../lib/Models/GeoJsonCatalogItem"); -var CatalogItemNameSearchProviderViewModel = require("../../lib/ViewModels/CatalogItemNameSearchProviderViewModel"); -var inherit = require("../../lib/Core/inherit"); -var runLater = require("../../lib/Core/runLater"); -var Terria = require("../../lib/Models/Terria"); - -describe("CatalogItemNameSearchProviderViewModel", function() { - var terria; - var searchProvider; - - beforeEach(function() { - terria = new Terria({ - baseUrl: "./" - }); - - searchProvider = new CatalogItemNameSearchProviderViewModel({ - terria: terria - }); - }); - - it("finds catalog items in a case-insensitive manner", function(done) { - var catalogGroup = terria.catalog.group; - - var item = new CatalogItem(terria); - item.name = "Thing to find"; - catalogGroup.add(item); - - searchProvider - .search("thing") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe("Thing to find"); - }) - .then(done) - .otherwise(done.fail); - }); - - it("finds catalog groups in a case-insensitive manner", function(done) { - var catalogGroup = terria.catalog.group; - - var item = new CatalogGroup(terria); - item.name = "Group to find"; - catalogGroup.add(item); - - searchProvider - .search("to") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe("Group to find"); - }) - .then(done) - .otherwise(done.fail); - }); - - it("does not find catalog items if they do not match", function(done) { - var catalogGroup = terria.catalog.group; - - var item = new CatalogItem(terria); - item.name = "Thing to find"; - catalogGroup.add(item); - - searchProvider - .search("foo") - .then(function() { - expect(searchProvider.searchResults.length).toBe(0); - }) - .then(done) - .otherwise(done.fail); - }); - - it("finds items in asynchronously-loaded groups", function(done) { - var DelayedGroup = function() { - CatalogGroup.call(this, terria); - - this.name = "Delayed Group"; - this._load = function() { - var that = this; - return runLater(function() { - var item = new CatalogItem(terria); - item.name = "Thing to find"; - that.add(item); - }); - }; - }; - inherit(CatalogGroup, DelayedGroup); - - terria.catalog.group.add(new DelayedGroup()); - searchProvider - .search("thing") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe("Thing to find"); - }) - .then(done) - .otherwise(done.fail); - }); - - it("finds results of a certain type in a case-insensitive manner", function(done) { - var catalogGroup = terria.catalog.group; - - var item1 = new WebMapServiceCatalogItem(terria); - item1.name = "WMS item to find"; - catalogGroup.add(item1); - - var item2 = new GeoJsonCatalogItem(terria, ""); - item2.name = "GeoJson item to find"; - catalogGroup.add(item2); - - searchProvider - .search("to is:wMs") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe("WMS item to find"); - }) - .then(done) - .otherwise(done.fail); - }); - - it("finds results not of a certain type in a case-insensitive manner", function(done) { - var catalogGroup = terria.catalog.group; - - var item1 = new WebMapServiceCatalogItem(terria); - item1.name = "WMS item to find"; - catalogGroup.add(item1); - - var item2 = new GeoJsonCatalogItem(terria, ""); - item2.name = "GeoJson item to find"; - catalogGroup.add(item2); - - searchProvider - .search("to -is:wMs") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe( - "GeoJson item to find" - ); - }) - .then(done) - .otherwise(done.fail); - }); - - it("finds results having a certain url", function(done) { - var catalogGroup = terria.catalog.group; - - var item1 = new CatalogItem(terria); - item1.name = "Server 1 item to find"; - item1.url = "http://server1.gov.au/page"; - catalogGroup.add(item1); - - var item2 = new CatalogItem(terria); - item2.name = "Server 2 item to find"; - item2.url = "http://server2.gov.au/page"; - catalogGroup.add(item2); - - searchProvider - .search("to url:server1.gov") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe( - "Server 1 item to find" - ); - }) - .then(done) - .otherwise(done.fail); - }); - - it("finds results that do not have a certain url", function(done) { - var catalogGroup = terria.catalog.group; - - var item1 = new CatalogItem(terria); - item1.name = "Server 1 item to find"; - item1.url = "http://server1.gov.au/page"; - catalogGroup.add(item1); - - var item2 = new CatalogItem(terria); - item2.name = "Server 2 item to find"; - item2.url = "http://server2.gov.au/page"; - catalogGroup.add(item2); - - searchProvider - .search("to -url:server1.gov") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe( - "Server 2 item to find" - ); - }) - .then(done) - .otherwise(done.fail); - }); - - it("stops searching after the specified number of items", function(done) { - var catalogGroup = terria.catalog.group; - - var maxResults = 9; - - // Add items matching the query. - for (var i = 0; i < maxResults; ++i) { - var item = new CatalogItem(terria); - item.name = "Thing to find " + i; - catalogGroup.add(item); - } - - // Add an 11th item that will flip out if asked to load. - var FlipOutGroup = function(terria) { - CatalogGroup.call(this, terria); - - this.name = "Flip Out Group"; - this._load = function() { - done.fail("This item should not be asked to load."); - }; - }; - inherit(CatalogGroup, FlipOutGroup); - catalogGroup.add(new FlipOutGroup(terria)); - - searchProvider.maxResults = maxResults; - searchProvider - .search("thing") - .then(function() { - expect(searchProvider.searchResults.length).toBe(maxResults); - }) - .then(done) - .otherwise(done.fail); - }); - - it("combines duplicate search entries of the same item in different groups", function(done) { - var catalogGroup = terria.catalog.group; - - var group1 = new CatalogGroup(terria); - group1.name = "Group1"; - catalogGroup.add(group1); - - var item = new CatalogItem(terria); - item.name = "Thing to find"; - catalogGroup.add(item); - group1.add(item); - - searchProvider - .search("to") - .then(function() { - expect(searchProvider.searchResults.length).toBe(1); - expect(searchProvider.searchResults[0].name).toBe("Thing to find"); - expect(searchProvider.searchResults[0].tooltip).toMatch( - /^In multiple locations including: / - ); - }) - .then(done) - .otherwise(done.fail); - }); - - it("does not combine different items with the same item name", function(done) { - var catalogGroup = terria.catalog.group; - - var item1 = new CatalogItem(terria); - item1.name = "Thing to find"; - item1.id = "thing1"; - catalogGroup.add(item1); - - var item2 = new CatalogItem(terria); - item2.name = "Thing to find"; - item2.id = "thing2"; - catalogGroup.add(item2); - - searchProvider - .search("to") - .then(function() { - expect(searchProvider.searchResults.length).toBe(2); - expect(searchProvider.searchResults[0].name).toBe("Thing to find"); - expect(searchProvider.searchResults[1].name).toBe("Thing to find"); - }) - .then(done) - .otherwise(done.fail); - }); -}); From f73539ee4900c3b9500f295853c6ef558dbade83 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Tue, 19 Jan 2021 14:00:46 +0100 Subject: [PATCH 002/129] add SearchProvider folder in ModelMixins --- .../{ => SerchProvider}/SearchProviderMixin.ts | 10 +++++----- .../WebFeatureServiceSearchProviderMixin.ts | 17 ++++++++--------- .../AustralianGazetteerSearchProvider.ts | 6 +++--- .../SearchProvider/BingMapsSearchProvider.ts | 2 +- .../SearchProvider/CatalogSearchProvider.ts | 2 +- .../SearchProvider/SearchProviderResults.ts | 2 +- lib/Models/SearchProvider/StubSearchProvider.ts | 2 +- lib/ReactViewModels/SearchState.ts | 2 +- 8 files changed, 21 insertions(+), 22 deletions(-) rename lib/ModelMixins/{ => SerchProvider}/SearchProviderMixin.ts (89%) rename lib/ModelMixins/{ => SerchProvider}/WebFeatureServiceSearchProviderMixin.ts (92%) diff --git a/lib/ModelMixins/SearchProviderMixin.ts b/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts similarity index 89% rename from lib/ModelMixins/SearchProviderMixin.ts rename to lib/ModelMixins/SerchProvider/SearchProviderMixin.ts index b53a05bd2e3..bb9b056b5c0 100644 --- a/lib/ModelMixins/SearchProviderMixin.ts +++ b/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts @@ -3,11 +3,11 @@ import { fromPromise } from "mobx-utils"; import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; -import Constructor from "../Core/Constructor"; -import Model from "../Models/Model"; -import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; -import Terria from "../Models/Terria"; -import SearchProviderTraits from "../Traits/SearchProvider/SearchProviderTraits"; +import Constructor from "../../Core/Constructor"; +import Model from "../../Models/Model"; +import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; +import Terria from "../../Models/Terria"; +import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; type SearchProvider = Model; diff --git a/lib/ModelMixins/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin.ts similarity index 92% rename from lib/ModelMixins/WebFeatureServiceSearchProviderMixin.ts rename to lib/ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin.ts index 70a0c26bf9e..c8c1a814835 100644 --- a/lib/ModelMixins/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin.ts @@ -2,15 +2,14 @@ import i18next from "i18next"; import { runInAction } from "mobx"; import Resource from "terriajs-cesium/Source/Core/Resource"; import URI from "urijs"; -import Constructor from "../Core/Constructor"; -import makeRealPromise from "../Core/makeRealPromise"; -import zoomRectangleFromPoint from "../Map/zoomRectangleFromPoint"; -import Model from "../Models/Model"; -import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; -import SearchResult from "../Models/SearchProvider/SearchResult"; -import Terria from "../Models/Terria"; -import xml2json from "../ThirdParty/xml2json"; -import WebFeatureServiceSearchProviderTraits from "../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; +import Constructor from "../../Core/Constructor"; +import makeRealPromise from "../../Core/makeRealPromise"; +import zoomRectangleFromPoint from "../../Map/zoomRectangleFromPoint"; +import Model from "../../Models/Model"; +import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; +import SearchResult from "../../Models/SearchProvider/SearchResult"; +import xml2json from "../../ThirdParty/xml2json"; +import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; import SearchProviderMixin from "./SearchProviderMixin"; function WebFeatureServiceSearchProviderMixin< diff --git a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts index 9d0dbf50954..2b4d2996215 100644 --- a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts +++ b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts @@ -1,6 +1,6 @@ import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; import CreateModel from "../CreateModel"; -import WebFeatureServiceSearchProviderMixin from "./../../ModelMixins/WebFeatureServiceSearchProviderMixin"; +import WebFeatureServiceSearchProviderMixin from "../../ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin"; import SearchResult from "./SearchResult"; const featureCodesToNamesMap = new Map([ @@ -224,8 +224,8 @@ export default class AustralianGazetteerSearchProvider extends WebFeatureService CreateModel(WebFeatureServiceSearchProviderTraits) ) { static readonly type = "australian-gazetteer-search-provider"; - - get type(){ + + get type() { return AustralianGazetteerSearchProvider.type; } diff --git a/lib/Models/SearchProvider/BingMapsSearchProvider.ts b/lib/Models/SearchProvider/BingMapsSearchProvider.ts index 312c873c5e8..522e3723d3c 100644 --- a/lib/Models/SearchProvider/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProvider/BingMapsSearchProvider.ts @@ -6,7 +6,7 @@ import Resource from "terriajs-cesium/Source/Core/Resource"; import loadJsonp from "../../Core/loadJsonp"; import SearchProviderMixin, { getMapCenter -} from "../../ModelMixins/SearchProviderMixin"; +} from "../../ModelMixins/SerchProvider/SearchProviderMixin"; import BingMapsSearchProviderTraits from "../../Traits/SearchProvider/BingMapsSearchProviderTraits"; import CreateModel from "../CreateModel"; import SearchProviderResults from "./SearchProviderResults"; diff --git a/lib/Models/SearchProvider/CatalogSearchProvider.ts b/lib/Models/SearchProvider/CatalogSearchProvider.ts index 1a1e85c9ba2..946b2a4ec67 100644 --- a/lib/Models/SearchProvider/CatalogSearchProvider.ts +++ b/lib/Models/SearchProvider/CatalogSearchProvider.ts @@ -4,7 +4,7 @@ import ReferenceMixin from "../../ModelMixins/ReferenceMixin"; import CatalogSearchProviderTraits from "../../Traits/SearchProvider/CatalogSearchProviderTraits"; import CreateModel from "../CreateModel"; import Terria from "../Terria"; -import SearchProviderMixin from "./../../ModelMixins/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SerchProvider/SearchProviderMixin"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; diff --git a/lib/Models/SearchProvider/SearchProviderResults.ts b/lib/Models/SearchProvider/SearchProviderResults.ts index 751e7f7cd60..78ee3340855 100644 --- a/lib/Models/SearchProvider/SearchProviderResults.ts +++ b/lib/Models/SearchProvider/SearchProviderResults.ts @@ -1,7 +1,7 @@ import { observable } from "mobx"; import SearchResult from "./SearchResult"; import { IPromiseBasedObservable, fromPromise } from "mobx-utils"; -import SearchProviderMixin from "./../../ModelMixins/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SerchProvider/SearchProviderMixin"; export default class SearchProviderResults { @observable results: SearchResult[] = []; diff --git a/lib/Models/SearchProvider/StubSearchProvider.ts b/lib/Models/SearchProvider/StubSearchProvider.ts index 50b19adb3db..0737ffad5ff 100644 --- a/lib/Models/SearchProvider/StubSearchProvider.ts +++ b/lib/Models/SearchProvider/StubSearchProvider.ts @@ -1,6 +1,6 @@ import LocationSearchProviderTraits from "./../../Traits/SearchProvider/LocationSearchProviderTraits"; import primitiveTrait from "./../../Traits/primitiveTrait"; -import SearchProviderMixin from "../../ModelMixins/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SerchProvider/SearchProviderMixin"; import CreateModel from "../CreateModel"; import SearchProviderResults from "./SearchProviderResults"; diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index bcd1fe31248..a12ebd79085 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -9,7 +9,7 @@ import filterOutUndefined from "../Core/filterOutUndefined"; import CatalogSearchProvider from "../Models/SearchProvider/CatalogSearchProvider"; import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; import Terria from "../Models/Terria"; -import SearchProviderMixin from "./../ModelMixins/SearchProviderMixin"; +import SearchProviderMixin from "../ModelMixins/SerchProvider/SearchProviderMixin"; interface SearchStateOptions { terria: Terria; From 26494d926985738ea4fc4d95022b2cb4537f15a3 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Tue, 19 Jan 2021 14:44:01 +0100 Subject: [PATCH 003/129] set default traits --- .../SerchProvider/SearchProviderMixin.ts | 4 +++- .../SearchProvider/BingMapsSearchProvider.ts | 12 ++-------- .../upsertSearchProviderFromJson.ts | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts index bb9b056b5c0..723049bc7df 100644 --- a/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts @@ -4,9 +4,11 @@ import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import Constructor from "../../Core/Constructor"; -import Model from "../../Models/Model"; +import Model, { BaseModel } from "../../Models/Model"; import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; +import StratumFromTraits from "../../Models/StratumFromTraits"; import Terria from "../../Models/Terria"; +import ModelTraits from "../../Traits/ModelTraits"; import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; type SearchProvider = Model; diff --git a/lib/Models/SearchProvider/BingMapsSearchProvider.ts b/lib/Models/SearchProvider/BingMapsSearchProvider.ts index 522e3723d3c..7adffd9947c 100644 --- a/lib/Models/SearchProvider/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProvider/BingMapsSearchProvider.ts @@ -25,10 +25,7 @@ export default class BingMapsSearchProvider extends SearchProviderMixin( constructor(uniqueId: string | undefined, terria: Terria) { super(uniqueId, terria); - if ( - (!this.key || this.key === "") && - this.terria.configParameters.bingMapsKey - ) { + if (!this.key && this.terria.configParameters.bingMapsKey) { this.setTrait( CommonStrata.defaults, "key", @@ -52,7 +49,6 @@ export default class BingMapsSearchProvider extends SearchProviderMixin( searchText: string, searchResults: SearchProviderResults ): Promise { - console.log(this.key); searchResults.results.length = 0; searchResults.message = undefined; @@ -179,11 +175,7 @@ function createZoomToFunction(model: BingMapsSearchProvider, resource: any) { const rectangle = Rectangle.fromDegrees(west, south, east, north); return function() { - const flightDurationSeconds: number = - model.flightDurationSeconds || - model.terria.configParameters.searchBar.flightDurationSeconds; - const terria = model.terria; - terria.currentViewer.zoomTo(rectangle, flightDurationSeconds); + terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds!); }; } diff --git a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts index c52dca54b74..bfbd63735ab 100644 --- a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts @@ -47,6 +47,8 @@ export default function upsertSearchProviderFromJson( model?.terria.addSearchProvider(model); } + addDefaultTraits(model); + try { updateModelFromJson(model, stratumName, json); } catch (error) { @@ -55,3 +57,25 @@ export default function upsertSearchProviderFromJson( model?.setTrait(CommonStrata.underride, "isExperiencingIssues", true); } } + +function addDefaultTraits(model: BaseModel) { + const terria = model.terria; + + model.setTrait( + CommonStrata.defaults, + "flightDurationSeconds", + terria.configParameters.searchBar.flightDurationSeconds + ); + + model.setTrait( + CommonStrata.defaults, + "minCharacters", + terria.configParameters.searchBar.minCharacters + ); + + model.setTrait( + CommonStrata.defaults, + "recommendedListLength", + terria.configParameters.searchBar.recommendedListLength + ); +} From 55f3c4296b6178056d1cdbed8e53922fd439bbea Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 21 Jan 2021 15:06:06 +0100 Subject: [PATCH 004/129] update useTranslationIfExists to request translation# at start of the key and add deprecation warning --- lib/Language/languageHelpers.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Language/languageHelpers.ts b/lib/Language/languageHelpers.ts index 1b0ea910ac3..3e1834adb5b 100644 --- a/lib/Language/languageHelpers.ts +++ b/lib/Language/languageHelpers.ts @@ -4,5 +4,15 @@ import i18next from "i18next"; * Takes a given string and translates it if it exists, otherwise return */ export function useTranslationIfExists(keyOrString: string) { - return i18next.exists(keyOrString) ? i18next.t(keyOrString) : keyOrString; + if (keyOrString && keyOrString.indexOf("translate#") === 0) { + const translationKey = keyOrString.substr("translate#".length); + return i18next.exists(translationKey) + ? i18next.t(translationKey) + : translationKey; + } else { + console.warn( + "using translation key within config won't work in future unless you prefix it with `translate#`" + ); + return keyOrString; + } } From c7525381394792007ad7829554f05f22797babb5 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 21 Jan 2021 15:16:58 +0100 Subject: [PATCH 005/129] restore default return for useTranslationIfExists until final decission to remove it, and to have time to update everything --- lib/Language/languageHelpers.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Language/languageHelpers.ts b/lib/Language/languageHelpers.ts index 3e1834adb5b..01a7a30c3ca 100644 --- a/lib/Language/languageHelpers.ts +++ b/lib/Language/languageHelpers.ts @@ -13,6 +13,8 @@ export function useTranslationIfExists(keyOrString: string) { console.warn( "using translation key within config won't work in future unless you prefix it with `translate#`" ); - return keyOrString; + // after the depreaction + // return keyOrString; + return i18next.exists(keyOrString) ? i18next.t(keyOrString) : keyOrString; } } From 4778f074b847bac2f25fc727b1465d509ccf3f01 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 21 Jan 2021 15:19:30 +0100 Subject: [PATCH 006/129] remove id from list of traits --- lib/Traits/SearchProvider/SearchProviderTraits.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/Traits/SearchProvider/SearchProviderTraits.ts b/lib/Traits/SearchProvider/SearchProviderTraits.ts index 0c2ac76e391..a023dfcb040 100644 --- a/lib/Traits/SearchProvider/SearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/SearchProviderTraits.ts @@ -9,24 +9,19 @@ export default class SearchProviderTraits extends ModelTraits { }) name: string = "unknown"; - @primitiveTrait({ - type: "string", - name: "ID", - description: "Unique id of the search provider." - }) - id?: string; - @primitiveTrait({ type: "boolean", name: "Open by default", - description: "Wheter are this search provider results open by default" + description: "Wheter are this search provider results open by default", + isNullable: true }) openByDefault: boolean = true; @primitiveTrait({ type: "number", name: "Minimum characters", - description: "Minimum number of characters required for search to start" + description: "Minimum number of characters required for search to start", + isNullable: true }) minCharacters?: number; } From c2608bfc3ea64936a34898b275116ab649214393 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 21 Jan 2021 15:21:00 +0100 Subject: [PATCH 007/129] make translation of names work correctly --- lib/ModelMixins/SerchProvider/SearchProviderMixin.ts | 9 +++++---- lib/Models/SearchProvider/SearchProviderResults.ts | 5 ++--- lib/Models/Terria.ts | 6 +++--- lib/Models/updateModelFromJson.ts | 4 ++++ lib/ReactViews/Search/SearchBoxAndResults.jsx | 9 +++------ .../SearchProvider/LocationSearchProviderTraits.ts | 6 ++++-- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts index 723049bc7df..4c28a48f4f6 100644 --- a/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts @@ -11,12 +11,13 @@ import Terria from "../../Models/Terria"; import ModelTraits from "../../Traits/ModelTraits"; import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; -type SearchProvider = Model; +type SearchProviderMixin = Model; -function SearchProviderMixin>(Base: T) { +function SearchProviderMixin>( + Base: T +) { abstract class SearchProviderMixin extends Base { abstract get type(): string; - @observable name = "Unknown"; @observable isOpen = this.openByDefault; @action @@ -26,7 +27,7 @@ function SearchProviderMixin>(Base: T) { @action search(searchText: string): SearchProviderResults { - const result = new SearchProviderResults(Base); + const result = new SearchProviderResults(this); result.resultsCompletePromise = fromPromise( this.doSearch(searchText, result) ); diff --git a/lib/Models/SearchProvider/SearchProviderResults.ts b/lib/Models/SearchProvider/SearchProviderResults.ts index 78ee3340855..91946a31771 100644 --- a/lib/Models/SearchProvider/SearchProviderResults.ts +++ b/lib/Models/SearchProvider/SearchProviderResults.ts @@ -11,9 +11,8 @@ export default class SearchProviderResults { Promise.resolve() ); - constructor( - readonly searchProvider: SearchProviderMixin.SearchProviderMixin - ) {} + constructor(readonly searchProvider: SearchProviderMixin) { + } get isSearching() { return this.resultsCompletePromise.state === "pending"; diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 899e0ced372..95191e61137 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -309,7 +309,7 @@ export default class Terria { languageConfiguration: undefined, displayOneBrand: 0, searchBar: { - placeholder: "search.placeholder", + placeholder: "translate#search.placeholder", recommendedListLength: 5, sortByName: true, flightDurationSeconds: 1.5, @@ -319,14 +319,14 @@ export default class Terria { { id: "search-provider/bing-maps", type: "bing-maps-search-provider", - name: "search.bingMaps", + name: "translate#viewModels.searchLocations", url: "https://dev.virtualearth.net/", flightDurationSeconds: 1.5 }, { id: "search-provider/australian-gazetteer", type: "australian-gazetteer-search-provider", - name: "viewModels.searchPlaceNames", + name: "translate#viewModels.searchPlaceNames", url: "http://services.ga.gov.au/gis/services/Australian_Gazetteer/MapServer/WFSServer", searchPropertyName: "Australian_Gazetteer:NameU", diff --git a/lib/Models/updateModelFromJson.ts b/lib/Models/updateModelFromJson.ts index e5d59929a8e..7fc34fa6a2e 100644 --- a/lib/Models/updateModelFromJson.ts +++ b/lib/Models/updateModelFromJson.ts @@ -2,6 +2,7 @@ import { runInAction, isObservableArray } from "mobx"; import TerriaError from "../Core/TerriaError"; import createStratumInstance from "./createStratumInstance"; import { BaseModel } from "./Model"; +import { useTranslationIfExists } from "./../Language/languageHelpers"; export default function updateModelFromJson( model: BaseModel, @@ -48,6 +49,9 @@ export default function updateModelFromJson( newTrait ); } + if (propertyName === "name") { + newTrait = useTranslationIfExists(jsonValue); + } model.setTrait(stratumName, propertyName, newTrait); } }); diff --git a/lib/ReactViews/Search/SearchBoxAndResults.jsx b/lib/ReactViews/Search/SearchBoxAndResults.jsx index dfb628c8d6b..1bd33992a9c 100644 --- a/lib/ReactViews/Search/SearchBoxAndResults.jsx +++ b/lib/ReactViews/Search/SearchBoxAndResults.jsx @@ -198,12 +198,9 @@ export class SearchBoxAndResultsRaw extends React.Component { overflow-y: auto; `} > - + {searchState.locationSearchResults.map(search => ( - + ))} diff --git a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts index b7285613ff5..5067a14f335 100644 --- a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts @@ -14,14 +14,16 @@ export default class LocationSearchProviderTraits extends SearchProviderTraits { type: "boolean", name: "Open by default", description: - "True if the geocoder should query as the user types to autocomplete." + "True if the geocoder should query as the user types to autocomplete.", + isNullable: true }) autocomplete?: boolean; @primitiveTrait({ type: "number", name: "URL", - description: "Time to move to the result location." + description: "Time to move to the result location.", + isNullable: true }) flightDurationSeconds?: number; } From d8616bd7b83be15a10f2205a00446f48f085279b Mon Sep 17 00:00:00 2001 From: zoran995 Date: Tue, 16 Feb 2021 11:56:18 +0100 Subject: [PATCH 008/129] Continue work on connecting search providers to UI - tsify some of the search UI components - properly separate the catalog and search bar - use the recommendedListLength when rendering - use isOpen when rendering - add boundingBoxLimit search bar config param --- .../LocationSearchProviderMixin.ts | 69 ++++++ .../SearchProviderMixin.ts | 44 +--- .../WebFeatureServiceSearchProviderMixin.ts | 4 +- .../AustralianGazetteerSearchProvider.ts | 2 +- .../SearchProvider/BingMapsSearchProvider.ts | 6 +- .../SearchProvider/CatalogSearchProvider.ts | 2 +- .../SearchProvider/SearchProviderResults.ts | 7 +- .../SearchProvider/StubSearchProvider.ts | 2 +- .../upsertSearchProviderFromJson.ts | 4 +- lib/Models/Terria.ts | 16 +- lib/ReactViewModels/SearchState.ts | 5 +- lib/ReactViews/Loader.tsx | 12 +- .../Search/LocationSearchResults.jsx | 193 ----------------- .../Search/LocationSearchResults.tsx | 204 ++++++++++++++++++ lib/ReactViews/Search/SearchBox.jsx | 8 +- lib/ReactViews/Search/SearchBoxAndResults.jsx | 26 +-- lib/ReactViews/Search/SearchHeader.jsx | 39 ---- lib/ReactViews/Search/SearchHeader.tsx | 32 +++ lib/ReactViews/Search/SearchResult.jsx | 120 ----------- lib/ReactViews/Search/SearchResult.tsx | 107 +++++++++ .../Search/location-search-result.scss | 83 ------- .../Search/location-search-result.scss.d.ts | 13 -- lib/ReactViews/Search/search-box.scss | 60 ------ lib/ReactViews/Search/search-box.scss.d.ts | 10 - lib/ReactViews/Search/search-result.scss | 95 -------- lib/ReactViews/Search/search-result.scss.d.ts | 17 -- lib/Styled/{List.jsx => List.tsx} | 0 .../LocationSearchProviderTraits.ts | 20 +- .../SearchProvider/SearchProviderTraits.ts | 8 - 29 files changed, 481 insertions(+), 727 deletions(-) create mode 100644 lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts rename lib/ModelMixins/{SerchProvider => SearchProvider}/SearchProviderMixin.ts (54%) rename lib/ModelMixins/{SerchProvider => SearchProvider}/WebFeatureServiceSearchProviderMixin.ts (97%) delete mode 100644 lib/ReactViews/Search/LocationSearchResults.jsx create mode 100644 lib/ReactViews/Search/LocationSearchResults.tsx delete mode 100644 lib/ReactViews/Search/SearchHeader.jsx create mode 100644 lib/ReactViews/Search/SearchHeader.tsx delete mode 100644 lib/ReactViews/Search/SearchResult.jsx create mode 100644 lib/ReactViews/Search/SearchResult.tsx delete mode 100644 lib/ReactViews/Search/location-search-result.scss delete mode 100644 lib/ReactViews/Search/location-search-result.scss.d.ts delete mode 100644 lib/ReactViews/Search/search-box.scss delete mode 100644 lib/ReactViews/Search/search-box.scss.d.ts delete mode 100644 lib/ReactViews/Search/search-result.scss delete mode 100644 lib/ReactViews/Search/search-result.scss.d.ts rename lib/Styled/{List.jsx => List.tsx} (100%) diff --git a/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts new file mode 100644 index 00000000000..faa54b3ad92 --- /dev/null +++ b/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts @@ -0,0 +1,69 @@ +import { action, observable } from "mobx"; +import { fromPromise } from "mobx-utils"; +import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; +import CesiumMath from "terriajs-cesium/Source/Core/Math"; +import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; +import Constructor from "../../Core/Constructor"; +import Model, { BaseModel } from "../../Models/Model"; +import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; +import StratumFromTraits from "../../Models/StratumFromTraits"; +import Terria from "../../Models/Terria"; +import ModelTraits from "../../Traits/ModelTraits"; +import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; +import CommonStrata from "../../Models/CommonStrata"; +import LocationSearchProviderTraits from "../../Traits/SearchProvider/LocationSearchProviderTraits"; +import SearchProviderMixin from "./SearchProviderMixin"; + +type LocationSearchProviderModel = Model; + +function LocationSearchProviderMixin< + T extends Constructor +>(Base: T) { + abstract class LocationSearchProviderMixin extends SearchProviderMixin(Base) { + @action + toggleOpen(stratumId: CommonStrata = CommonStrata.user) { + this.setTrait(stratumId, "isOpen", !this.isOpen); + } + + get hasLocationSearchProviderMixin() { + return true; + } + } + return LocationSearchProviderMixin; +} + +interface MapCenter { + longitude: number; + latitude: number; +} + +export function getMapCenter(terria: Terria): MapCenter { + const view = terria.currentViewer.getCurrentCameraView(); + if (view.position !== undefined) { + const cameraPositionCartographic = Ellipsoid.WGS84.cartesianToCartographic( + view.position + ); + return { + longitude: CesiumMath.toDegrees(cameraPositionCartographic.longitude), + latitude: CesiumMath.toDegrees(cameraPositionCartographic.latitude) + }; + } else { + const center = Rectangle.center(view.rectangle); + return { + longitude: CesiumMath.toDegrees(center.longitude), + latitude: CesiumMath.toDegrees(center.latitude) + }; + } +} + +namespace LocationSearchProviderMixin { + export interface LocationSearchProviderMixin + extends InstanceType> {} + export function isMixedInto( + model: any + ): model is LocationSearchProviderMixin { + return model && model.hasLocationSearchProviderMixin; + } +} + +export default LocationSearchProviderMixin; diff --git a/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts similarity index 54% rename from lib/ModelMixins/SerchProvider/SearchProviderMixin.ts rename to lib/ModelMixins/SearchProvider/SearchProviderMixin.ts index 4c28a48f4f6..cf3e9066936 100644 --- a/lib/ModelMixins/SerchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts @@ -1,29 +1,17 @@ -import { action, observable } from "mobx"; +import { action } from "mobx"; import { fromPromise } from "mobx-utils"; -import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; -import CesiumMath from "terriajs-cesium/Source/Core/Math"; -import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import Constructor from "../../Core/Constructor"; -import Model, { BaseModel } from "../../Models/Model"; +import Model from "../../Models/Model"; import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; -import StratumFromTraits from "../../Models/StratumFromTraits"; -import Terria from "../../Models/Terria"; -import ModelTraits from "../../Traits/ModelTraits"; import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; -type SearchProviderMixin = Model; +type SearchProviderModel = Model; -function SearchProviderMixin>( +function SearchProviderMixin>( Base: T ) { abstract class SearchProviderMixin extends Base { abstract get type(): string; - @observable isOpen = this.openByDefault; - - @action - toggleOpen() { - this.isOpen = !this.isOpen; - } @action search(searchText: string): SearchProviderResults { @@ -69,27 +57,3 @@ namespace SearchProviderMixin { } export default SearchProviderMixin; - -interface MapCenter { - longitude: number; - latitude: number; -} - -export function getMapCenter(terria: Terria): MapCenter { - const view = terria.currentViewer.getCurrentCameraView(); - if (view.position !== undefined) { - const cameraPositionCartographic = Ellipsoid.WGS84.cartesianToCartographic( - view.position - ); - return { - longitude: CesiumMath.toDegrees(cameraPositionCartographic.longitude), - latitude: CesiumMath.toDegrees(cameraPositionCartographic.latitude) - }; - } else { - const center = Rectangle.center(view.rectangle); - return { - longitude: CesiumMath.toDegrees(center.longitude), - latitude: CesiumMath.toDegrees(center.latitude) - }; - } -} diff --git a/lib/ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts similarity index 97% rename from lib/ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin.ts rename to lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts index c8c1a814835..56a1e276079 100644 --- a/lib/ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts @@ -10,12 +10,12 @@ import SearchProviderResults from "../../Models/SearchProvider/SearchProviderRes import SearchResult from "../../Models/SearchProvider/SearchResult"; import xml2json from "../../ThirdParty/xml2json"; import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; -import SearchProviderMixin from "./SearchProviderMixin"; +import LocationSearchProviderMixin from "./LocationSearchProviderMixin"; function WebFeatureServiceSearchProviderMixin< T extends Constructor> >(Base: T) { - abstract class WebFeatureServiceSearchProviderMixin extends SearchProviderMixin( + abstract class WebFeatureServiceSearchProviderMixin extends LocationSearchProviderMixin( Base ) { protected abstract featureToSearchResultFunction: ( diff --git a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts index 2b4d2996215..986f781134d 100644 --- a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts +++ b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts @@ -1,6 +1,6 @@ import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; import CreateModel from "../CreateModel"; -import WebFeatureServiceSearchProviderMixin from "../../ModelMixins/SerchProvider/WebFeatureServiceSearchProviderMixin"; +import WebFeatureServiceSearchProviderMixin from "../../ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin"; import SearchResult from "./SearchResult"; const featureCodesToNamesMap = new Map([ diff --git a/lib/Models/SearchProvider/BingMapsSearchProvider.ts b/lib/Models/SearchProvider/BingMapsSearchProvider.ts index 7adffd9947c..e910f0950ed 100644 --- a/lib/Models/SearchProvider/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProvider/BingMapsSearchProvider.ts @@ -4,9 +4,9 @@ import defined from "terriajs-cesium/Source/Core/defined"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import Resource from "terriajs-cesium/Source/Core/Resource"; import loadJsonp from "../../Core/loadJsonp"; -import SearchProviderMixin, { +import LocationSearchProviderMixin, { getMapCenter -} from "../../ModelMixins/SerchProvider/SearchProviderMixin"; +} from "../../ModelMixins/SearchProvider/LocationSearchProviderMixin"; import BingMapsSearchProviderTraits from "../../Traits/SearchProvider/BingMapsSearchProviderTraits"; import CreateModel from "../CreateModel"; import SearchProviderResults from "./SearchProviderResults"; @@ -14,7 +14,7 @@ import SearchResult from "./SearchResult"; import CommonStrata from "./../CommonStrata"; import Terria from "../Terria"; -export default class BingMapsSearchProvider extends SearchProviderMixin( +export default class BingMapsSearchProvider extends LocationSearchProviderMixin( CreateModel(BingMapsSearchProviderTraits) ) { static readonly type = "bing-maps-search-provider"; diff --git a/lib/Models/SearchProvider/CatalogSearchProvider.ts b/lib/Models/SearchProvider/CatalogSearchProvider.ts index 946b2a4ec67..4bdafa81e5d 100644 --- a/lib/Models/SearchProvider/CatalogSearchProvider.ts +++ b/lib/Models/SearchProvider/CatalogSearchProvider.ts @@ -4,7 +4,7 @@ import ReferenceMixin from "../../ModelMixins/ReferenceMixin"; import CatalogSearchProviderTraits from "../../Traits/SearchProvider/CatalogSearchProviderTraits"; import CreateModel from "../CreateModel"; import Terria from "../Terria"; -import SearchProviderMixin from "../../ModelMixins/SerchProvider/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; diff --git a/lib/Models/SearchProvider/SearchProviderResults.ts b/lib/Models/SearchProvider/SearchProviderResults.ts index 91946a31771..09fcc1aafe5 100644 --- a/lib/Models/SearchProvider/SearchProviderResults.ts +++ b/lib/Models/SearchProvider/SearchProviderResults.ts @@ -1,7 +1,7 @@ import { observable } from "mobx"; import SearchResult from "./SearchResult"; import { IPromiseBasedObservable, fromPromise } from "mobx-utils"; -import SearchProviderMixin from "../../ModelMixins/SerchProvider/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; export default class SearchProviderResults { @observable results: SearchResult[] = []; @@ -11,8 +11,9 @@ export default class SearchProviderResults { Promise.resolve() ); - constructor(readonly searchProvider: SearchProviderMixin) { - } + constructor( + readonly searchProvider: SearchProviderMixin.SearchProviderMixin + ) {} get isSearching() { return this.resultsCompletePromise.state === "pending"; diff --git a/lib/Models/SearchProvider/StubSearchProvider.ts b/lib/Models/SearchProvider/StubSearchProvider.ts index 0737ffad5ff..28745157278 100644 --- a/lib/Models/SearchProvider/StubSearchProvider.ts +++ b/lib/Models/SearchProvider/StubSearchProvider.ts @@ -1,6 +1,6 @@ import LocationSearchProviderTraits from "./../../Traits/SearchProvider/LocationSearchProviderTraits"; import primitiveTrait from "./../../Traits/primitiveTrait"; -import SearchProviderMixin from "../../ModelMixins/SerchProvider/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; import CreateModel from "../CreateModel"; import SearchProviderResults from "./SearchProviderResults"; diff --git a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts index bfbd63735ab..19474f3f14c 100644 --- a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts @@ -47,7 +47,7 @@ export default function upsertSearchProviderFromJson( model?.terria.addSearchProvider(model); } - addDefaultTraits(model); + setDefaultTraits(model); try { updateModelFromJson(model, stratumName, json); @@ -58,7 +58,7 @@ export default function upsertSearchProviderFromJson( } } -function addDefaultTraits(model: BaseModel) { +function setDefaultTraits(model: BaseModel) { const terria = model.terria; model.setTrait( diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index f80853b2cad..ae40c94569b 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -246,6 +246,10 @@ interface SearchBar { * Minimum number of characters to start search. */ minCharacters: number; + /** + * Bounding box limits for the search results. + */ + boundingBoxLimit?: number[]; /** * Array of search providers to be used. */ @@ -401,18 +405,8 @@ export default class Terria { type: "bing-maps-search-provider", name: "translate#viewModels.searchLocations", url: "https://dev.virtualearth.net/", - flightDurationSeconds: 1.5 - }, - { - id: "search-provider/australian-gazetteer", - type: "australian-gazetteer-search-provider", - name: "translate#viewModels.searchPlaceNames", - url: - "http://services.ga.gov.au/gis/services/Australian_Gazetteer/MapServer/WFSServer", - searchPropertyName: "Australian_Gazetteer:NameU", - searchPropertyTypeName: "Australian_Gazetteer:Gazetteer_of_Australia", flightDurationSeconds: 1.5, - minCharacters: 3 + isOpen: true } ] } diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index a12ebd79085..fc7373547e0 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -6,10 +6,11 @@ import { reaction } from "mobx"; import filterOutUndefined from "../Core/filterOutUndefined"; +import LocationSearchProviderMixin from "../ModelMixins/SearchProvider/LocationSearchProviderMixin"; +import SearchProviderMixin from "../ModelMixins/SearchProvider/SearchProviderMixin"; import CatalogSearchProvider from "../Models/SearchProvider/CatalogSearchProvider"; import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; import Terria from "../Models/Terria"; -import SearchProviderMixin from "../ModelMixins/SerchProvider/SearchProviderMixin"; interface SearchStateOptions { terria: Terria; @@ -22,7 +23,7 @@ export default class SearchState { catalogSearchProvider: SearchProviderMixin.SearchProviderMixin | undefined; @observable - locationSearchProviders: SearchProviderMixin.SearchProviderMixin[]; + locationSearchProviders: LocationSearchProviderMixin.LocationSearchProviderMixin[]; @observable catalogSearchText: string = ""; @observable isWaitingToStartCatalogSearch: boolean = false; diff --git a/lib/ReactViews/Loader.tsx b/lib/ReactViews/Loader.tsx index a8a6c816fa0..0d5a62f1d50 100644 --- a/lib/ReactViews/Loader.tsx +++ b/lib/ReactViews/Loader.tsx @@ -10,11 +10,19 @@ interface PropsType extends WithTranslation { message?: string; boxProps?: any; textProps?: any; + hideMessage?: boolean; t: TFunction; [spread: string]: any; } const Loader: React.FC = (props: PropsType) => { - const { message, t, boxProps, textProps, ...rest }: PropsType = props; + const { + message, + t, + boxProps, + textProps, + hideMessage, + ...rest + }: PropsType = props; return ( = (props: PropsType) => { {...rest} /> - {message || t("loader.loadingMessage")} + {!hideMessage && (message || t("loader.loadingMessage"))} ); diff --git a/lib/ReactViews/Search/LocationSearchResults.jsx b/lib/ReactViews/Search/LocationSearchResults.jsx deleted file mode 100644 index 54c1d7320db..00000000000 --- a/lib/ReactViews/Search/LocationSearchResults.jsx +++ /dev/null @@ -1,193 +0,0 @@ -/** - Initially this was written to support various location search providers in master, - however we only have a single location provider at the moment, and how we merge - them in the new design is yet to be resolved, see: - https://github.com/TerriaJS/nsw-digital-twin/issues/248#issuecomment-599919318 - */ - -import { observer } from "mobx-react"; -import React from "react"; -import createReactClass from "create-react-class"; -import styled from "styled-components"; -import PropTypes from "prop-types"; -import { withTranslation } from "react-i18next"; -import SearchHeader from "./SearchHeader"; -import SearchResult from "./SearchResult"; -import classNames from "classnames"; -import Styles from "./location-search-result.scss"; -import isDefined from "../../Core/isDefined"; - -import Icon, { StyledIcon } from "../Icon"; -// import Box, { BoxSpan } from "../../Styled/Box"; -import { BoxSpan } from "../../Styled/Box"; -import Text, { TextSpan } from "../../Styled/Text"; - -import { RawButton } from "../../Styled/Button"; - -const RawButtonAndHighlight = styled(RawButton)` - ${p => ` - &:hover, &:focus { - background-color: ${p.theme.greyLighter}; - ${StyledIcon} { - fill-opacity: 1; - } - }`} -`; - -const MAX_RESULTS_BEFORE_TRUNCATING = 5; - -const LocationSearchResults = observer( - createReactClass({ - displayName: "LocationSearchResults", - - propTypes: { - viewState: PropTypes.object.isRequired, - isWaitingForSearchToStart: PropTypes.bool, - terria: PropTypes.object.isRequired, - search: PropTypes.object.isRequired, - onLocationClick: PropTypes.func.isRequired, - theme: PropTypes.string, - locationSearchText: PropTypes.string, - t: PropTypes.func.isRequired - }, - - getInitialState() { - return { - isOpen: true, - isExpanded: false - }; - }, - - getDefaultProps() { - return { - theme: "light" - }; - }, - - toggleIsOpen() { - this.setState({ - isOpen: !this.state.isOpen - }); - }, - - toggleExpand() { - this.setState({ - isExpanded: !this.state.isExpanded - }); - }, - - renderResultsFooter() { - const { t } = this.props; - if (this.state.isExpanded) { - return t("search.viewLess", { - name: this.props.search.searchProvider.name - }); - } - return t("search.viewMore", { - name: this.props.search.searchProvider.name - }); - }, - - render() { - const search = this.props.search; - const { isOpen, isExpanded } = this.state; - const searchProvider = search.searchProvider; - const locationSearchBoundingBox = this.props.terria.configParameters - .locationSearchBoundingBox; - - const validResults = isDefined(locationSearchBoundingBox) - ? search.results.filter(function(r) { - return ( - r.location.longitude > locationSearchBoundingBox[0] && - r.location.longitude < locationSearchBoundingBox[2] && - r.location.latitude > locationSearchBoundingBox[1] && - r.location.latitude < locationSearchBoundingBox[3] - ); - }) - : search.results; - - const results = - validResults.length > MAX_RESULTS_BEFORE_TRUNCATING - ? isExpanded - ? validResults - : validResults.slice(0, MAX_RESULTS_BEFORE_TRUNCATING) - : validResults; - - return ( -
- {/* */} - - - {`${search.searchProvider.name} (${validResults?.length})`} - - - - - -
    - {results.map((result, i) => ( - - ))} -
- {isOpen && validResults.length > MAX_RESULTS_BEFORE_TRUNCATING && ( - - - - {this.renderResultsFooter()} - - - - )} -
-
- ); - } - }) -); - -module.exports = withTranslation()(LocationSearchResults); diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx new file mode 100644 index 00000000000..1ad36202da2 --- /dev/null +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -0,0 +1,204 @@ +/** + Initially this was written to support various location search providers in master, + however we only have a single location provider at the moment, and how we merge + them in the new design is yet to be resolved, see: + https://github.com/TerriaJS/nsw-digital-twin/issues/248#issuecomment-599919318 + */ + +import { observable, computed, action } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; +import { + useTranslation, + withTranslation, + WithTranslation +} from "react-i18next"; +import styled, { DefaultTheme } from "styled-components"; +import isDefined from "../../Core/isDefined"; +import Terria from "../../Models/Terria"; +import ViewState from "../../ReactViewModels/ViewState"; +import Ul from "../../Styled/List"; +import Icon, { StyledIcon } from "../Icon"; +import LocationSearchProviderMixin from "./../../ModelMixins/SearchProvider/LocationSearchProviderMixin"; +import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; +import SearchHeader from "./SearchHeader"; +import SearchResult from "./SearchResult"; +import Loader from "../Loader"; +const BoxSpan: any = require("../../Styled/Box").BoxSpan; +const Box: any = require("../../Styled/Box").default; +const Text: any = require("../../Styled/Text").default; +const TextSpan: any = require("../../Styled/Text").TextSpan; +const RawButton: any = require("../../Styled/Button").RawButton; + +const RawButtonAndHighlight = styled(RawButton)` + ${p => ` + &:hover, &:focus { + background-color: ${p.theme.greyLighter}; + ${StyledIcon} { + fill-opacity: 1; + } + }`} +`; + +interface PropsType extends WithTranslation { + viewState: ViewState; + isWaitingForSearchToStart: boolean; + terria: Terria; + search: SearchProviderResults; + onLocationClick: () => void; + theme: DefaultTheme; + locationSearchText: string; +} + +@observer +class LocationSearchResults extends React.Component { + @observable isExpanded = false; + + @action.bound + toggleExpand() { + this.isExpanded = !this.isExpanded; + } + + @computed + get validResults() { + const { search, terria } = this.props; + const locationSearchBoundingBox = + terria.configParameters.searchBar.boundingBoxLimit; + const validResults = isDefined(locationSearchBoundingBox) + ? search.results.filter(function(r: any) { + return ( + r.location.longitude > locationSearchBoundingBox[0] && + r.location.longitude < locationSearchBoundingBox[2] && + r.location.latitude > locationSearchBoundingBox[1] && + r.location.latitude < locationSearchBoundingBox[3] + ); + }) + : search.results; + return validResults; + } + + render() { + const { search } = this.props; + const searchProvider: LocationSearchProviderMixin.LocationSearchProviderMixin = search.searchProvider as any; + + const maxResults = searchProvider.recommendedListLength || 5; + const validResults = this.validResults; + const results = + validResults.length > maxResults + ? this.isExpanded + ? validResults + : validResults.slice(0, maxResults) + : validResults; + const isOpen = searchProvider.isOpen; + return ( + + searchProvider.toggleOpen()} + > + + + + + + + {isOpen && ( + <> + +
    + {results.map((result: any, i: number) => ( + + ))} +
+ {validResults.length > maxResults && ( + + + + + + + + )} + + )} +
+
+ ); + } +} + +interface SearchResultsFooterProps { + isExpanded: boolean; + name: string; +} + +const SearchResultsFooter: React.FC = ( + props: SearchResultsFooterProps +) => { + const { t } = useTranslation(); + if (props.isExpanded) { + return t("search.viewLess", { + name: props.name + }); + } + return t("search.viewMore", { + name: props.name + }); +}; + +interface NameWithLoaderProps { + name: string; + length?: number; + isOpen: boolean; + search: SearchProviderResults; + isWaitingForSearchToStart: boolean; +} + +const NameWithLoader: React.FC = observer( + (props: NameWithLoaderProps) => ( + + + {`${props.name} (${props.length || + 0})`} + + {!props.isOpen && + (props.search.isSearching || props.isWaitingForSearchToStart) && ( + + )} + + ) +); +export default withTranslation()(LocationSearchResults); diff --git a/lib/ReactViews/Search/SearchBox.jsx b/lib/ReactViews/Search/SearchBox.jsx index 0027ba02355..74ccf71f097 100644 --- a/lib/ReactViews/Search/SearchBox.jsx +++ b/lib/ReactViews/Search/SearchBox.jsx @@ -1,12 +1,12 @@ -import React from "react"; -import PropTypes from "prop-types"; import createReactClass from "create-react-class"; import debounce from "lodash-es/debounce"; -import Icon, { StyledIcon } from "../Icon"; +import PropTypes from "prop-types"; +import React from "react"; import styled, { withTheme } from "styled-components"; import Box, { BoxSpan } from "../../Styled/Box"; -import Text from "../../Styled/Text"; import { RawButton } from "../../Styled/Button"; +import Text from "../../Styled/Text"; +import Icon, { StyledIcon } from "../Icon"; const SearchInput = styled.input` box-sizing: border-box; diff --git a/lib/ReactViews/Search/SearchBoxAndResults.jsx b/lib/ReactViews/Search/SearchBoxAndResults.jsx index 1bd33992a9c..6765ebe623c 100644 --- a/lib/ReactViews/Search/SearchBoxAndResults.jsx +++ b/lib/ReactViews/Search/SearchBoxAndResults.jsx @@ -1,23 +1,19 @@ -import React from "react"; -import { removeMarker } from "../../Models/LocationMarkerUtils"; import { reaction, runInAction } from "mobx"; -import { Trans } from "react-i18next"; -import PropTypes from "prop-types"; import { observer } from "mobx-react"; +import PropTypes from "prop-types"; +import React from "react"; +import { Trans } from "react-i18next"; import styled from "styled-components"; -// import { ThemeContext } from "styled-components"; - -import SearchBox from "../Search/SearchBox"; -// import SidebarSearch from "../Search/SidebarSearch"; -import LocationSearchResults from "../Search/LocationSearchResults"; -import Icon, { StyledIcon } from "../Icon"; - +import { addMarker, removeMarker } from "../../Models/LocationMarkerUtils"; import Box from "../../Styled/Box"; -import Text from "../../Styled/Text"; -import Spacing from "../../Styled/Spacing"; import { RawButton } from "../../Styled/Button"; - -import { addMarker } from "../../Models/LocationMarkerUtils"; +import Spacing from "../../Styled/Spacing"; +import Text from "../../Styled/Text"; +import Icon, { StyledIcon } from "../Icon"; +// import SidebarSearch from "../Search/SidebarSearch"; +import LocationSearchResults from "./LocationSearchResults"; +// import { ThemeContext } from "styled-components"; +import SearchBox from "./SearchBox"; export function SearchInDataCatalog({ viewState, handleClick }) { const locationSearchText = viewState.searchState.locationSearchText; diff --git a/lib/ReactViews/Search/SearchHeader.jsx b/lib/ReactViews/Search/SearchHeader.jsx deleted file mode 100644 index bbc5f03260c..00000000000 --- a/lib/ReactViews/Search/SearchHeader.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import Loader from "../Loader"; -import { observer } from "mobx-react"; -import React from "react"; -import createReactClass from "create-react-class"; -import PropTypes from "prop-types"; -import Styles from "./search-header.scss"; - -/** Renders either a loader or a message based off search state. */ -export default observer( - createReactClass({ - displayName: "SearchHeader", - - propTypes: { - searchResults: PropTypes.object.isRequired, - isWaitingForSearchToStart: PropTypes.bool - }, - - render() { - if ( - this.props.searchResults.isSearching || - this.props.isWaitingForSearchToStart - ) { - return ( -
- -
- ); - } else if (this.props.searchResults.message) { - return ( -
- {this.props.searchResults.message} -
- ); - } else { - return null; - } - } - }) -); diff --git a/lib/ReactViews/Search/SearchHeader.tsx b/lib/ReactViews/Search/SearchHeader.tsx new file mode 100644 index 00000000000..5ead1157445 --- /dev/null +++ b/lib/ReactViews/Search/SearchHeader.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import Loader from "../Loader"; +import { observer } from "mobx-react"; +const Text = require("../../Styled/Text").default; +const BoxSpan = require("../../Styled/Box").BoxSpan; + +interface SearchHeaderProps { + searchResults: { [key: string]: any }; + isWaitingForSearchToStart: boolean; +} + +const SearchHeader: React.FC = observer( + (props: SearchHeaderProps) => { + if (props.searchResults.isSearching || props.isWaitingForSearchToStart) { + return ( +
+ +
+ ); + } else if (props.searchResults.message) { + return ( + + {props.searchResults.message} + + ); + } else { + return null; + } + } +); + +export default SearchHeader; diff --git a/lib/ReactViews/Search/SearchResult.jsx b/lib/ReactViews/Search/SearchResult.jsx deleted file mode 100644 index 491feb2ffcc..00000000000 --- a/lib/ReactViews/Search/SearchResult.jsx +++ /dev/null @@ -1,120 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import styled, { withTheme } from "styled-components"; -import createReactClass from "create-react-class"; -import Icon, { StyledIcon } from "../Icon"; - -import Box, { BoxSpan } from "../../Styled/Box"; -import { RawButton } from "../../Styled/Button"; -import { TextSpan } from "../../Styled/Text"; -import Hr from "../../Styled/Hr"; -import Spacing, { SpacingSpan } from "../../Styled/Spacing"; - -import highlightKeyword from "../ReactViewHelpers/highlightKeyword"; - -// Not sure how to generalise this or if it should be kept in stlyed/Button.jsx - -// Initially had this as border bottom on the button, but need a HR given it's not a full width border -// // ${p => !p.isLastResult && `border-bottom: 1px solid ${p.theme.greyLighter};`} -const RawButtonAndHighlight = styled(RawButton)` - ${p => ` - &:hover, &:focus { - background-color: ${p.theme.greyLighter}; - ${StyledIcon} { - fill-opacity: 1; - } - }`} -`; - -// A Location item when doing Bing map searvh or Gazetter search -export const SearchResult = createReactClass({ - propTypes: { - name: PropTypes.string.isRequired, - clickAction: PropTypes.func.isRequired, - isLastResult: PropTypes.bool, - locationSearchText: PropTypes.string, - icon: PropTypes.string, - theme: PropTypes.object, - searchResultTheme: PropTypes.string - }, - - getDefaultProps() { - return { - icon: false, - searchResultTheme: "light" - }; - }, - - render() { - const { - searchResultTheme, - theme, - name, - locationSearchText, - icon - // isLastResult - } = this.props; - const isDarkTheme = searchResultTheme === "dark"; - const isLightTheme = searchResultTheme === "light"; - const highlightedResultName = highlightKeyword(name, locationSearchText); - return ( -
  • - - - {/* {!isLastResult && ( */} - - -
    - -
    - {/* )} */} - - - {icon && ( - - )} - - - - {highlightedResultName} - - - - - -
    -
    -
  • - ); - } -}); - -export default withTheme(SearchResult); diff --git a/lib/ReactViews/Search/SearchResult.tsx b/lib/ReactViews/Search/SearchResult.tsx new file mode 100644 index 00000000000..a431a4278cc --- /dev/null +++ b/lib/ReactViews/Search/SearchResult.tsx @@ -0,0 +1,107 @@ +import React from "react"; +import styled, { useTheme } from "styled-components"; +import { Li } from "../../Styled/List"; +import Icon, { StyledIcon } from "../Icon"; +import highlightKeyword from "../ReactViewHelpers/highlightKeyword"; + +const Box = require("../../Styled/Box").default; +const BoxSpan = require("../../Styled/Box").BoxSpan; +const TextSpan = require("../../Styled/Text").TextSpan; +const RawButton = require("../../Styled/Button").RawButton; +const Spacing = require("../../Styled/Spacing").default; +const SpacingSpan = require("../../Styled/Spacing").SpacingSpan; +const Hr = require("../../Styled/Hr").default; + +// Not sure how to generalise this or if it should be kept in stlyed/Button.jsx + +// Initially had this as border bottom on the button, but need a HR given it's not a full width border +// // ${p => !p.isLastResult && `border-bottom: 1px solid ${p.theme.greyLighter};`} +const RawButtonAndHighlight = styled(RawButton)` + ${p => ` + &:hover, &:focus { + background-color: ${p.theme.greyLighter}; + ${StyledIcon} { + fill-opacity: 1; + } + }`} +`; + +interface SearchResultProps { + name: string; + clickAction(): void; + isLastResult: boolean; + locationSearchText: string; + icon: string; +} + +const SearchResult: React.FC = ( + props: SearchResultProps +) => { + const theme = useTheme(); + const highlightedResultName = highlightKeyword( + props.name, + props.locationSearchText + ); + const isLightTheme = true; + const isDarkTheme = false; + return ( +
  • + + + {/* {!isLastResult && ( */} + + +
    + +
    + {/* )} */} + + + {props.icon && ( + + )} + + + + {highlightedResultName} + + + + + +
    +
    +
  • + ); +}; + +export default SearchResult; diff --git a/lib/ReactViews/Search/location-search-result.scss b/lib/ReactViews/Search/location-search-result.scss deleted file mode 100644 index b6455bd65e3..00000000000 --- a/lib/ReactViews/Search/location-search-result.scss +++ /dev/null @@ -1,83 +0,0 @@ -@import "../../Sass/common/mixins"; -@import "~terriajs-variables"; - -.heading { - composes: btn from "../../Sass/common/_buttons.scss"; - padding: $padding; - position: relative; - width: 100%; - text-align: left; - font-weight: bold; - &, - &:hover, - &:focus { - box-shadow: inset 0 -1px 0 0 rgba(255, 255, 255, 0.15); - } - @media (min-width: $md) { - color: $text-light; - } - svg { - height: 10px; - width: 10px; - position: absolute; - right: $padding + $padding-small; - top: $padding + $padding-small; - } -} - -.footer { - composes: btn from "../../Sass/common/_buttons.scss"; - padding: $padding; - font-size: 12px; - position: relative; - width: 100%; - text-align: left; - box-shadow: none; - @media (min-width: $md) { - color: $text-light; - } - svg { - height: 20px; - width: 20px; - position: absolute; - right: $padding; - top: $padding; - } -} - -.provider-result { - margin-top: $padding-small; - @media (min-width: $md) { - color: $text-darker; - } - // background-color: $dark-with-overlay; - .items { - display: none; - } -} - -// on mobile, we don't want the gap between search results -.light { - // background: #fff; - margin-top: 0; - svg { - // fill: #000; - } -} - -.dark { - svg { - // fill: #ffffff; - } -} - -.isOpen { - .items { - display: block; - } -} - -.items { - composes: clearfix from "../../Sass/common/_base.scss"; - composes: list-reset from "../../Sass/common/_base.scss"; -} diff --git a/lib/ReactViews/Search/location-search-result.scss.d.ts b/lib/ReactViews/Search/location-search-result.scss.d.ts deleted file mode 100644 index d53b3b8dd6a..00000000000 --- a/lib/ReactViews/Search/location-search-result.scss.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'footer': string; - 'heading': string; - 'isOpen': string; - 'items': string; - 'light': string; - 'provider-result': string; - 'providerResult': string; -} -declare var cssExports: CssExports; -export = cssExports; diff --git a/lib/ReactViews/Search/search-box.scss b/lib/ReactViews/Search/search-box.scss deleted file mode 100644 index 204fec49fd4..00000000000 --- a/lib/ReactViews/Search/search-box.scss +++ /dev/null @@ -1,60 +0,0 @@ -@import "~terriajs-variables"; -@import "../../Sass/common/mixins"; - -.formLabel { - position: absolute; - svg { - // height: $input-height; - // width: $input-height; - height: 20px; - width: 20px; - fill: $charcoal-grey; - padding: $padding; - fill-opacity: 0.5; - } -} - -.searchField { - composes: field from "../../Sass/common/_form.scss"; - font-family: $font-base; -} - -.searchData { - position: relative; - width: 100%; -} - -input[type="text"].searchField { - padding-left: $input-height; - padding-right: $input-height; - color: $text-dark; - width: 100%; - overflow: hidden; - border-color: transparent; // if you need to remove borders, always use transparent "X"px borders instead of 0 borders for a11y - @include placeholder { - text-align: center; - @include transition(all, 0.25s, linear); - } - - &:focus { - @include placeholder { - padding-left: 0; - } - } -} - -.searchClear { - composes: btn from "../../Sass/common/_buttons.scss"; - right: 0px; - top: 0px; - position: absolute; - height: $input-height; - width: $input-height; - svg { - height: 15px; - width: 15px; - margin: 0 auto; - fill: $charcoal-grey; - fill-opacity: 0.5; - } -} diff --git a/lib/ReactViews/Search/search-box.scss.d.ts b/lib/ReactViews/Search/search-box.scss.d.ts deleted file mode 100644 index d7e28504b25..00000000000 --- a/lib/ReactViews/Search/search-box.scss.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'formLabel': string; - 'searchClear': string; - 'searchData': string; - 'searchField': string; -} -declare var cssExports: CssExports; -export = cssExports; diff --git a/lib/ReactViews/Search/search-result.scss b/lib/ReactViews/Search/search-result.scss deleted file mode 100644 index 618d47f3cce..00000000000 --- a/lib/ReactViews/Search/search-result.scss +++ /dev/null @@ -1,95 +0,0 @@ -@import "~terriajs-variables"; - -.search-result { - span { - overflow-wrap: break-word; - word-wrap: break-word; - } -} - -.search-result.light .btn { - // border-bottom: 1px solid rgba($grey, 0.4); - background: transparent; - &:hover { - // border-bottom: 1px solid $color-primary; - color: $color-primary; - } - svg { - fill: #000; - } -} - -.search-result.dark .btn { - color: #fff; - border: 0; - box-shadow: inset 0 -1px 0 0 rgba(255, 255, 255, 0.15); - margin-bottom: 0; - svg { - fill-opacity: 0.5; - } - &:hover { - svg { - fill: #fff; - } - } -} - -.icon { - position: absolute; - top: 5px; - bottom: 0; - // TODO: better icon management - left: -3px; - height: 21px; - width: 17px; - margin: 7px 0; -} - -.resultName { - padding-left: 20px; -} - -.arrow-icon { - position: absolute; - top: 5px; - bottom: 0; - right: 1px; - height: 10px; - width: 10px; - width: 14px; - margin: 8px; - // margin: $padding; - - svg { - fill-opacity: 0; - } -} - -.btn { - composes: btn from "../../Sass/common/_buttons.scss"; - &.btn { - position: relative; - padding: $padding; - padding-left: 0; - margin-bottom: $padding-mini; - padding-right: $padding; - width: 100%; - border: 1px solid transparent; - } - &:hover { - background-color: $grey-lighter; - .arrow-icon svg { - fill-opacity: 1; - } - } - &.btnLocationName { - padding: $padding $padding * 3; - } -} -.btnWithBorderBottom { - &.btnWithBorderBottom { - @media (min-width: $sm) { - border-bottom: 1px solid $grey; - } - } -} diff --git a/lib/ReactViews/Search/search-result.scss.d.ts b/lib/ReactViews/Search/search-result.scss.d.ts deleted file mode 100644 index ba4c634fc8a..00000000000 --- a/lib/ReactViews/Search/search-result.scss.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'arrow-icon': string; - 'arrowIcon': string; - 'btn': string; - 'btnLocationName': string; - 'btnWithBorderBottom': string; - 'dark': string; - 'icon': string; - 'light': string; - 'resultName': string; - 'search-result': string; - 'searchResult': string; -} -declare var cssExports: CssExports; -export = cssExports; diff --git a/lib/Styled/List.jsx b/lib/Styled/List.tsx similarity index 100% rename from lib/Styled/List.jsx rename to lib/Styled/List.tsx diff --git a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts index 5067a14f335..06f73b469a2 100644 --- a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts @@ -17,7 +17,14 @@ export default class LocationSearchProviderTraits extends SearchProviderTraits { "True if the geocoder should query as the user types to autocomplete.", isNullable: true }) - autocomplete?: boolean; + autocomplete?: boolean = true; + + @primitiveTrait({ + type: "number", + name: "recommendedListLength", + description: "Maximum amount of entries in the suggestion list." + }) + recommendedListLength: number = 5; @primitiveTrait({ type: "number", @@ -25,7 +32,16 @@ export default class LocationSearchProviderTraits extends SearchProviderTraits { description: "Time to move to the result location.", isNullable: true }) - flightDurationSeconds?: number; + flightDurationSeconds?: number = 1.5; + + @primitiveTrait({ + type: "boolean", + name: "Is open", + description: + "True if the search results of this search provider are visible; otherwise, false.", + isNullable: true + }) + isOpen: boolean = true; } export class SearchProviderMapCenterTraits extends ModelTraits { diff --git a/lib/Traits/SearchProvider/SearchProviderTraits.ts b/lib/Traits/SearchProvider/SearchProviderTraits.ts index a023dfcb040..f19e6449184 100644 --- a/lib/Traits/SearchProvider/SearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/SearchProviderTraits.ts @@ -9,14 +9,6 @@ export default class SearchProviderTraits extends ModelTraits { }) name: string = "unknown"; - @primitiveTrait({ - type: "boolean", - name: "Open by default", - description: "Wheter are this search provider results open by default", - isNullable: true - }) - openByDefault: boolean = true; - @primitiveTrait({ type: "number", name: "Minimum characters", From 030b913918ae35e216376b792737431da576dc3f Mon Sep 17 00:00:00 2001 From: zoran995 Date: Tue, 16 Feb 2021 11:57:18 +0100 Subject: [PATCH 009/129] restore australian-gazetteer-search-provider as example --- lib/Models/Terria.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index ae40c94569b..42d38bdee19 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -407,6 +407,19 @@ export default class Terria { url: "https://dev.virtualearth.net/", flightDurationSeconds: 1.5, isOpen: true + }, + { + id: "search-provider/australian-gazetteer", + type: "australian-gazetteer-search-provider", + name: "translate#viewModels.searchPlaceNames", + url: + "http://services.ga.gov.au/gis/services/Australian_Gazetteer/MapServer/WFSServer", + searchPropertyName: "Australian_Gazetteer:NameU", + searchPropertyTypeName: "Australian_Gazetteer:Gazetteer_of_Australia", + flightDurationSeconds: 1.5, + minCharacters: 3, + recommendedListLength: 3, + isOpen: false } ] } From e826dd66195167aba8cd4e01f99f5c56ab12727c Mon Sep 17 00:00:00 2001 From: zoran995 Date: Tue, 16 Feb 2021 13:29:30 +0100 Subject: [PATCH 010/129] move shoudRunSearch to search function so we don't have to implement it for every search provider --- lib/Language/en/translation.json | 2 ++ lib/ModelMixins/SearchProvider/SearchProviderMixin.ts | 10 +++++++++- lib/Models/SearchProvider/BingMapsSearchProvider.ts | 4 ---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/Language/en/translation.json b/lib/Language/en/translation.json index a4394b46fe2..ddd59794964 100644 --- a/lib/Language/en/translation.json +++ b/lib/Language/en/translation.json @@ -607,6 +607,8 @@ "viewModels": { "searchNoLocations": "Sorry, no locations match your search query.", "searchErrorOccurred": "An error occurred while searching. Please check your internet connection or try again later.", + "seachMinCharacters": "You need to enter minimum {{count}} character", + "seachMinCharacters_plural": "You need to enter minimum {{count}} characters", "searchAddresses": "Addresses", "searchPlaceNames": "Official Place Names", "searchNoPlaceNames": "Sorry, no official place names match your search query.", diff --git a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts index cf3e9066936..fbadf029f90 100644 --- a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts @@ -4,6 +4,7 @@ import Constructor from "../../Core/Constructor"; import Model from "../../Models/Model"; import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; +import i18next from "i18next"; type SearchProviderModel = Model; @@ -16,6 +17,13 @@ function SearchProviderMixin>( @action search(searchText: string): SearchProviderResults { const result = new SearchProviderResults(this); + if (!this.shouldRunSearch(searchText)) { + result.resultsCompletePromise = fromPromise(Promise.resolve()); + result.message = i18next.t("viewModels.seachMinCharacters", { + count: this.minCharacters + }); + return result; + } result.resultsCompletePromise = fromPromise( this.doSearch(searchText, result) ); @@ -27,7 +35,7 @@ function SearchProviderMixin>( results: SearchProviderResults ): Promise; - shouldRunSearch(searchText: string) { + private shouldRunSearch(searchText: string) { if ( searchText === undefined || /^\s*$/.test(searchText) || diff --git a/lib/Models/SearchProvider/BingMapsSearchProvider.ts b/lib/Models/SearchProvider/BingMapsSearchProvider.ts index e910f0950ed..4bbd5d2035d 100644 --- a/lib/Models/SearchProvider/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProvider/BingMapsSearchProvider.ts @@ -52,10 +52,6 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( searchResults.results.length = 0; searchResults.message = undefined; - if (this.shouldRunSearch(searchText)) { - return Promise.resolve(); - } - this.terria.analytics.logEvent("search", "bing", searchText); const searchQuery = new Resource({ From e388d1e026f4bf4945614b9ca0607fe6c70390d9 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Tue, 16 Feb 2021 13:30:42 +0100 Subject: [PATCH 011/129] remove autocomplete and sortByName for later implementation if needed --- lib/Models/Terria.ts | 15 ++------------- .../LocationSearchProviderTraits.ts | 9 --------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 5347a8db651..fcbb4d3f385 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -223,7 +223,7 @@ interface ConfigParameters { interface SearchBar { /** * Input text field placeholder shown when no input has been given yet. The string is translateable. - * @default "search.placeholder" + * @default "translate#search.placeholder" */ placeholder: string; /** @@ -231,21 +231,11 @@ interface SearchBar { * @default 5 */ recommendedListLength: number; - /** - * Defines whether search results are to be sorted alphanumerically. - * @default true - */ - sortByName: boolean; /** * The duration of the camera flight to an entered location, in seconds. * @default 1.5 */ flightDurationSeconds: number; - /** - * True if the geocoder should query as the user types to autocomplete. - * @default true - */ - autocomplete: boolean; /** * Minimum number of characters to start search. */ @@ -400,9 +390,7 @@ export default class Terria { searchBar: { placeholder: "translate#search.placeholder", recommendedListLength: 5, - sortByName: true, flightDurationSeconds: 1.5, - autocomplete: true, minCharacters: 3, searchProviders: [ { @@ -411,6 +399,7 @@ export default class Terria { name: "translate#viewModels.searchLocations", url: "https://dev.virtualearth.net/", flightDurationSeconds: 1.5, + minCharacters: 5, isOpen: true }, { diff --git a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts index 06f73b469a2..2893c7ea07f 100644 --- a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts @@ -10,15 +10,6 @@ export default class LocationSearchProviderTraits extends SearchProviderTraits { }) url: string = ""; - @primitiveTrait({ - type: "boolean", - name: "Open by default", - description: - "True if the geocoder should query as the user types to autocomplete.", - isNullable: true - }) - autocomplete?: boolean = true; - @primitiveTrait({ type: "number", name: "recommendedListLength", From 34570f3b64093d0f3fcbe4f2d4cdc48af1b0e51d Mon Sep 17 00:00:00 2001 From: zoran995 Date: Wed, 17 Feb 2021 17:55:24 +0100 Subject: [PATCH 012/129] make searchBar optional in config --- lib/Models/Terria.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index fcbb4d3f385..c0b5eae898a 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -217,7 +217,7 @@ interface ConfigParameters { /** * The search bar allows requesting information from various search services at once. */ - searchBar: SearchBar; + searchBar?: SearchBar; } interface SearchBar { From 53c42107300eae7780630f088ffcd3eabf050e3a Mon Sep 17 00:00:00 2001 From: zoran995 Date: Wed, 17 Feb 2021 18:03:41 +0100 Subject: [PATCH 013/129] resolve issues with optional searchBar --- lib/ModelMixins/SearchProvider/SearchProviderMixin.ts | 2 +- .../WebFeatureServiceSearchProviderMixin.ts | 2 +- .../SearchProvider/upsertSearchProviderFromJson.ts | 6 +++--- lib/Models/Terria.ts | 2 +- lib/ReactViews/Search/LocationSearchResults.tsx | 4 ++-- .../LocationSearchProviderTraitsSpec.ts | 11 +++++++++++ 6 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts diff --git a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts index fbadf029f90..606fc07735b 100644 --- a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts @@ -42,7 +42,7 @@ function SearchProviderMixin>( (this.minCharacters && searchText.length < this.minCharacters) || (this.minCharacters === undefined && searchText.length < - this.terria.configParameters.searchBar.minCharacters) + this.terria.configParameters.searchBar!.minCharacters) ) { return false; } diff --git a/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts index 56a1e276079..dad3a5d4a16 100644 --- a/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts @@ -213,7 +213,7 @@ function createZoomToFunction( const flightDurationSeconds: number = model.flightDurationSeconds || - model.terria.configParameters.searchBar.flightDurationSeconds; + model.terria.configParameters.searchBar!.flightDurationSeconds; return function() { model.terria.currentViewer.zoomTo(rectangle, flightDurationSeconds); diff --git a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts index 19474f3f14c..4921a9c561d 100644 --- a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts @@ -64,18 +64,18 @@ function setDefaultTraits(model: BaseModel) { model.setTrait( CommonStrata.defaults, "flightDurationSeconds", - terria.configParameters.searchBar.flightDurationSeconds + terria.configParameters.searchBar!.flightDurationSeconds ); model.setTrait( CommonStrata.defaults, "minCharacters", - terria.configParameters.searchBar.minCharacters + terria.configParameters.searchBar!.minCharacters ); model.setTrait( CommonStrata.defaults, "recommendedListLength", - terria.configParameters.searchBar.recommendedListLength + terria.configParameters.searchBar!.recommendedListLength ); } diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index c0b5eae898a..6b96e90f3bd 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -730,7 +730,7 @@ export default class Terria { } }) .then(() => { - let searchProviders = this.configParameters.searchBar.searchProviders; + let searchProviders = this.configParameters.searchBar!.searchProviders; if (!isObservableArray(searchProviders)) throw new TerriaError({ sender: SearchProviderFactory, diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 1ad36202da2..7b8e5bc2abd 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -62,8 +62,8 @@ class LocationSearchResults extends React.Component { @computed get validResults() { const { search, terria } = this.props; - const locationSearchBoundingBox = - terria.configParameters.searchBar.boundingBoxLimit; + const locationSearchBoundingBox = terria.configParameters.searchBar! + .boundingBoxLimit; const validResults = isDefined(locationSearchBoundingBox) ? search.results.filter(function(r: any) { return ( diff --git a/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts b/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts new file mode 100644 index 00000000000..781a510e90f --- /dev/null +++ b/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts @@ -0,0 +1,11 @@ +import Terria from "../../../lib/Models/Terria"; + +describe("LocationSearchProviderTraits", function() { + let terria: Terria; + beforeEach(async function() { + terria = new Terria({ + baseUrl: "./" + }); + //geoJsonCatalogItem = new GeoJsonCatalogItem("test", terria); + }); +}); From 5a580a3e539da3054de8ce5b98843a827c6fceba Mon Sep 17 00:00:00 2001 From: zoran995 Date: Wed, 17 Feb 2021 23:09:56 +0100 Subject: [PATCH 014/129] correct name of translation string --- lib/Language/en/translation.json | 4 ++-- lib/ModelMixins/SearchProvider/SearchProviderMixin.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Language/en/translation.json b/lib/Language/en/translation.json index ddd59794964..c289fc50858 100644 --- a/lib/Language/en/translation.json +++ b/lib/Language/en/translation.json @@ -607,8 +607,8 @@ "viewModels": { "searchNoLocations": "Sorry, no locations match your search query.", "searchErrorOccurred": "An error occurred while searching. Please check your internet connection or try again later.", - "seachMinCharacters": "You need to enter minimum {{count}} character", - "seachMinCharacters_plural": "You need to enter minimum {{count}} characters", + "searchMinCharacters": "You need to enter minimum {{count}} character", + "searchMinCharacters_plural": "You need to enter minimum {{count}} characters", "searchAddresses": "Addresses", "searchPlaceNames": "Official Place Names", "searchNoPlaceNames": "Sorry, no official place names match your search query.", diff --git a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts index 606fc07735b..349b806a5fb 100644 --- a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts @@ -19,7 +19,7 @@ function SearchProviderMixin>( const result = new SearchProviderResults(this); if (!this.shouldRunSearch(searchText)) { result.resultsCompletePromise = fromPromise(Promise.resolve()); - result.message = i18next.t("viewModels.seachMinCharacters", { + result.message = i18next.t("viewModels.searchMinCharacters", { count: this.minCharacters }); return result; From 894237bf3869e4231fe6edf2eaf30b717f07ae35 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 18 Feb 2021 00:44:17 +0100 Subject: [PATCH 015/129] copy deprecationWarning from cesium, move default search providers array to config.json --- lib/Core/deprecationWarning.ts | 94 +++++++++++++++++++ lib/Language/languageHelpers.ts | 11 ++- lib/Models/Terria.ts | 44 ++++----- .../Search/LocationSearchResults.tsx | 4 +- 4 files changed, 119 insertions(+), 34 deletions(-) create mode 100644 lib/Core/deprecationWarning.ts diff --git a/lib/Core/deprecationWarning.ts b/lib/Core/deprecationWarning.ts new file mode 100644 index 00000000000..9b57e432663 --- /dev/null +++ b/lib/Core/deprecationWarning.ts @@ -0,0 +1,94 @@ +import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; +import defined from "terriajs-cesium/Source/Core/defined"; +import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; + +/** + * Port of Cesium's functions `deprecationWarning` and `oneTimeWarning`. + */ +const warnings: { [key: string]: boolean } = {}; + +/** + * Logs a one time message to the console. Use this function instead of + * console.log directly since this does not log duplicate messages + * unless it is called from multiple workers. + * + * @function oneTimeWarning + * + * @param {String} identifier The unique identifier for this warning. + * @param {String} [message=identifier] The message to log to the console. + * + * @example + * for(var i=0;i>includeStart('debug', pragmas.debug); + if (!defined(identifier)) { + throw new DeveloperError("identifier is required."); + } + //>>includeEnd('debug'); + + if (!defined(warnings[identifier])) { + warnings[identifier] = true; + console.warn(defaultValue(message, identifier)); + } +} + +/** + * Logs a deprecation message to the console. Use this function instead of + * console.log directly since this does not log duplicate messages + * unless it is called from multiple workers. + * + * @function deprecationWarning + * + * @param {String} identifier The unique identifier for this deprecated API. + * @param {String} message The message to log to the console. + * + * @example + * // Deprecated function or class + * function Foo() { + * deprecationWarning('Foo', 'Foo was deprecated in Cesium 1.01. It will be removed in 1.03. Use newFoo instead.'); + * // ... + * } + * + * // Deprecated function + * Bar.prototype.func = function() { + * deprecationWarning('Bar.func', 'Bar.func() was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newFunc() instead.'); + * // ... + * }; + * + * // Deprecated property + * Object.defineProperties(Bar.prototype, { + * prop : { + * get : function() { + * deprecationWarning('Bar.prop', 'Bar.prop was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newProp instead.'); + * // ... + * }, + * set : function(value) { + * deprecationWarning('Bar.prop', 'Bar.prop was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newProp instead.'); + * // ... + * } + * } + * }); + * + * @private + */ +function deprecationWarning(identifier: string, message: string) { + //>>includeStart('debug', pragmas.debug); + if (!defined(identifier) || !defined(message)) { + throw new DeveloperError("identifier and message are required."); + } + //>>includeEnd('debug'); + + oneTimeWarning(identifier, message); +} + +export default deprecationWarning; diff --git a/lib/Language/languageHelpers.ts b/lib/Language/languageHelpers.ts index 01a7a30c3ca..c8f32ea0c9d 100644 --- a/lib/Language/languageHelpers.ts +++ b/lib/Language/languageHelpers.ts @@ -1,5 +1,5 @@ import i18next from "i18next"; - +import deprecationWarning from "../Core/deprecationWarning"; /** * Takes a given string and translates it if it exists, otherwise return */ @@ -9,12 +9,15 @@ export function useTranslationIfExists(keyOrString: string) { return i18next.exists(translationKey) ? i18next.t(translationKey) : translationKey; - } else { - console.warn( - "using translation key within config won't work in future unless you prefix it with `translate#`" + } else if (keyOrString) { + deprecationWarning( + "useTranslationIfExists", + "Using translation key inside config without `translate#` prefix is deprecated" ); // after the depreaction // return keyOrString; return i18next.exists(keyOrString) ? i18next.t(keyOrString) : keyOrString; + } else { + return keyOrString; } } diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 6b96e90f3bd..71cae12da5f 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -392,30 +392,7 @@ export default class Terria { recommendedListLength: 5, flightDurationSeconds: 1.5, minCharacters: 3, - searchProviders: [ - { - id: "search-provider/bing-maps", - type: "bing-maps-search-provider", - name: "translate#viewModels.searchLocations", - url: "https://dev.virtualearth.net/", - flightDurationSeconds: 1.5, - minCharacters: 5, - isOpen: true - }, - { - id: "search-provider/australian-gazetteer", - type: "australian-gazetteer-search-provider", - name: "translate#viewModels.searchPlaceNames", - url: - "http://services.ga.gov.au/gis/services/Australian_Gazetteer/MapServer/WFSServer", - searchPropertyName: "Australian_Gazetteer:NameU", - searchPropertyTypeName: "Australian_Gazetteer:Gazetteer_of_Australia", - flightDurationSeconds: 1.5, - minCharacters: 3, - recommendedListLength: 3, - isOpen: false - } - ] + searchProviders: [] } }; @@ -576,8 +553,10 @@ export default class Terria { } if (this.locationSearchProviders.has(model.uniqueId)) { - throw new RuntimeError( - "A SearchProvider with the specified ID already exists." + console.log( + new DeveloperError( + "A SearchProvider with the specified ID already exists." + ) ); } @@ -730,7 +709,7 @@ export default class Terria { } }) .then(() => { - let searchProviders = this.configParameters.searchBar!.searchProviders; + let searchProviders = this.configParameters.searchBar?.searchProviders; if (!isObservableArray(searchProviders)) throw new TerriaError({ sender: SearchProviderFactory, @@ -843,7 +822,16 @@ export default class Terria { updateParameters(parameters: ConfigParameters): void { Object.keys(parameters).forEach((key: string) => { if (this.configParameters.hasOwnProperty(key)) { - this.configParameters[key] = parameters[key]; + if (key === "searchBar") { + // merge default and new + //@ts-ignore + this.configParameters[key] = { + ...this.configParameters[key], + ...parameters[key] + }; + } else { + this.configParameters[key] = parameters[key]; + } } }); diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 7b8e5bc2abd..d9927e669e6 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -62,8 +62,8 @@ class LocationSearchResults extends React.Component { @computed get validResults() { const { search, terria } = this.props; - const locationSearchBoundingBox = terria.configParameters.searchBar! - .boundingBoxLimit; + const locationSearchBoundingBox = + terria.configParameters.searchBar?.boundingBoxLimit; const validResults = isDefined(locationSearchBoundingBox) ? search.results.filter(function(r: any) { return ( From 4fba7f8e5ae0cb37cde091c73a641addf5a434fa Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 18 Feb 2021 00:45:08 +0100 Subject: [PATCH 016/129] add tests --- .../AustralianGazetteerSearchProviderSpec.ts | 19 +++++-- .../BingMapsSearchProviderSpec.ts | 55 +++++++++++++++++++ .../LocationSearchProviderTraitsSpec.ts | 23 +++++++- 3 files changed, 90 insertions(+), 7 deletions(-) rename test/Models/{ => SearchProvider}/AustralianGazetteerSearchProviderSpec.ts (55%) create mode 100644 test/Models/SearchProvider/BingMapsSearchProviderSpec.ts diff --git a/test/Models/AustralianGazetteerSearchProviderSpec.ts b/test/Models/SearchProvider/AustralianGazetteerSearchProviderSpec.ts similarity index 55% rename from test/Models/AustralianGazetteerSearchProviderSpec.ts rename to test/Models/SearchProvider/AustralianGazetteerSearchProviderSpec.ts index 33a4a48047c..9ffe6205216 100644 --- a/test/Models/AustralianGazetteerSearchProviderSpec.ts +++ b/test/Models/SearchProvider/AustralianGazetteerSearchProviderSpec.ts @@ -1,9 +1,8 @@ import { configure } from "mobx"; -import createAustralianGazetteerSearchProvider from "../../lib/Models/SearchProvider/AustralianGazetteerSearchProvider"; -import Terria from "../../lib/Models/Terria"; -import WebFeatureServiceSearchProvider from "../../lib/Models/SearchProvider/WebFeatureServiceSearchProvider"; +import Terria from "../../../lib/Models/Terria"; +import AustralianGazetteerSearchProvider from "../../../lib/Models/SearchProvider/AustralianGazetteerSearchProvider"; -const wfsResponseXml = require("raw-loader!../../wwwroot/test/WFS/getWithFilter.xml"); +const wfsResponseXml = require("raw-loader!../../../wwwroot/test/WFS/getWithFilter.xml"); configure({ enforceActions: "observed", @@ -11,10 +10,18 @@ configure({ }); describe("GazetteerSearchProvider", function() { - let searchProvider: WebFeatureServiceSearchProvider; + let searchProvider: AustralianGazetteerSearchProvider; beforeEach(function() { - searchProvider = createAustralianGazetteerSearchProvider(new Terria()); + searchProvider = new AustralianGazetteerSearchProvider( + "test", + new Terria() + ); + }); + + it(" type", function() { + expect(searchProvider.type).toEqual(AustralianGazetteerSearchProvider.type); }); + it("queries the web feature service and returns search results", async function() { spyOn(searchProvider, "getXml").and.returnValue( Promise.resolve(wfsResponseXml) diff --git a/test/Models/SearchProvider/BingMapsSearchProviderSpec.ts b/test/Models/SearchProvider/BingMapsSearchProviderSpec.ts new file mode 100644 index 00000000000..bfa3673ec23 --- /dev/null +++ b/test/Models/SearchProvider/BingMapsSearchProviderSpec.ts @@ -0,0 +1,55 @@ +import BingMapsSearchProvider from "../../../lib/Models/SearchProvider/BingMapsSearchProvider"; +import Terria from "../../../lib/Models/Terria"; + +describe("BingMapsSearchProvider", function() { + let searchProvider: BingMapsSearchProvider; + beforeEach(function() { + searchProvider = new BingMapsSearchProvider("test", new Terria()); + jasmine.Ajax.install(); + jasmine.Ajax.stubRequest(/https:\/\/dev.virtualearth.net/i).andReturn({ + responseText: JSON.stringify({ + resourceSets: [ + { + estimatedTotal: 1, + resources: [ + { + address: { + addressLine: "Borgo Garibaldi", + adminDistrict: "Veneto", + adminDistrict2: "Belluno", + countryRegion: "Italy", + formattedAddress: "Borgo Garibaldi, 32026 Borgo Valbelluna", + locality: "Borgo Valbelluna", + postalCode: "32026" + }, + bbox: [46.06294, 12.08387, 46.06359, 12.08573], + confidence: "Medium", + entityType: "RoadBlock", + name: "Borgo Garibaldi, 32026 Borgo Valbelluna", + point: { type: "Point", coordinates: [46.06323, 12.0848] } + } + ] + } + ] + }) + }); + }); + + afterEach(function() { + jasmine.Ajax.uninstall(); + }); + + it(" type", function() { + expect(searchProvider.type).toEqual(BingMapsSearchProvider.type); + }); + + it("find location", function(done) { + const results = searchProvider.search("melb"); + expect(results).toBeDefined(); + expect(results.results.length).toEqual(1); + expect(results.results[0].name).toEqual( + "Borgo Garibaldi, 32026 Borgo Valbelluna" + ); + done(); + }); +}); diff --git a/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts b/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts index 781a510e90f..e07d7033acf 100644 --- a/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts +++ b/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts @@ -1,11 +1,32 @@ import Terria from "../../../lib/Models/Terria"; +import BingMapsSearchProvider from "../../../lib/Models/SearchProvider/BingMapsSearchProvider"; +import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProvider/LocationSearchProviderMixin"; describe("LocationSearchProviderTraits", function() { let terria: Terria; + let bingMapsSearchProvider: BingMapsSearchProvider; beforeEach(async function() { terria = new Terria({ baseUrl: "./" }); - //geoJsonCatalogItem = new GeoJsonCatalogItem("test", terria); + bingMapsSearchProvider = new BingMapsSearchProvider("test", terria); + }); + + it(" - properly mixed", function() { + expect( + LocationSearchProviderMixin.isMixedInto(bingMapsSearchProvider) + ).toBeTruthy(); + }); + + it(" - propperly defines default recommendedListLength", function() { + expect(bingMapsSearchProvider.recommendedListLength).toEqual(5); + }); + + it(" - propperly defines default flightDurationSeconds", function() { + expect(bingMapsSearchProvider.flightDurationSeconds).toEqual(1.5); + }); + + it(" - propperly defines default isOpen", function() { + expect(bingMapsSearchProvider.isOpen).toEqual(true); }); }); From e104c34f439a05adbc932e54fc58a2fe8393732f Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 18 Feb 2021 00:49:54 +0100 Subject: [PATCH 017/129] use propper translation strings --- lib/Language/en/translation.json | 8 ++++++++ lib/Models/SearchProvider/upsertSearchProviderFromJson.ts | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Language/en/translation.json b/lib/Language/en/translation.json index c289fc50858..92de53a1d3b 100644 --- a/lib/Language/en/translation.json +++ b/lib/Language/en/translation.json @@ -1437,6 +1437,14 @@ "datasetScaleErrorMessage": "The {{name}} dataset will not be shown when zoomed in this close to the map because the data custodian has indicated that the data is not intended or suitable for display at this scale. Click the dataset's Info button on the Now Viewing tab for more information about the dataset and the data custodian." } }, + "searchProvider": { + "models": { + "unsupportedTypeTitle": "Unknown type", + "unsupportedTypeMessage": "Could not create unknown model type {{type}}.", + "idForMatchingErrorTitle": "Missing property", + "idForMatchingErrorMessage": "Model objects must have an `id`, `localId`, or `name` property." + } + }, "deltaTool": { "titlePrefix": "Change Detection", "description": "This tool visualizes the difference between imagery captured at two discrete points in time.", diff --git a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts index 4921a9c561d..c9c7427e756 100644 --- a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts @@ -18,8 +18,8 @@ export default function upsertSearchProviderFromJson( const id = json.localId || json.name; if (id === undefined) { throw new TerriaError({ - title: i18next.t("models.catalog.idForMatchingErrorTitle"), - message: i18next.t("models.catalog.idForMatchingErrorMessage") + title: i18next.t("searchProvider.models.idForMatchingErrorTitle"), + message: i18next.t("searchProvider.models.idForMatchingErrorMessage") }); } uniqueId = id; @@ -32,8 +32,8 @@ export default function upsertSearchProviderFromJson( if (model === undefined) { console.log( new TerriaError({ - title: i18next.t("models.catalog.unsupportedTypeTitle"), - message: i18next.t("models.catalog.unsupportedTypeMessage", { + title: i18next.t("searchProvider.models.unsupportedTypeTitle"), + message: i18next.t("searchProvider.models.unsupportedTypeMessage", { type: json.type }) }) From 91630c46e9f4e85bcd74d06c7c54bed494c2c004 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 18 Feb 2021 01:11:09 +0100 Subject: [PATCH 018/129] tests --- .../BingMapsSearchProviderSpec.ts | 55 ------------------- .../BingMapsSearchProviderTraitsSpec.ts | 28 ++++++++++ 2 files changed, 28 insertions(+), 55 deletions(-) delete mode 100644 test/Models/SearchProvider/BingMapsSearchProviderSpec.ts create mode 100644 test/Traits/SearchProvider/BingMapsSearchProviderTraitsSpec.ts diff --git a/test/Models/SearchProvider/BingMapsSearchProviderSpec.ts b/test/Models/SearchProvider/BingMapsSearchProviderSpec.ts deleted file mode 100644 index bfa3673ec23..00000000000 --- a/test/Models/SearchProvider/BingMapsSearchProviderSpec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import BingMapsSearchProvider from "../../../lib/Models/SearchProvider/BingMapsSearchProvider"; -import Terria from "../../../lib/Models/Terria"; - -describe("BingMapsSearchProvider", function() { - let searchProvider: BingMapsSearchProvider; - beforeEach(function() { - searchProvider = new BingMapsSearchProvider("test", new Terria()); - jasmine.Ajax.install(); - jasmine.Ajax.stubRequest(/https:\/\/dev.virtualearth.net/i).andReturn({ - responseText: JSON.stringify({ - resourceSets: [ - { - estimatedTotal: 1, - resources: [ - { - address: { - addressLine: "Borgo Garibaldi", - adminDistrict: "Veneto", - adminDistrict2: "Belluno", - countryRegion: "Italy", - formattedAddress: "Borgo Garibaldi, 32026 Borgo Valbelluna", - locality: "Borgo Valbelluna", - postalCode: "32026" - }, - bbox: [46.06294, 12.08387, 46.06359, 12.08573], - confidence: "Medium", - entityType: "RoadBlock", - name: "Borgo Garibaldi, 32026 Borgo Valbelluna", - point: { type: "Point", coordinates: [46.06323, 12.0848] } - } - ] - } - ] - }) - }); - }); - - afterEach(function() { - jasmine.Ajax.uninstall(); - }); - - it(" type", function() { - expect(searchProvider.type).toEqual(BingMapsSearchProvider.type); - }); - - it("find location", function(done) { - const results = searchProvider.search("melb"); - expect(results).toBeDefined(); - expect(results.results.length).toEqual(1); - expect(results.results[0].name).toEqual( - "Borgo Garibaldi, 32026 Borgo Valbelluna" - ); - done(); - }); -}); diff --git a/test/Traits/SearchProvider/BingMapsSearchProviderTraitsSpec.ts b/test/Traits/SearchProvider/BingMapsSearchProviderTraitsSpec.ts new file mode 100644 index 00000000000..1d4ca1f6ad7 --- /dev/null +++ b/test/Traits/SearchProvider/BingMapsSearchProviderTraitsSpec.ts @@ -0,0 +1,28 @@ +import Terria from "../../../lib/Models/Terria"; +import BingMapsSearchProvider from "../../../lib/Models/SearchProvider/BingMapsSearchProvider"; +import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProvider/LocationSearchProviderMixin"; + +describe("BingMapsSearchProviderTraits", function() { + let terria: Terria; + let bingMapsSearchProvider: BingMapsSearchProvider; + beforeEach(async function() { + terria = new Terria({ + baseUrl: "./" + }); + bingMapsSearchProvider = new BingMapsSearchProvider("test", terria); + }); + + it(" - properly mixed", function() { + expect( + LocationSearchProviderMixin.isMixedInto(bingMapsSearchProvider) + ).toBeTruthy(); + }); + + describe(" - default values", function() { + it(" - propperly defines default url", function() { + expect(bingMapsSearchProvider.url).toEqual( + "https://dev.virtualearth.net/" + ); + }); + }); +}); From 907b4cc41ed99ea9693e3c91ba552bd2cb5f22f4 Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 8 Jul 2021 08:30:15 +0200 Subject: [PATCH 019/129] fix bad merge --- lib/Styled/List.tsx | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/Styled/List.tsx b/lib/Styled/List.tsx index 8df57000c29..94a61b3024b 100644 --- a/lib/Styled/List.tsx +++ b/lib/Styled/List.tsx @@ -1,13 +1,37 @@ -import styled from "styled-components"; +import styled, { css } from "styled-components"; -export const Ul = styled.ul` +export const Li = styled.li``; + +type ListProps = Partial<{ + spaced: boolean; + lined: boolean; + fullWidth: boolean; +}>; + +export const Ul = styled.ul` list-style: none; padding: 0; margin: 0; + ${props => props.fullWidth && "width: 100%;"} + ${props => + props.spaced && + css` + ${Li}:not(:first-child) { + padding-top: 5px; + } + `} + ${props => + props.lined && + css` + ${Li}:first-child { + padding-bottom: 5px; + } + ${Li}:not(:first-child) { + border-top: 1px solid grey; + } + `} `; -export const Li = styled.li``; - export const Ol = styled(Ul).attrs({ as: "ol" })``; From 7ec3b1f113460127efeabe1d5da086d24e169dcc Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 8 Jul 2021 09:16:45 +0200 Subject: [PATCH 020/129] connect search providers with analytics and resolve merge conflicts --- lib/Core/AnalyticEvents/analyticEvents.ts | 4 +--- .../LocationSearchProviderMixin.ts | 19 +++++++-------- .../SearchProvider/SearchProviderMixin.ts | 15 ++++++++---- .../WebFeatureServiceSearchProviderMixin.ts | 2 ++ .../AustralianGazetteerSearchProvider.ts | 12 ++++++++++ .../SearchProvider/BingMapsSearchProvider.ts | 14 +++++++++-- .../SearchProvider/CatalogSearchProvider.ts | 21 ++++++++--------- .../SearchProvider/SearchProviderResults.ts | 2 +- .../SearchProvider/StubSearchProvider.ts | 4 ++++ lib/ReactViewModels/SearchState.ts | 16 +++++++------ lib/ReactViews/Mobile/MobileHeader.jsx | 10 +------- .../Search/LocationSearchResults.tsx | 18 +++++++-------- lib/ReactViews/Search/SearchBoxAndResults.jsx | 17 +++++++------- lib/ReactViews/Search/SearchHeader.tsx | 6 ++--- lib/ReactViews/Search/SearchResult.tsx | 23 ++++++------------- lib/Styled/Icon.tsx | 2 +- lib/Styled/Input.tsx | 2 +- lib/Styled/Select.tsx | 2 +- 18 files changed, 99 insertions(+), 90 deletions(-) diff --git a/lib/Core/AnalyticEvents/analyticEvents.ts b/lib/Core/AnalyticEvents/analyticEvents.ts index 51154c1218c..39f9f443cf3 100644 --- a/lib/Core/AnalyticEvents/analyticEvents.ts +++ b/lib/Core/AnalyticEvents/analyticEvents.ts @@ -15,9 +15,7 @@ export enum Category { export enum SearchAction { bing = "Bing", catalog = "Catalog", - gazetteer = "Gazetteer", - gnaf = "gnaf", - nominatim = "nominatim" + gazetteer = "Gazetteer" } export enum LaunchAction { diff --git a/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts index faa54b3ad92..15e40342c25 100644 --- a/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts @@ -1,15 +1,10 @@ -import { action, observable } from "mobx"; -import { fromPromise } from "mobx-utils"; +import { action } from "mobx"; import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import Constructor from "../../Core/Constructor"; -import Model, { BaseModel } from "../../Models/Model"; -import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; -import StratumFromTraits from "../../Models/StratumFromTraits"; +import Model from "../../Models/Model"; import Terria from "../../Models/Terria"; -import ModelTraits from "../../Traits/ModelTraits"; -import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; import CommonStrata from "../../Models/CommonStrata"; import LocationSearchProviderTraits from "../../Traits/SearchProvider/LocationSearchProviderTraits"; import SearchProviderMixin from "./SearchProviderMixin"; @@ -20,15 +15,16 @@ function LocationSearchProviderMixin< T extends Constructor >(Base: T) { abstract class LocationSearchProviderMixin extends SearchProviderMixin(Base) { + get hasLocationSearchProviderMixin() { + return true; + } + @action toggleOpen(stratumId: CommonStrata = CommonStrata.user) { this.setTrait(stratumId, "isOpen", !this.isOpen); } - - get hasLocationSearchProviderMixin() { - return true; - } } + return LocationSearchProviderMixin; } @@ -59,6 +55,7 @@ export function getMapCenter(terria: Terria): MapCenter { namespace LocationSearchProviderMixin { export interface LocationSearchProviderMixin extends InstanceType> {} + export function isMixedInto( model: any ): model is LocationSearchProviderMixin { diff --git a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts index 349b806a5fb..e62d16c54fa 100644 --- a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts @@ -14,6 +14,13 @@ function SearchProviderMixin>( abstract class SearchProviderMixin extends Base { abstract get type(): string; + protected abstract logEvent(searchText: string): void; + + protected abstract doSearch( + searchText: string, + results: SearchProviderResults + ): Promise; + @action search(searchText: string): SearchProviderResults { const result = new SearchProviderResults(this); @@ -24,17 +31,13 @@ function SearchProviderMixin>( }); return result; } + this.logEvent(searchText); result.resultsCompletePromise = fromPromise( this.doSearch(searchText, result) ); return result; } - protected abstract doSearch( - searchText: string, - results: SearchProviderResults - ): Promise; - private shouldRunSearch(searchText: string) { if ( searchText === undefined || @@ -53,12 +56,14 @@ function SearchProviderMixin>( return true; } } + return SearchProviderMixin; } namespace SearchProviderMixin { export interface SearchProviderMixin extends InstanceType> {} + export function isMixedInto(model: any): model is SearchProviderMixin { return model && model.hasSearchProviderMixin; } diff --git a/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts index dad3a5d4a16..80d5e68e508 100644 --- a/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts @@ -177,6 +177,7 @@ function WebFeatureServiceSearchProviderMixin< results.message = i18next.t("viewModels.searchErrorOccurred"); }); } + get isWebFeatureServiceSearchProviderMixin() { return true; } @@ -190,6 +191,7 @@ namespace WebFeatureServiceSearchProviderMixin { extends InstanceType< ReturnType > {} + export function isMixedInto( model: any ): model is WebFeatureServiceSearchProviderMixin { diff --git a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts index 986f781134d..e9725ca0cfb 100644 --- a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts +++ b/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts @@ -2,6 +2,10 @@ import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/W import CreateModel from "../CreateModel"; import WebFeatureServiceSearchProviderMixin from "../../ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin"; import SearchResult from "./SearchResult"; +import { + Category, + SearchAction +} from "../../Core/AnalyticEvents/analyticEvents"; const featureCodesToNamesMap = new Map([ ["AF", "Aviation"], @@ -229,6 +233,14 @@ export default class AustralianGazetteerSearchProvider extends WebFeatureService return AustralianGazetteerSearchProvider.type; } + protected logEvent(searchText: string) { + this.terria.analytics?.logEvent( + Category.search, + SearchAction.gazetteer, + searchText + ); + } + featureToSearchResultFunction: ( feature: any ) => SearchResult = featureToSearchResultFunction; diff --git a/lib/Models/SearchProvider/BingMapsSearchProvider.ts b/lib/Models/SearchProvider/BingMapsSearchProvider.ts index 4bbd5d2035d..6a075946c65 100644 --- a/lib/Models/SearchProvider/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProvider/BingMapsSearchProvider.ts @@ -13,6 +13,10 @@ import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; import CommonStrata from "./../CommonStrata"; import Terria from "../Terria"; +import { + Category, + SearchAction +} from "../../Core/AnalyticEvents/analyticEvents"; export default class BingMapsSearchProvider extends LocationSearchProviderMixin( CreateModel(BingMapsSearchProviderTraits) @@ -45,6 +49,14 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( } } + protected logEvent(searchText: string) { + this.terria.analytics?.logEvent( + Category.search, + SearchAction.gazetteer, + searchText + ); + } + protected doSearch( searchText: string, searchResults: SearchProviderResults @@ -52,8 +64,6 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( searchResults.results.length = 0; searchResults.message = undefined; - this.terria.analytics.logEvent("search", "bing", searchText); - const searchQuery = new Resource({ url: this.url + "REST/v1/Locations", queryParameters: { diff --git a/lib/Models/SearchProvider/CatalogSearchProvider.ts b/lib/Models/SearchProvider/CatalogSearchProvider.ts index 810902cfb6e..a5fe35e79a7 100644 --- a/lib/Models/SearchProvider/CatalogSearchProvider.ts +++ b/lib/Models/SearchProvider/CatalogSearchProvider.ts @@ -12,12 +12,9 @@ import Terria from "../Terria"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; -interface CatalogSearchProviderOptions { - terria: Terria; -} - type UniqueIdString = string; type ResultMap = Map; + export function loadAndSearchCatalogRecursively( terria: Terria, searchTextLowercase: string, @@ -105,13 +102,20 @@ export default class CatalogSearchProvider extends SearchProviderMixin( CreateModel(CatalogSearchProviderTraits) ) { static readonly type = "catalog-search-provider"; + @observable isSearching: boolean = false; + @observable debounceDurationOnceLoaded: number = 300; get type() { return CatalogSearchProvider.type; } - @observable isSearching: boolean = false; - @observable debounceDurationOnceLoaded: number = 300; + protected logEvent(searchText: string) { + this.terria.analytics?.logEvent( + Category.search, + SearchAction.catalog, + searchText + ); + } protected doSearch( searchText: string, @@ -126,11 +130,6 @@ export default class CatalogSearchProvider extends SearchProviderMixin( return Promise.resolve(); } - this.terria.analytics?.logEvent( - Category.search, - SearchAction.catalog, - searchText - ); const resultMap: ResultMap = new Map(); const promise: Promise = loadAndSearchCatalogRecursively( diff --git a/lib/Models/SearchProvider/SearchProviderResults.ts b/lib/Models/SearchProvider/SearchProviderResults.ts index 09fcc1aafe5..8f2c887bd31 100644 --- a/lib/Models/SearchProvider/SearchProviderResults.ts +++ b/lib/Models/SearchProvider/SearchProviderResults.ts @@ -1,6 +1,6 @@ import { observable } from "mobx"; import SearchResult from "./SearchResult"; -import { IPromiseBasedObservable, fromPromise } from "mobx-utils"; +import { fromPromise, IPromiseBasedObservable } from "mobx-utils"; import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; export default class SearchProviderResults { diff --git a/lib/Models/SearchProvider/StubSearchProvider.ts b/lib/Models/SearchProvider/StubSearchProvider.ts index 28745157278..4fec65f6175 100644 --- a/lib/Models/SearchProvider/StubSearchProvider.ts +++ b/lib/Models/SearchProvider/StubSearchProvider.ts @@ -22,6 +22,10 @@ export default class StubSearchProvider extends SearchProviderMixin( return StubSearchProvider.type; } + protected logEvent(searchText: string) { + return; + } + protected doSearch( searchText: string, results: SearchProviderResults diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index fc7373547e0..dfe12ad9bea 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -52,21 +52,23 @@ export default class SearchState { new CatalogSearchProvider("catalog-search-provider", options.terria); this.locationSearchProviders = options.locationSearchProviders || []; + const self = this; + this._catalogSearchDisposer = reaction( - () => this.catalogSearchText, + () => self.catalogSearchText, () => { - this.isWaitingToStartCatalogSearch = true; - if (this.catalogSearchProvider) { - this.catalogSearchResults = this.catalogSearchProvider.search(""); + self.isWaitingToStartCatalogSearch = true; + if (self.catalogSearchProvider) { + self.catalogSearchResults = self.catalogSearchProvider.search(""); } } ); this._locationSearchDisposer = reaction( - () => this.locationSearchText, + () => self.locationSearchText, () => { - this.isWaitingToStartLocationSearch = true; - this.locationSearchResults = this.locationSearchProviders.map( + self.isWaitingToStartLocationSearch = true; + self.locationSearchResults = self.locationSearchProviders.map( provider => { return provider.search(""); } diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index 6ed7bc0eb25..8a4599b16a2 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -4,21 +4,13 @@ import { runInAction } from "mobx"; import { observer } from "mobx-react"; import PropTypes from "prop-types"; import React from "react"; -import SearchBox from "../Search/SearchBox"; -import MobileModalWindow from "./MobileModalWindow"; -import Branding from "../SidePanel/Branding"; -import Styles from "./mobile-header.scss"; -import Icon, { StyledIcon } from "../../Styled/Icon"; -import MobileMenu from "./MobileMenu"; -import classNames from "classnames"; -import { removeMarker } from "../../Models/LocationMarkerUtils"; import { withTranslation } from "react-i18next"; import { withTheme } from "styled-components"; import { useTranslationIfExists } from "../../Language/languageHelpers"; import { removeMarker } from "../../Models/LocationMarkerUtils"; import Box from "../../Styled/Box"; import { RawButton } from "../../Styled/Button"; -import Icon, { StyledIcon } from "../Icon"; +import Icon, { StyledIcon } from "../../Styled/Icon"; import SearchBox from "../Search/SearchBox"; import Branding from "../SidePanel/Branding"; import Styles from "./mobile-header.scss"; diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index d9927e669e6..8189ab45c71 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -5,7 +5,7 @@ https://github.com/TerriaJS/nsw-digital-twin/issues/248#issuecomment-599919318 */ -import { observable, computed, action } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import React from "react"; import { @@ -15,20 +15,18 @@ import { } from "react-i18next"; import styled, { DefaultTheme } from "styled-components"; import isDefined from "../../Core/isDefined"; +import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; import Terria from "../../Models/Terria"; import ViewState from "../../ReactViewModels/ViewState"; +import Box, { BoxSpan } from "../../Styled/Box"; +import { RawButton } from "../../Styled/Button"; +import Icon, { StyledIcon } from "../../Styled/Icon"; import Ul from "../../Styled/List"; -import Icon, { StyledIcon } from "../Icon"; +import Text, { TextSpan } from "../../Styled/Text"; +import Loader from "../Loader"; import LocationSearchProviderMixin from "./../../ModelMixins/SearchProvider/LocationSearchProviderMixin"; -import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; import SearchHeader from "./SearchHeader"; import SearchResult from "./SearchResult"; -import Loader from "../Loader"; -const BoxSpan: any = require("../../Styled/Box").BoxSpan; -const Box: any = require("../../Styled/Box").default; -const Text: any = require("../../Styled/Text").default; -const TextSpan: any = require("../../Styled/Text").TextSpan; -const RawButton: any = require("../../Styled/Button").RawButton; const RawButtonAndHighlight = styled(RawButton)` ${p => ` @@ -109,7 +107,7 @@ class LocationSearchResults extends React.Component { isOpen={isOpen} search={search} isWaitingForSearchToStart={this.props.isWaitingForSearchToStart} - > + /> ); } + SearchInDataCatalog.propTypes = { handleClick: PropTypes.func.isRequired, viewState: PropTypes.object.isRequired @@ -74,11 +66,13 @@ const PresentationBox = styled(Box).attrs({ `; export const LOCATION_SEARCH_INPUT_NAME = "LocationSearchInput"; + export class SearchBoxAndResultsRaw extends React.Component { constructor(props) { super(props); this.locationSearchRef = React.createRef(); } + componentDidMount() { this.props.viewState.updateAppRef( LOCATION_SEARCH_INPUT_NAME, @@ -114,6 +108,7 @@ export class SearchBoxAndResultsRaw extends React.Component { this._nowViewingChangeSubscription = undefined; } } + changeSearchText(newText) { runInAction(() => { this.props.viewState.searchState.locationSearchText = newText; @@ -134,17 +129,21 @@ export class SearchBoxAndResultsRaw extends React.Component { }); } } + search() { this.props.viewState.searchState.searchLocations(); } + toggleShowLocationSearchResults(bool) { runInAction(() => { this.props.viewState.searchState.showLocationSearchResults = bool; }); } + startLocationSearch() { this.toggleShowLocationSearchResults(true); } + render() { const { viewState, placeholder } = this.props; const searchState = viewState.searchState; diff --git a/lib/ReactViews/Search/SearchHeader.tsx b/lib/ReactViews/Search/SearchHeader.tsx index 5ead1157445..89a4202d251 100644 --- a/lib/ReactViews/Search/SearchHeader.tsx +++ b/lib/ReactViews/Search/SearchHeader.tsx @@ -1,8 +1,8 @@ +import { observer } from "mobx-react"; import React from "react"; +import { BoxSpan } from "../../Styled/Box"; +import Text from "../../Styled/Text"; import Loader from "../Loader"; -import { observer } from "mobx-react"; -const Text = require("../../Styled/Text").default; -const BoxSpan = require("../../Styled/Box").BoxSpan; interface SearchHeaderProps { searchResults: { [key: string]: any }; diff --git a/lib/ReactViews/Search/SearchResult.tsx b/lib/ReactViews/Search/SearchResult.tsx index a431a4278cc..681c51acca9 100644 --- a/lib/ReactViews/Search/SearchResult.tsx +++ b/lib/ReactViews/Search/SearchResult.tsx @@ -1,16 +1,13 @@ import React from "react"; import styled, { useTheme } from "styled-components"; import { Li } from "../../Styled/List"; -import Icon, { StyledIcon } from "../Icon"; +import Icon, { StyledIcon } from "../../Styled/Icon"; import highlightKeyword from "../ReactViewHelpers/highlightKeyword"; - -const Box = require("../../Styled/Box").default; -const BoxSpan = require("../../Styled/Box").BoxSpan; -const TextSpan = require("../../Styled/Text").TextSpan; -const RawButton = require("../../Styled/Button").RawButton; -const Spacing = require("../../Styled/Spacing").default; -const SpacingSpan = require("../../Styled/Spacing").SpacingSpan; -const Hr = require("../../Styled/Hr").default; +import Box, { BoxSpan } from "../../Styled/Box"; +import { TextSpan } from "../../Styled/Text"; +import Spacing, { SpacingSpan } from "../../Styled/Spacing"; +import { RawButton } from "../../Styled/Button"; +import Hr from "../../Styled/Hr"; // Not sure how to generalise this or if it should be kept in stlyed/Button.jsx @@ -59,13 +56,7 @@ const SearchResult: React.FC = ( {/* )} */} - + ` -moz-appearance: none; diff --git a/lib/Styled/Select.tsx b/lib/Styled/Select.tsx index 9589283aa80..a7862c88785 100644 --- a/lib/Styled/Select.tsx +++ b/lib/Styled/Select.tsx @@ -27,7 +27,7 @@ or with overrides on icon import React from "react"; import styled, { useTheme } from "styled-components"; -const Box: any = require("./Box").default; +import Box from "./Box"; import { GLYPHS, StyledIcon } from "./Icon"; const StyledSelect = styled.select` From 7bef63def684eab07785b31c11fa8c28b6a9375c Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 8 Jul 2021 12:12:48 +0200 Subject: [PATCH 021/129] resolve conflict trait location --- lib/Models/SearchProvider/StubSearchProvider.ts | 2 +- lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts | 6 +++--- lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts | 2 +- lib/Traits/SearchProvider/LocationSearchProviderTraits.ts | 2 +- lib/Traits/SearchProvider/SearchProviderTraits.ts | 2 +- .../SearchProvider/WebFeatureServiceSearchProviderTraits.ts | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Models/SearchProvider/StubSearchProvider.ts b/lib/Models/SearchProvider/StubSearchProvider.ts index 4fec65f6175..bd0e99969e2 100644 --- a/lib/Models/SearchProvider/StubSearchProvider.ts +++ b/lib/Models/SearchProvider/StubSearchProvider.ts @@ -1,5 +1,5 @@ import LocationSearchProviderTraits from "./../../Traits/SearchProvider/LocationSearchProviderTraits"; -import primitiveTrait from "./../../Traits/primitiveTrait"; +import primitiveTrait from "./../../Traits/Decorators/primitiveTrait"; import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; import CreateModel from "../CreateModel"; import SearchProviderResults from "./SearchProviderResults"; diff --git a/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts b/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts index f39d28a1227..81649828868 100644 --- a/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts @@ -1,5 +1,5 @@ import mixTraits from "../mixTraits"; -import primitiveTrait from "../primitiveTrait"; +import primitiveTrait from "../Decorators/primitiveTrait"; import LocationSearchProviderTraits, { SearchProviderMapCenterTraits } from "./LocationSearchProviderTraits"; @@ -27,8 +27,8 @@ export default class BingMapsSearchProviderTraits extends mixTraits( @primitiveTrait({ type: "string", name: "Culture", - description: `Use the culture parameter to specify a culture for your request. - The culture parameter provides the result in the language of the culture. + description: `Use the culture parameter to specify a culture for your request. + The culture parameter provides the result in the language of the culture. For a list of supported cultures, see [Supported Culture Codes](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes)` }) culture: string = "en-au"; diff --git a/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts b/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts index 7808d9d81b4..2dc2ff84927 100644 --- a/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts @@ -1,6 +1,6 @@ import mixTraits from "../mixTraits"; import SearchProviderTraits from "./SearchProviderTraits"; -import primitiveTrait from "../primitiveTrait"; +import primitiveTrait from "../Decorators/primitiveTrait"; export default class CatalogSearchProviderTraits extends mixTraits( SearchProviderTraits diff --git a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts index 2893c7ea07f..cb00c39d7c8 100644 --- a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts @@ -1,5 +1,5 @@ import ModelTraits from "../ModelTraits"; -import primitiveTrait from "../primitiveTrait"; +import primitiveTrait from "../Decorators/primitiveTrait"; import SearchProviderTraits from "./SearchProviderTraits"; export default class LocationSearchProviderTraits extends SearchProviderTraits { diff --git a/lib/Traits/SearchProvider/SearchProviderTraits.ts b/lib/Traits/SearchProvider/SearchProviderTraits.ts index f19e6449184..175ed48a22e 100644 --- a/lib/Traits/SearchProvider/SearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/SearchProviderTraits.ts @@ -1,5 +1,5 @@ import ModelTraits from "../ModelTraits"; -import primitiveTrait from "../primitiveTrait"; +import primitiveTrait from "../Decorators/primitiveTrait"; export default class SearchProviderTraits extends ModelTraits { @primitiveTrait({ diff --git a/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts b/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts index c95945ff74c..1be4884242e 100644 --- a/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts +++ b/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts @@ -1,4 +1,4 @@ -import primitiveTrait from "../primitiveTrait"; +import primitiveTrait from "../Decorators/primitiveTrait"; import LocationSearchProviderTraits from "./LocationSearchProviderTraits"; export default class WebFeatureServiceSearchProviderTraits extends LocationSearchProviderTraits { From ea9618849d41abf35ba3552d9874dd3cd20dba6e Mon Sep 17 00:00:00 2001 From: zoran995 Date: Thu, 8 Jul 2021 13:29:49 +0200 Subject: [PATCH 022/129] fix lint --- lib/ReactViews/Mobile/MobileHeader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index 8a4599b16a2..d03e128a971 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -141,7 +141,7 @@ const MobileHeader = observer( render() { const searchState = this.props.viewState.searchState; - const { t } = this.props; + const { t, terria } = this.props; const nowViewingLength = terria.workbench.items !== undefined ? terria.workbench.items.length From da980b94caea4f8ffce74706470af3bf5f7e1304 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 2 Sep 2021 19:19:12 +0200 Subject: [PATCH 023/129] rename directory to searchproviders --- .../LocationSearchProviderMixin.ts | 4 ++-- .../SearchProviderMixin.ts | 6 +++--- .../WebFeatureServiceSearchProviderMixin.ts | 6 +++--- .../AustralianGazetteerSearchProvider.ts | 8 ++++---- .../BingMapsSearchProvider.ts | 16 ++++++++-------- .../CatalogSearchProvider.ts | 4 ++-- .../SearchProviderFactory.ts | 0 .../SearchProviderResults.ts | 4 ++-- .../SearchResult.ts | 0 .../StubSearchProvider.ts | 6 +++--- .../createStubSearchProvider.ts | 6 +++--- .../registerSearchProviders.ts | 0 .../upsertSearchProviderFromJson.ts | 8 ++++---- lib/Models/Terria.ts | 8 +++----- lib/ReactViewModels/SearchState.ts | 8 ++++---- lib/ReactViews/Search/LocationSearchResults.tsx | 4 ++-- .../BingMapsSearchProviderTraits.ts | 0 .../CatalogSearchProviderTraits.ts | 0 .../LocationSearchProviderTraits.ts | 0 .../SearchProviderTraits.ts | 0 .../WebFeatureServiceSearchProviderTraits.ts | 0 .../AustralianGazetteerSearchProviderSpec.ts | 2 +- .../BingMapsSearchProviderTraitsSpec.ts | 4 ++-- .../LocationSearchProviderTraitsSpec.ts | 4 ++-- 24 files changed, 48 insertions(+), 50 deletions(-) rename lib/ModelMixins/{SearchProvider => SearchProviders}/LocationSearchProviderMixin.ts (98%) rename lib/ModelMixins/{SearchProvider => SearchProviders}/SearchProviderMixin.ts (91%) rename lib/ModelMixins/{SearchProvider => SearchProviders}/WebFeatureServiceSearchProviderMixin.ts (97%) rename lib/Models/{SearchProvider => SearchProviders}/AustralianGazetteerSearchProvider.ts (98%) rename lib/Models/{SearchProvider => SearchProviders}/BingMapsSearchProvider.ts (97%) rename lib/Models/{SearchProvider => SearchProviders}/CatalogSearchProvider.ts (98%) rename lib/Models/{SearchProvider => SearchProviders}/SearchProviderFactory.ts (100%) rename lib/Models/{SearchProvider => SearchProviders}/SearchProviderResults.ts (96%) rename lib/Models/{SearchProvider => SearchProviders}/SearchResult.ts (100%) rename lib/Models/{SearchProvider => SearchProviders}/StubSearchProvider.ts (82%) rename lib/Models/{SearchProvider => SearchProviders}/createStubSearchProvider.ts (90%) rename lib/Models/{SearchProvider => SearchProviders}/registerSearchProviders.ts (100%) rename lib/Models/{SearchProvider => SearchProviders}/upsertSearchProviderFromJson.ts (94%) rename lib/Traits/{SearchProvider => SearchProviders}/BingMapsSearchProviderTraits.ts (100%) rename lib/Traits/{SearchProvider => SearchProviders}/CatalogSearchProviderTraits.ts (100%) rename lib/Traits/{SearchProvider => SearchProviders}/LocationSearchProviderTraits.ts (100%) rename lib/Traits/{SearchProvider => SearchProviders}/SearchProviderTraits.ts (100%) rename lib/Traits/{SearchProvider => SearchProviders}/WebFeatureServiceSearchProviderTraits.ts (100%) rename test/Models/{SearchProvider => SearchProviders}/AustralianGazetteerSearchProviderSpec.ts (95%) rename test/Traits/{SearchProvider => SearchProviders}/BingMapsSearchProviderTraitsSpec.ts (91%) rename test/Traits/{SearchProvider => SearchProviders}/LocationSearchProviderTraitsSpec.ts (92%) diff --git a/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts similarity index 98% rename from lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts rename to lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts index 15e40342c25..51600365987 100644 --- a/lib/ModelMixins/SearchProvider/LocationSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts @@ -3,10 +3,10 @@ import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import Constructor from "../../Core/Constructor"; +import CommonStrata from "../../Models/CommonStrata"; import Model from "../../Models/Model"; import Terria from "../../Models/Terria"; -import CommonStrata from "../../Models/CommonStrata"; -import LocationSearchProviderTraits from "../../Traits/SearchProvider/LocationSearchProviderTraits"; +import LocationSearchProviderTraits from "../../Traits/SearchProviders/LocationSearchProviderTraits"; import SearchProviderMixin from "./SearchProviderMixin"; type LocationSearchProviderModel = Model; diff --git a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts similarity index 91% rename from lib/ModelMixins/SearchProvider/SearchProviderMixin.ts rename to lib/ModelMixins/SearchProviders/SearchProviderMixin.ts index e62d16c54fa..4e961471f8a 100644 --- a/lib/ModelMixins/SearchProvider/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts @@ -1,10 +1,10 @@ +import i18next from "i18next"; import { action } from "mobx"; import { fromPromise } from "mobx-utils"; import Constructor from "../../Core/Constructor"; import Model from "../../Models/Model"; -import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; -import SearchProviderTraits from "../../Traits/SearchProvider/SearchProviderTraits"; -import i18next from "i18next"; +import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; +import SearchProviderTraits from "../../Traits/SearchProviders/SearchProviderTraits"; type SearchProviderModel = Model; diff --git a/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts similarity index 97% rename from lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts rename to lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index 80d5e68e508..d205658edd3 100644 --- a/lib/ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -6,10 +6,10 @@ import Constructor from "../../Core/Constructor"; import makeRealPromise from "../../Core/makeRealPromise"; import zoomRectangleFromPoint from "../../Map/zoomRectangleFromPoint"; import Model from "../../Models/Model"; -import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; -import SearchResult from "../../Models/SearchProvider/SearchResult"; +import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; +import SearchResult from "../../Models/SearchProviders/SearchResult"; import xml2json from "../../ThirdParty/xml2json"; -import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; +import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProviders/WebFeatureServiceSearchProviderTraits"; import LocationSearchProviderMixin from "./LocationSearchProviderMixin"; function WebFeatureServiceSearchProviderMixin< diff --git a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts b/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts similarity index 98% rename from lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts rename to lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts index e9725ca0cfb..48f7a21c7d2 100644 --- a/lib/Models/SearchProvider/AustralianGazetteerSearchProvider.ts +++ b/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts @@ -1,11 +1,11 @@ -import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProvider/WebFeatureServiceSearchProviderTraits"; -import CreateModel from "../CreateModel"; -import WebFeatureServiceSearchProviderMixin from "../../ModelMixins/SearchProvider/WebFeatureServiceSearchProviderMixin"; -import SearchResult from "./SearchResult"; import { Category, SearchAction } from "../../Core/AnalyticEvents/analyticEvents"; +import WebFeatureServiceSearchProviderMixin from "../../ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin"; +import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProviders/WebFeatureServiceSearchProviderTraits"; +import CreateModel from "../CreateModel"; +import SearchResult from "./SearchResult"; const featureCodesToNamesMap = new Map([ ["AF", "Aviation"], diff --git a/lib/Models/SearchProvider/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts similarity index 97% rename from lib/Models/SearchProvider/BingMapsSearchProvider.ts rename to lib/Models/SearchProviders/BingMapsSearchProvider.ts index 6a075946c65..b62eb5e62ae 100644 --- a/lib/Models/SearchProvider/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -3,20 +3,20 @@ import { runInAction } from "mobx"; import defined from "terriajs-cesium/Source/Core/defined"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import Resource from "terriajs-cesium/Source/Core/Resource"; +import { + Category, + SearchAction +} from "../../Core/AnalyticEvents/analyticEvents"; import loadJsonp from "../../Core/loadJsonp"; import LocationSearchProviderMixin, { getMapCenter -} from "../../ModelMixins/SearchProvider/LocationSearchProviderMixin"; -import BingMapsSearchProviderTraits from "../../Traits/SearchProvider/BingMapsSearchProviderTraits"; +} from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import BingMapsSearchProviderTraits from "../../Traits/SearchProviders/BingMapsSearchProviderTraits"; +import CommonStrata from "../CommonStrata"; import CreateModel from "../CreateModel"; +import Terria from "../Terria"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; -import CommonStrata from "./../CommonStrata"; -import Terria from "../Terria"; -import { - Category, - SearchAction -} from "../../Core/AnalyticEvents/analyticEvents"; export default class BingMapsSearchProvider extends LocationSearchProviderMixin( CreateModel(BingMapsSearchProviderTraits) diff --git a/lib/Models/SearchProvider/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts similarity index 98% rename from lib/Models/SearchProvider/CatalogSearchProvider.ts rename to lib/Models/SearchProviders/CatalogSearchProvider.ts index a5fe35e79a7..9e84d7ebcfd 100644 --- a/lib/Models/SearchProvider/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -5,8 +5,8 @@ import { } from "../../Core/AnalyticEvents/analyticEvents"; import GroupMixin from "../../ModelMixins/GroupMixin"; import ReferenceMixin from "../../ModelMixins/ReferenceMixin"; -import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; -import CatalogSearchProviderTraits from "../../Traits/SearchProvider/CatalogSearchProviderTraits"; +import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProviderMixin"; +import CatalogSearchProviderTraits from "../../Traits/SearchProviders/CatalogSearchProviderTraits"; import CreateModel from "../CreateModel"; import Terria from "../Terria"; import SearchProviderResults from "./SearchProviderResults"; diff --git a/lib/Models/SearchProvider/SearchProviderFactory.ts b/lib/Models/SearchProviders/SearchProviderFactory.ts similarity index 100% rename from lib/Models/SearchProvider/SearchProviderFactory.ts rename to lib/Models/SearchProviders/SearchProviderFactory.ts diff --git a/lib/Models/SearchProvider/SearchProviderResults.ts b/lib/Models/SearchProviders/SearchProviderResults.ts similarity index 96% rename from lib/Models/SearchProvider/SearchProviderResults.ts rename to lib/Models/SearchProviders/SearchProviderResults.ts index 8f2c887bd31..df3cf9e6538 100644 --- a/lib/Models/SearchProvider/SearchProviderResults.ts +++ b/lib/Models/SearchProviders/SearchProviderResults.ts @@ -1,7 +1,7 @@ import { observable } from "mobx"; -import SearchResult from "./SearchResult"; import { fromPromise, IPromiseBasedObservable } from "mobx-utils"; -import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProviderMixin"; +import SearchResult from "./SearchResult"; export default class SearchProviderResults { @observable results: SearchResult[] = []; diff --git a/lib/Models/SearchProvider/SearchResult.ts b/lib/Models/SearchProviders/SearchResult.ts similarity index 100% rename from lib/Models/SearchProvider/SearchResult.ts rename to lib/Models/SearchProviders/SearchResult.ts diff --git a/lib/Models/SearchProvider/StubSearchProvider.ts b/lib/Models/SearchProviders/StubSearchProvider.ts similarity index 82% rename from lib/Models/SearchProvider/StubSearchProvider.ts rename to lib/Models/SearchProviders/StubSearchProvider.ts index bd0e99969e2..b6fe2e47719 100644 --- a/lib/Models/SearchProvider/StubSearchProvider.ts +++ b/lib/Models/SearchProviders/StubSearchProvider.ts @@ -1,6 +1,6 @@ -import LocationSearchProviderTraits from "./../../Traits/SearchProvider/LocationSearchProviderTraits"; -import primitiveTrait from "./../../Traits/Decorators/primitiveTrait"; -import SearchProviderMixin from "../../ModelMixins/SearchProvider/SearchProviderMixin"; +import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProviderMixin"; +import primitiveTrait from "../../Traits/Decorators/primitiveTrait"; +import LocationSearchProviderTraits from "../../Traits/SearchProviders/LocationSearchProviderTraits"; import CreateModel from "../CreateModel"; import SearchProviderResults from "./SearchProviderResults"; diff --git a/lib/Models/SearchProvider/createStubSearchProvider.ts b/lib/Models/SearchProviders/createStubSearchProvider.ts similarity index 90% rename from lib/Models/SearchProvider/createStubSearchProvider.ts rename to lib/Models/SearchProviders/createStubSearchProvider.ts index 76434e11ba8..914668b8038 100644 --- a/lib/Models/SearchProvider/createStubSearchProvider.ts +++ b/lib/Models/SearchProviders/createStubSearchProvider.ts @@ -1,7 +1,7 @@ -import Terria from "./../Terria"; -import StubSearchProvider from "./StubSearchProvider"; -import CommonStrata from "./../CommonStrata"; +import CommonStrata from "../CommonStrata"; import { BaseModel } from "../Model"; +import Terria from "../Terria"; +import StubSearchProvider from "./StubSearchProvider"; const getUniqueStubSearchProviderName = (terria: Terria) => { const stubName = "[StubSearchProvider]"; diff --git a/lib/Models/SearchProvider/registerSearchProviders.ts b/lib/Models/SearchProviders/registerSearchProviders.ts similarity index 100% rename from lib/Models/SearchProvider/registerSearchProviders.ts rename to lib/Models/SearchProviders/registerSearchProviders.ts diff --git a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts similarity index 94% rename from lib/Models/SearchProvider/upsertSearchProviderFromJson.ts rename to lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index c9c7427e756..fbbe43fbde4 100644 --- a/lib/Models/SearchProvider/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -1,9 +1,9 @@ -import Terria from "./../Terria"; -import ModelFactory from "./../ModelFactory"; -import { BaseModel } from "../Model"; import i18next from "i18next"; import TerriaError from "../../Core/TerriaError"; -import CommonStrata from "./../CommonStrata"; +import CommonStrata from "../CommonStrata"; +import { BaseModel } from "../Model"; +import ModelFactory from "../ModelFactory"; +import Terria from "../Terria"; import updateModelFromJson from "../updateModelFromJson"; import createStubSearchProvider from "./createStubSearchProvider"; diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index ff2008b3ad8..072674d6d7f 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -57,9 +57,7 @@ import ReferenceMixin from "../ModelMixins/ReferenceMixin"; import TimeVarying from "../ModelMixins/TimeVarying"; import { HelpContentItem } from "../ReactViewModels/defaultHelpContent"; import { defaultTerms, Term } from "../ReactViewModels/defaultTerms"; -import NotificationState, { - Notification -} from "../ReactViewModels/NotificationState"; +import NotificationState from "../ReactViewModels/NotificationState"; import { shareConvertNotification } from "../ReactViews/Notification/shareConvertNotification"; import MappableTraits from "../Traits/TraitsClasses/MappableTraits"; import { BaseMapViewModel } from "../ViewModels/BaseMapViewModel"; @@ -90,8 +88,8 @@ import MapInteractionMode from "./MapInteractionMode"; import { BaseModel } from "./Model"; import NoViewer from "./NoViewer"; import openGroup from "./openGroup"; -import SearchProviderFactory from "./SearchProvider/SearchProviderFactory"; -import upsertSearchProviderFromJson from "./SearchProvider/upsertSearchProviderFromJson"; +import SearchProviderFactory from "./SearchProviders/SearchProviderFactory"; +import upsertSearchProviderFromJson from "./SearchProviders/upsertSearchProviderFromJson"; import ShareDataService from "./ShareDataService"; import SplitItemReference from "./SplitItemReference"; import TimelineStack from "./TimelineStack"; diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index dfe12ad9bea..7fa7acbbc39 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -6,10 +6,10 @@ import { reaction } from "mobx"; import filterOutUndefined from "../Core/filterOutUndefined"; -import LocationSearchProviderMixin from "../ModelMixins/SearchProvider/LocationSearchProviderMixin"; -import SearchProviderMixin from "../ModelMixins/SearchProvider/SearchProviderMixin"; -import CatalogSearchProvider from "../Models/SearchProvider/CatalogSearchProvider"; -import SearchProviderResults from "../Models/SearchProvider/SearchProviderResults"; +import LocationSearchProviderMixin from "../ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import SearchProviderMixin from "../ModelMixins/SearchProviders/SearchProviderMixin"; +import CatalogSearchProvider from "../Models/SearchProviders/CatalogSearchProvider"; +import SearchProviderResults from "../Models/SearchProviders/SearchProviderResults"; import Terria from "../Models/Terria"; interface SearchStateOptions { diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 8189ab45c71..45e83b0e602 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -15,7 +15,8 @@ import { } from "react-i18next"; import styled, { DefaultTheme } from "styled-components"; import isDefined from "../../Core/isDefined"; -import SearchProviderResults from "../../Models/SearchProvider/SearchProviderResults"; +import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; import Terria from "../../Models/Terria"; import ViewState from "../../ReactViewModels/ViewState"; import Box, { BoxSpan } from "../../Styled/Box"; @@ -24,7 +25,6 @@ import Icon, { StyledIcon } from "../../Styled/Icon"; import Ul from "../../Styled/List"; import Text, { TextSpan } from "../../Styled/Text"; import Loader from "../Loader"; -import LocationSearchProviderMixin from "./../../ModelMixins/SearchProvider/LocationSearchProviderMixin"; import SearchHeader from "./SearchHeader"; import SearchResult from "./SearchResult"; diff --git a/lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts b/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts similarity index 100% rename from lib/Traits/SearchProvider/BingMapsSearchProviderTraits.ts rename to lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts diff --git a/lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts b/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts similarity index 100% rename from lib/Traits/SearchProvider/CatalogSearchProviderTraits.ts rename to lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts diff --git a/lib/Traits/SearchProvider/LocationSearchProviderTraits.ts b/lib/Traits/SearchProviders/LocationSearchProviderTraits.ts similarity index 100% rename from lib/Traits/SearchProvider/LocationSearchProviderTraits.ts rename to lib/Traits/SearchProviders/LocationSearchProviderTraits.ts diff --git a/lib/Traits/SearchProvider/SearchProviderTraits.ts b/lib/Traits/SearchProviders/SearchProviderTraits.ts similarity index 100% rename from lib/Traits/SearchProvider/SearchProviderTraits.ts rename to lib/Traits/SearchProviders/SearchProviderTraits.ts diff --git a/lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts b/lib/Traits/SearchProviders/WebFeatureServiceSearchProviderTraits.ts similarity index 100% rename from lib/Traits/SearchProvider/WebFeatureServiceSearchProviderTraits.ts rename to lib/Traits/SearchProviders/WebFeatureServiceSearchProviderTraits.ts diff --git a/test/Models/SearchProvider/AustralianGazetteerSearchProviderSpec.ts b/test/Models/SearchProviders/AustralianGazetteerSearchProviderSpec.ts similarity index 95% rename from test/Models/SearchProvider/AustralianGazetteerSearchProviderSpec.ts rename to test/Models/SearchProviders/AustralianGazetteerSearchProviderSpec.ts index 9ffe6205216..1c7faea9edc 100644 --- a/test/Models/SearchProvider/AustralianGazetteerSearchProviderSpec.ts +++ b/test/Models/SearchProviders/AustralianGazetteerSearchProviderSpec.ts @@ -1,6 +1,6 @@ import { configure } from "mobx"; +import AustralianGazetteerSearchProvider from "../../../lib/Models/SearchProviders/AustralianGazetteerSearchProvider"; import Terria from "../../../lib/Models/Terria"; -import AustralianGazetteerSearchProvider from "../../../lib/Models/SearchProvider/AustralianGazetteerSearchProvider"; const wfsResponseXml = require("raw-loader!../../../wwwroot/test/WFS/getWithFilter.xml"); diff --git a/test/Traits/SearchProvider/BingMapsSearchProviderTraitsSpec.ts b/test/Traits/SearchProviders/BingMapsSearchProviderTraitsSpec.ts similarity index 91% rename from test/Traits/SearchProvider/BingMapsSearchProviderTraitsSpec.ts rename to test/Traits/SearchProviders/BingMapsSearchProviderTraitsSpec.ts index 1d4ca1f6ad7..96678eab1c1 100644 --- a/test/Traits/SearchProvider/BingMapsSearchProviderTraitsSpec.ts +++ b/test/Traits/SearchProviders/BingMapsSearchProviderTraitsSpec.ts @@ -1,6 +1,6 @@ +import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import BingMapsSearchProvider from "../../../lib/Models/SearchProviders/BingMapsSearchProvider"; import Terria from "../../../lib/Models/Terria"; -import BingMapsSearchProvider from "../../../lib/Models/SearchProvider/BingMapsSearchProvider"; -import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProvider/LocationSearchProviderMixin"; describe("BingMapsSearchProviderTraits", function() { let terria: Terria; diff --git a/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts b/test/Traits/SearchProviders/LocationSearchProviderTraitsSpec.ts similarity index 92% rename from test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts rename to test/Traits/SearchProviders/LocationSearchProviderTraitsSpec.ts index e07d7033acf..4ef2f6f9d44 100644 --- a/test/Traits/SearchProvider/LocationSearchProviderTraitsSpec.ts +++ b/test/Traits/SearchProviders/LocationSearchProviderTraitsSpec.ts @@ -1,6 +1,6 @@ +import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import BingMapsSearchProvider from "../../../lib/Models/SearchProviders/BingMapsSearchProvider"; import Terria from "../../../lib/Models/Terria"; -import BingMapsSearchProvider from "../../../lib/Models/SearchProvider/BingMapsSearchProvider"; -import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProvider/LocationSearchProviderMixin"; describe("LocationSearchProviderTraits", function() { let terria: Terria; From 69b95223548b3dc3792c6473b78b9c285f27c1f0 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 2 Sep 2021 19:42:48 +0200 Subject: [PATCH 024/129] add default value to zoomTo --- lib/Models/GlobeOrMap.ts | 4 ++-- lib/Models/SearchProviders/BingMapsSearchProvider.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Models/GlobeOrMap.ts b/lib/Models/GlobeOrMap.ts index 120702c91e5..02c8d963bae 100644 --- a/lib/Models/GlobeOrMap.ts +++ b/lib/Models/GlobeOrMap.ts @@ -24,9 +24,9 @@ import TimeVarying from "../ModelMixins/TimeVarying"; import MouseCoords from "../ReactViewModels/MouseCoords"; import CameraView from "./CameraView"; import Cesium3DTilesCatalogItem from "./Catalog/CatalogItems/Cesium3DTilesCatalogItem"; +import GeoJsonCatalogItem from "./Catalog/CatalogItems/GeoJsonCatalogItem"; import CommonStrata from "./Definition/CommonStrata"; import Feature from "./Feature"; -import GeoJsonCatalogItem from "./Catalog/CatalogItems/GeoJsonCatalogItem"; import Terria from "./Terria"; require("./ImageryLayerFeatureInfo"); // overrides Cesium's prototype.configureDescriptionFromProperties @@ -70,7 +70,7 @@ export default abstract class GlobeOrMap { @action zoomTo( target: CameraView | Rectangle | MappableMixin.Instance, - flightDurationSeconds: number + flightDurationSeconds: number = 3.0 ): Promise { this.isMapZooming = true; const zoomId = createGuid(); diff --git a/lib/Models/SearchProviders/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts index a35980a1718..ab125b0231e 100644 --- a/lib/Models/SearchProviders/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -182,6 +182,6 @@ function createZoomToFunction(model: BingMapsSearchProvider, resource: any) { return function() { const terria = model.terria; - terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds!); + terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds); }; } From 819c7abf8de7f567cbabd8e21826eabda6820b1f Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 2 Sep 2021 19:50:26 +0200 Subject: [PATCH 025/129] fix bad merge --- .../Ows/WebFeatureServiceSearchProvider.ts | 221 ------------------ lib/Models/SearchProviders/SearchProvider.ts | 27 --- 2 files changed, 248 deletions(-) delete mode 100644 lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts delete mode 100644 lib/Models/SearchProviders/SearchProvider.ts diff --git a/lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts b/lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts deleted file mode 100644 index 55ac7ddeac9..00000000000 --- a/lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts +++ /dev/null @@ -1,221 +0,0 @@ -import i18next from "i18next"; -import { runInAction } from "mobx"; -import URI from "urijs"; -import zoomRectangleFromPoint from "../../../Map/zoomRectangleFromPoint"; -import xml2json from "../../../ThirdParty/xml2json"; -import SearchProvider from "../../SearchProviders/SearchProvider"; -import SearchProviderResults from "../../SearchProviders/SearchProviderResults"; -import SearchResult from "../../SearchProviders/SearchResult"; -import Terria from "../../Terria"; -import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; -import Resource from "terriajs-cesium/Source/Core/Resource"; -import makeRealPromise from "../../../Core/makeRealPromise"; - -export interface WebFeatureServiceSearchProviderOptions { - /** Base url for the service */ - wfsServiceUrl: string; - /** Which property to look for the search text in */ - searchPropertyName: string; - /** Type of the properties to search */ - searchPropertyTypeName: string; - /** Convert a WFS feature to a search result */ - featureToSearchResultFunction: (feature: any) => SearchResult; - terria: Terria; - /** How long it takes to zoom in when a search result is clicked */ - flightDurationSeconds?: number; - /** Apply a function to search text before it gets passed to the service. Useful for changing case */ - transformSearchText?: (searchText: string) => string; - /** Return true if a feature should be included in search results */ - searchResultFilterFunction?: (feature: any) => boolean; - /** Return a score that gets used to sort results (in descending order) */ - searchResultScoreFunction?: (feature: any, searchText: string) => number; - /** name of the search provider */ - name: string; -} - -export default class WebFeatureServiceSearchProvider extends SearchProvider { - private _wfsServiceUrl: uri.URI; - private _searchPropertyName: string; - private _searchPropertyTypeName: string; - private _featureToSearchResultFunction: (feature: any) => SearchResult; - flightDurationSeconds: number; - readonly terria: Terria; - private _transformSearchText: ((searchText: string) => string) | undefined; - private _searchResultFilterFunction: ((feature: any) => boolean) | undefined; - private _searchResultScoreFunction: - | ((feature: any, searchText: string) => number) - | undefined; - cancelRequest?: () => void; - private _waitingForResults: boolean = false; - - constructor(options: WebFeatureServiceSearchProviderOptions) { - super(); - this._wfsServiceUrl = new URI(options.wfsServiceUrl); - this._searchPropertyName = options.searchPropertyName; - this._searchPropertyTypeName = options.searchPropertyTypeName; - this._featureToSearchResultFunction = options.featureToSearchResultFunction; - this.terria = options.terria; - this.flightDurationSeconds = defaultValue( - options.flightDurationSeconds, - 1.5 - ); - this._transformSearchText = options.transformSearchText; - this._searchResultFilterFunction = options.searchResultFilterFunction; - this._searchResultScoreFunction = options.searchResultScoreFunction; - this.name = options.name; - } - - getXml(): Promise { - const resource = new Resource({ url: this._wfsServiceUrl.toString() }); - this._waitingForResults = true; - const xmlPromise = resource.fetchXML(); - this.cancelRequest = resource.request.cancelFunction; - return makeRealPromise(xmlPromise).finally(() => { - this._waitingForResults = false; - }); - } - - protected doSearch( - searchText: string, - results: SearchProviderResults - ): Promise { - results.results.length = 0; - results.message = undefined; - - if (this._waitingForResults) { - // There's been a new search! Cancel the previous one. - if (this.cancelRequest !== undefined) { - this.cancelRequest(); - this.cancelRequest = undefined; - } - this._waitingForResults = false; - } - - const originalSearchText = searchText; - - searchText = searchText.trim(); - if (this._transformSearchText !== undefined) { - searchText = this._transformSearchText(searchText); - } - if (searchText.length < 2) { - return Promise.resolve(); - } - - // Support for matchCase="false" is patchy, but we try anyway - const filter = ` - ${this._searchPropertyName} - *${searchText}*`; - - this._wfsServiceUrl.setSearch({ - service: "WFS", - request: "GetFeature", - typeName: this._searchPropertyTypeName, - version: "1.1.0", - srsName: "urn:ogc:def:crs:EPSG::4326", // srsName must be formatted like this for correct lat/long order >:( - filter: filter - }); - - return this.getXml() - .then((xml: any) => { - let json: any = xml2json(xml); - let features: any[]; - if (json === undefined) { - results.message = i18next.t("viewModels.searchErrorOccurred"); - return; - } - - if (json.member !== undefined) { - features = json.member; - } else if (json.featureMember !== undefined) { - features = json.featureMember; - } else { - results.message = i18next.t("viewModels.searchNoPlaceNames"); - return; - } - - // if there's only one feature, make it an array - if (!Array.isArray(features)) { - features = [features]; - } - - const resultSet = new Set(); - - runInAction(() => { - if (this._searchResultFilterFunction !== undefined) { - features = features.filter(this._searchResultFilterFunction); - } - - if (features.length === 0) { - results.message = i18next.t("viewModels.searchNoPlaceNames"); - return; - } - - if (this._searchResultScoreFunction !== undefined) { - features = features.sort( - (featureA, featureB) => - this._searchResultScoreFunction!(featureB, originalSearchText) - - this._searchResultScoreFunction!(featureA, originalSearchText) - ); - } - - let searchResults = features - .map(this._featureToSearchResultFunction) - .map(result => { - result.clickAction = createZoomToFunction(this, result.location); - return result; - }); - - // If we don't have a scoring function, sort the search results now - // We can't do this earlier because we don't know what the schema of the unprocessed feature looks like - if (this._searchResultScoreFunction === undefined) { - // Put shorter results first - // They have a larger percentage of letters that the user actually typed in them - searchResults = searchResults.sort( - (featureA, featureB) => - featureA.name.length - featureB.name.length - ); - } - - // Remove results that have the same name and are close to each other - searchResults = searchResults.filter(result => { - const hash = `${result.name},${result.location?.latitude.toFixed( - 1 - )},${result.location?.longitude.toFixed(1)}`; - if (resultSet.has(hash)) { - return false; - } - resultSet.add(hash); - return true; - }); - - // append new results to all results - results.results.push(...searchResults); - }); - }) - .catch(e => { - if (results.isCanceled) { - // A new search has superseded this one, so ignore the result. - return; - } - results.message = i18next.t("viewModels.searchErrorOccurred"); - }); - } -} - -function createZoomToFunction( - model: WebFeatureServiceSearchProvider, - location: any -) { - // Server does not return information of a bounding box, just a location. - // bboxSize is used to expand a point - var bboxSize = 0.2; - var rectangle = zoomRectangleFromPoint( - location.latitude, - location.longitude, - bboxSize - ); - - return function() { - model.terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds); - }; -} diff --git a/lib/Models/SearchProviders/SearchProvider.ts b/lib/Models/SearchProviders/SearchProvider.ts deleted file mode 100644 index de29a899952..00000000000 --- a/lib/Models/SearchProviders/SearchProvider.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { action, observable } from "mobx"; -import { fromPromise } from "mobx-utils"; -import SearchProviderResults from "./SearchProviderResults"; - -export default abstract class SearchProvider { - @observable name = "Unknown"; - @observable isOpen = true; - - @action - toggleOpen() { - this.isOpen = !this.isOpen; - } - - @action - search(searchText: string): SearchProviderResults { - const result = new SearchProviderResults(this); - result.resultsCompletePromise = fromPromise( - this.doSearch(searchText, result) - ); - return result; - } - - protected abstract doSearch( - searchText: string, - results: SearchProviderResults - ): Promise; -} From 28702c809bf2816099819898bcb727cc337911c8 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 2 Sep 2021 20:00:13 +0200 Subject: [PATCH 026/129] don't update name in updateModelFromJson but when rendering --- lib/Models/Definition/updateModelFromJson.ts | 5 ----- lib/ReactViews/Search/LocationSearchResults.tsx | 6 ++++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/Models/Definition/updateModelFromJson.ts b/lib/Models/Definition/updateModelFromJson.ts index 69c7e6e1ad2..179edc0e2e2 100644 --- a/lib/Models/Definition/updateModelFromJson.ts +++ b/lib/Models/Definition/updateModelFromJson.ts @@ -2,7 +2,6 @@ import { isObservableArray, runInAction } from "mobx"; import isDefined from "../../Core/isDefined"; import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; -import { useTranslationIfExists } from "./../../Language/languageHelpers"; import createStratumInstance from "./createStratumInstance"; import { BaseModel } from "./Model"; @@ -63,10 +62,6 @@ export default function updateModelFromJson( } model.setTrait(stratumName, propertyName, newTrait); } - if (propertyName === "name") { - newTrait = useTranslationIfExists(jsonValue); - } - model.setTrait(stratumName, propertyName, newTrait); } }); }); diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 45e83b0e602..05020d7554d 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -15,6 +15,7 @@ import { } from "react-i18next"; import styled, { DefaultTheme } from "styled-components"; import isDefined from "../../Core/isDefined"; +import { useTranslationIfExists } from "../../Language/languageHelpers"; import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; import Terria from "../../Models/Terria"; @@ -189,8 +190,9 @@ const NameWithLoader: React.FC = observer( (props: NameWithLoaderProps) => ( - {`${props.name} (${props.length || - 0})`} + {`${useTranslationIfExists( + props.name + )} (${props.length || 0})`} {!props.isOpen && (props.search.isSearching || props.isWaitingForSearchToStart) && ( From 2463af83910db58feda6327644ebd5b0f48c0d06 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Thu, 2 Sep 2021 20:54:04 +0200 Subject: [PATCH 027/129] use new error handling --- .../upsertSearchProviderFromJson.ts | 45 +++++++++++++------ lib/Models/Terria.ts | 36 +++++++++++---- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index a54a282c714..f583b591a95 100644 --- a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -1,4 +1,5 @@ import i18next from "i18next"; +import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; import CommonStrata from "../Definition/CommonStrata"; import { BaseModel } from "../Definition/Model"; @@ -6,21 +7,26 @@ import ModelFactory from "../Definition/ModelFactory"; import updateModelFromJson from "../Definition/updateModelFromJson"; import Terria from "../Terria"; import createStubSearchProvider from "./createStubSearchProvider"; +import StubSearchProvider from "./StubSearchProvider"; export default function upsertSearchProviderFromJson( factory: ModelFactory, terria: Terria, stratumName: string, json: any -) { +): Result { + const errors: TerriaError[] = []; + let uniqueId = json.id; if (uniqueId === undefined) { const id = json.localId || json.name; if (id === undefined) { - throw new TerriaError({ - title: i18next.t("searchProvider.models.idForMatchingErrorTitle"), - message: i18next.t("searchProvider.models.idForMatchingErrorMessage") - }); + return Result.error( + new TerriaError({ + title: i18next.t("models.catalog.idForMatchingErrorTitle"), + message: i18next.t("models.catalog.idForMatchingErrorMessage") + }) + ); } uniqueId = id; } @@ -30,7 +36,7 @@ export default function upsertSearchProviderFromJson( if (model === undefined) { model = factory.create(json.type, uniqueId, terria); if (model === undefined) { - console.log( + errors.push( new TerriaError({ title: i18next.t("searchProvider.models.unsupportedTypeTitle"), message: i18next.t("searchProvider.models.unsupportedTypeMessage", { @@ -44,18 +50,29 @@ export default function upsertSearchProviderFromJson( stub.setTrait(CommonStrata.override, "name", `${uniqueId} (Stub)`); } - model?.terria.addSearchProvider(model); + if (model.type !== StubSearchProvider.type) { + try { + model.terria.addSearchProvider(model); + } catch (error) { + errors.push(error); + } + } } setDefaultTraits(model); - try { - updateModelFromJson(model, stratumName, json); - } catch (error) { - console.log(`Error updating search provider from JSON`); - console.log(error); - model?.setTrait(CommonStrata.underride, "isExperiencingIssues", true); - } + updateModelFromJson(model, stratumName, json).catchError(error => { + errors.push(error); + model!.setTrait(CommonStrata.underride, "isExperiencingIssues", true); + }); + + return new Result( + model, + TerriaError.combine( + errors, + `Error upserting search provider JSON: \`${uniqueId}\`` + ) + ); } function setDefaultTraits(model: BaseModel) { diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index aa9cb2ab6e5..34a8a631d97 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -914,12 +914,18 @@ export default class Terria { ) ); + this.initializeSearchProviders().catchError(error => + this.raiseErrorToUser( + TerriaError.from(error, "Failed to initialize searchProviders") + ) + ); + if (options.applicationUrl) { (await this.updateApplicationUrl(options.applicationUrl.href)).raiseError( this ); } - this.initializeSearchProviders(); + this.loadPersistedMapSettings(); } @@ -942,21 +948,33 @@ export default class Terria { } initializeSearchProviders() { + const errors: TerriaError[] = []; let searchProviders = this.configParameters.searchBar?.searchProviders; - if (!isObservableArray(searchProviders)) - throw new TerriaError({ - sender: SearchProviderFactory, - title: "SearchProviders", - message: "" - }); - searchProviders.forEach(searchProvider => { + if (!isObservableArray(searchProviders)) { + errors.push( + new TerriaError({ + sender: SearchProviderFactory, + title: "SearchProviders", + message: { key: "searchProvider.noSearchProviders" } + }) + ); + } + searchProviders?.forEach(searchProvider => { upsertSearchProviderFromJson( SearchProviderFactory, this, CommonStrata.definition, searchProvider - ); + ).pushErrorTo(errors); }); + + return new Result( + undefined, + TerriaError.combine( + errors, + "An error occurred while loading search providers" + ) + ); } async loadPersistedOrInitBaseMap() { From 8e15054af1f8d3dc40e0256da7df78334282b22e Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Fri, 3 Sep 2021 00:21:58 +0200 Subject: [PATCH 028/129] properly handle translations, and fix issue with catalog search provider not properly handling errors --- lib/Language/en/translation.json | 1 + lib/Language/languageHelpers.ts | 12 +++-- .../SearchProviders/SearchProviderMixin.ts | 10 ++-- .../WebFeatureServiceSearchProviderMixin.ts | 19 ++++--- .../SearchProviders/BingMapsSearchProvider.ts | 20 +++++--- .../SearchProviders/CatalogSearchProvider.ts | 49 +++++++++++-------- .../SearchProviders/SearchProviderResults.ts | 7 ++- .../Search/LocationSearchResults.tsx | 4 +- lib/ReactViews/Search/SearchHeader.tsx | 11 ++++- 9 files changed, 87 insertions(+), 46 deletions(-) diff --git a/lib/Language/en/translation.json b/lib/Language/en/translation.json index 6aedf624cbe..14c19281190 100644 --- a/lib/Language/en/translation.json +++ b/lib/Language/en/translation.json @@ -1520,6 +1520,7 @@ } }, "searchProvider": { + "noSearchProviders": "There is no configured search providers", "models": { "unsupportedTypeTitle": "Unknown type", "unsupportedTypeMessage": "Could not create unknown model type {{type}}.", diff --git a/lib/Language/languageHelpers.ts b/lib/Language/languageHelpers.ts index 3d57da40cee..065ddd55ae1 100644 --- a/lib/Language/languageHelpers.ts +++ b/lib/Language/languageHelpers.ts @@ -1,14 +1,18 @@ import i18next from "i18next"; + /** * Takes a given string and translates it if it exists, otherwise return */ -export function useTranslationIfExists(keyOrString: string) { - if (keyOrString && keyOrString.indexOf("translate#") === 0) { +export function useTranslationIfExists( + keyOrString: string = "", + options?: { [key: string]: string | number | undefined } +) { + if (keyOrString.indexOf("translate#") === 0) { const translationKey = keyOrString.substr("translate#".length); return i18next.exists(translationKey) - ? i18next.t(translationKey) + ? i18next.t(translationKey, options) : translationKey; } else { - return keyOrString || ""; + return keyOrString; } } diff --git a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts index aa488842053..fba60fd2b1b 100644 --- a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts @@ -1,4 +1,3 @@ -import i18next from "i18next"; import { action } from "mobx"; import { fromPromise } from "mobx-utils"; import Constructor from "../../Core/Constructor"; @@ -26,9 +25,12 @@ function SearchProviderMixin>( const result = new SearchProviderResults(this); if (!this.shouldRunSearch(searchText)) { result.resultsCompletePromise = fromPromise(Promise.resolve()); - result.message = i18next.t("viewModels.searchMinCharacters", { - count: this.minCharacters - }); + result.message = { + content: "translate#viewModels.searchMinCharacters", + params: { + count: this.minCharacters + } + }; return result; } this.logEvent(searchText); diff --git a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index d205658edd3..3fa2a49af40 100644 --- a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -1,11 +1,10 @@ -import i18next from "i18next"; import { runInAction } from "mobx"; import Resource from "terriajs-cesium/Source/Core/Resource"; import URI from "urijs"; import Constructor from "../../Core/Constructor"; import makeRealPromise from "../../Core/makeRealPromise"; import zoomRectangleFromPoint from "../../Map/zoomRectangleFromPoint"; -import Model from "../../Models/Model"; +import Model from "../../Models/Definition/Model"; import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; import SearchResult from "../../Models/SearchProviders/SearchResult"; import xml2json from "../../ThirdParty/xml2json"; @@ -91,7 +90,9 @@ function WebFeatureServiceSearchProviderMixin< let json: any = xml2json(xml); let features: any[]; if (json === undefined) { - results.message = i18next.t("viewModels.searchErrorOccurred"); + results.message = { + content: "translate#viewModels.searchErrorOccurred" + }; return; } @@ -100,7 +101,9 @@ function WebFeatureServiceSearchProviderMixin< } else if (json.featureMember !== undefined) { features = json.featureMember; } else { - results.message = i18next.t("viewModels.searchNoPlaceNames"); + results.message = { + content: "translate#translate#viewModels.searchNoPlaceNames" + }; return; } @@ -117,7 +120,9 @@ function WebFeatureServiceSearchProviderMixin< } if (features.length === 0) { - results.message = i18next.t("viewModels.searchNoPlaceNames"); + results.message = { + content: "translate#viewModels.searchNoPlaceNames" + }; return; } @@ -174,7 +179,9 @@ function WebFeatureServiceSearchProviderMixin< // A new search has superseded this one, so ignore the result. return; } - results.message = i18next.t("viewModels.searchErrorOccurred"); + results.message = { + content: "translate#viewModels.searchErrorOccurred" + }; }); } diff --git a/lib/Models/SearchProviders/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts index ab125b0231e..764c3c16574 100644 --- a/lib/Models/SearchProviders/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -1,4 +1,3 @@ -import i18next from "i18next"; import { runInAction } from "mobx"; import defined from "terriajs-cesium/Source/Core/defined"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; @@ -8,6 +7,7 @@ import { SearchAction } from "../../Core/AnalyticEvents/analyticEvents"; import loadJsonp from "../../Core/loadJsonp"; +import { useTranslationIfExists } from "../../Language/languageHelpers"; import LocationSearchProviderMixin, { getMapCenter } from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; @@ -43,7 +43,7 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( if (!this.key || this.key === "") { console.warn( "The " + - this.name + + useTranslationIfExists(this.name) + " 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." ); } @@ -91,13 +91,17 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( } if (result.resourceSets.length === 0) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); + searchResults.message = { + content: "translate#viewModels.searchNoLocations" + }; return; } var resourceSet = result.resourceSets[0]; if (resourceSet.resources.length === 0) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); + searchResults.message = { + content: "translate#viewModels.searchNoLocations" + }; return; } @@ -109,7 +113,9 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( }); if (searchResults.results.length === 0) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); + searchResults.message = { + content: "translate#viewModels.searchNoLocations" + }; } }) .catch(() => { @@ -118,7 +124,9 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( return; } - searchResults.message = i18next.t("viewModels.searchErrorOccurred"); + searchResults.message = { + content: "translate#viewModels.searchErrorOccurred" + }; }); } diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index e4f7b5b2686..7c02059e02b 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -69,7 +69,7 @@ export function loadAndSearchCatalogRecursively( if (referencesAndGroupsToLoad.length === 0) { return Promise.resolve(terria); } - return new Promise(resolve => { + return new Promise((resolve, reject) => { autorun(reaction => { Promise.all( referencesAndGroupsToLoad.map(async model => { @@ -82,18 +82,22 @@ export function loadAndSearchCatalogRecursively( // return model.loadMembers(); // } }) - ).then(() => { - // Then call this function again to see if new child references were loaded in - resolve( - loadAndSearchCatalogRecursively( - terria, - searchTextLowercase, - searchResults, - resultMap, - iteration + 1 - ) - ); - }); + ) + .then(() => { + // Then call this function again to see if new child references were loaded in + resolve( + loadAndSearchCatalogRecursively( + terria, + searchTextLowercase, + searchResults, + resultMap, + iteration + 1 + ) + ); + }) + .catch(error => { + reject(error); + }); reaction.dispose(); }); }); @@ -133,14 +137,12 @@ export default class CatalogSearchProvider extends SearchProviderMixin( const resultMap: ResultMap = new Map(); - const promise: Promise = loadAndSearchCatalogRecursively( + return loadAndSearchCatalogRecursively( this.terria, searchText.toLowerCase(), searchResults, resultMap - ); - - return promise + ) .then(terria => { runInAction(() => { this.isSearching = false; @@ -156,8 +158,9 @@ export default class CatalogSearchProvider extends SearchProviderMixin( }); if (searchResults.results.length === 0) { - searchResults.message = - "Sorry, no locations match your search query."; + searchResults.message = { + content: "translate#viewModels.searchNoLocations" + }; } }) .catch(() => { @@ -165,9 +168,13 @@ export default class CatalogSearchProvider extends SearchProviderMixin( // A new search has superseded this one, so ignore the result. return; } + runInAction(() => { + this.isSearching = false; + }); - searchResults.message = - "An error occurred while searching. Please check your internet connection or try again later."; + searchResults.message = { + content: "translate#viewModels.searchErrorOccurred" + }; }); } } diff --git a/lib/Models/SearchProviders/SearchProviderResults.ts b/lib/Models/SearchProviders/SearchProviderResults.ts index df3cf9e6538..9718de635d5 100644 --- a/lib/Models/SearchProviders/SearchProviderResults.ts +++ b/lib/Models/SearchProviders/SearchProviderResults.ts @@ -5,7 +5,12 @@ import SearchResult from "./SearchResult"; export default class SearchProviderResults { @observable results: SearchResult[] = []; - @observable message: string | undefined; + @observable message?: { + content: string; + params?: { + [key: string]: string | number | undefined; + }; + }; isCanceled = false; resultsCompletePromise: IPromiseBasedObservable = fromPromise( Promise.resolve() diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 05020d7554d..4c70a0a9d5a 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -170,11 +170,11 @@ const SearchResultsFooter: React.FC = ( const { t } = useTranslation(); if (props.isExpanded) { return t("search.viewLess", { - name: props.name + name: useTranslationIfExists(props.name) }); } return t("search.viewMore", { - name: props.name + name: useTranslationIfExists(props.name) }); }; diff --git a/lib/ReactViews/Search/SearchHeader.tsx b/lib/ReactViews/Search/SearchHeader.tsx index 89a4202d251..ef74ed80732 100644 --- a/lib/ReactViews/Search/SearchHeader.tsx +++ b/lib/ReactViews/Search/SearchHeader.tsx @@ -1,11 +1,13 @@ import { observer } from "mobx-react"; import React from "react"; +import { useTranslationIfExists } from "../../Language/languageHelpers"; +import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; import { BoxSpan } from "../../Styled/Box"; import Text from "../../Styled/Text"; import Loader from "../Loader"; interface SearchHeaderProps { - searchResults: { [key: string]: any }; + searchResults: SearchProviderResults; isWaitingForSearchToStart: boolean; } @@ -20,7 +22,12 @@ const SearchHeader: React.FC = observer( } else if (props.searchResults.message) { return ( - {props.searchResults.message} + + {useTranslationIfExists( + props.searchResults.message.content, + props.searchResults.message.params + )} + ); } else { From c91a2403ed02d538c15e028b5b25159bd46109b7 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Fri, 3 Sep 2021 00:23:04 +0200 Subject: [PATCH 029/129] fix data catalog not showing search result messages --- lib/Models/SearchProviders/CatalogSearchProvider.ts | 10 ++++++++++ lib/ReactViews/DataCatalog/DataCatalog.jsx | 13 ++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index 7c02059e02b..9b9a550cf2d 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -7,6 +7,7 @@ import GroupMixin from "../../ModelMixins/GroupMixin"; import ReferenceMixin from "../../ModelMixins/ReferenceMixin"; import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProviderMixin"; import CatalogSearchProviderTraits from "../../Traits/SearchProviders/CatalogSearchProviderTraits"; +import CommonStrata from "../Definition/CommonStrata"; import CreateModel from "../Definition/CreateModel"; import Terria from "../Terria"; import SearchProviderResults from "./SearchProviderResults"; @@ -110,6 +111,15 @@ export default class CatalogSearchProvider extends SearchProviderMixin( @observable isSearching: boolean = false; @observable debounceDurationOnceLoaded: number = 300; + constructor(id: string | undefined, terria: Terria) { + super(id, terria); + this.setTrait( + CommonStrata.defaults, + "minCharacters", + terria.configParameters.searchBar!.minCharacters + ); + } + get type() { return CatalogSearchProvider.type; } diff --git a/lib/ReactViews/DataCatalog/DataCatalog.jsx b/lib/ReactViews/DataCatalog/DataCatalog.jsx index d4ffbe7a58d..68c9d55db11 100644 --- a/lib/ReactViews/DataCatalog/DataCatalog.jsx +++ b/lib/ReactViews/DataCatalog/DataCatalog.jsx @@ -1,17 +1,12 @@ -import React from "react"; -import { observer } from "mobx-react"; - import createReactClass from "create-react-class"; - +import { observer } from "mobx-react"; import PropTypes from "prop-types"; +import React from "react"; import { withTranslation } from "react-i18next"; - import defined from "terriajs-cesium/Source/Core/defined"; - -import DataCatalogMember from "./DataCatalogMember"; import SearchHeader from "../Search/SearchHeader"; - import Styles from "./data-catalog.scss"; +import DataCatalogMember from "./DataCatalogMember"; // Displays the data catalog. export const DataCatalog = observer( @@ -48,7 +43,7 @@ export const DataCatalog = observer( Date: Fri, 3 Sep 2021 00:28:13 +0200 Subject: [PATCH 030/129] fix search in data catalogue button --- lib/ReactViews/Search/SearchBoxAndResults.jsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/ReactViews/Search/SearchBoxAndResults.jsx b/lib/ReactViews/Search/SearchBoxAndResults.jsx index 89b58cc5758..82d12719296 100644 --- a/lib/ReactViews/Search/SearchBoxAndResults.jsx +++ b/lib/ReactViews/Search/SearchBoxAndResults.jsx @@ -2,19 +2,20 @@ import { reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import PropTypes from "prop-types"; import React from "react"; -import { Trans } from "react-i18next"; +import { useTranslation } from "react-i18next"; import styled from "styled-components"; import { addMarker, removeMarker } from "../../Models/LocationMarkerUtils"; -import SearchBox from "../Search/SearchBox"; -import LocationSearchResults from "../Search/LocationSearchResults"; -import Icon, { StyledIcon } from "../../Styled/Icon"; import Box from "../../Styled/Box"; import { RawButton } from "../../Styled/Button"; +import Icon, { StyledIcon } from "../../Styled/Icon"; import Spacing from "../../Styled/Spacing"; import Text from "../../Styled/Text"; +import LocationSearchResults from "../Search/LocationSearchResults"; +import SearchBox from "../Search/SearchBox"; export function SearchInDataCatalog({ viewState, handleClick }) { const locationSearchText = viewState.searchState.locationSearchText; + const { t } = useTranslation(); return ( - - Search {locationSearchText} in the Data Catalogue - + {t("search.searchInDataCatalog", { + locationSearchText: locationSearchText + })} From 4b6221a3309d9248d32fd863d2dbb38e19c2f8d9 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Fri, 3 Sep 2021 00:43:15 +0200 Subject: [PATCH 031/129] update translation file --- lib/Language/en/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Language/en/translation.json b/lib/Language/en/translation.json index 14c19281190..9398615b849 100644 --- a/lib/Language/en/translation.json +++ b/lib/Language/en/translation.json @@ -446,7 +446,7 @@ "resultsLabel": "Search Results", "done": "Done", "data": "Data", - "searchInDataCatalog": "Search <1>'{{locationSearchText}}' in the Data Catalogue", + "searchInDataCatalog": "Search '{{locationSearchText}}' in the Data Catalogue", "search": "Search '{{searchText}}' in the Data Catalogue", "viewLess": "View less {{name}} results", "viewMore": "View more {{name}} results", From cc6ec14aa17e47fc306b40da2f72ae6af61dc26b Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 15:41:21 +0200 Subject: [PATCH 032/129] update bingMapsSearchProvider warning when there is no key --- .../SearchProviders/BingMapsSearchProvider.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/Models/SearchProviders/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts index 764c3c16574..f626b11dac7 100644 --- a/lib/Models/SearchProviders/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -29,22 +29,24 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( constructor(uniqueId: string | undefined, terria: Terria) { super(uniqueId, terria); - if (!this.key && this.terria.configParameters.bingMapsKey) { - this.setTrait( - CommonStrata.defaults, - "key", - this.terria.configParameters.bingMapsKey - ); - } - this.showWarning(); + runInAction(() => { + if (!this.key && this.terria.configParameters.bingMapsKey) { + this.setTrait( + CommonStrata.defaults, + "key", + this.terria.configParameters.bingMapsKey + ); + } + this.showWarning(); + }); } showWarning() { if (!this.key || this.key === "") { console.warn( - "The " + - useTranslationIfExists(this.name) + - " 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." + `The ${useTranslationIfExists(this.name)}(${ + 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.` ); } } From ce884cd1ad5bbe1d76af922cdf58a2e22ced6dff Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 17:25:47 +0200 Subject: [PATCH 033/129] use mixTraits of directly extending Traits --- lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts | 2 +- lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts | 2 +- lib/Traits/SearchProviders/LocationSearchProviderTraits.ts | 7 +++++-- .../WebFeatureServiceSearchProviderTraits.ts | 5 ++++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts b/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts index 81649828868..d233d921f0d 100644 --- a/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts @@ -1,5 +1,5 @@ -import mixTraits from "../mixTraits"; import primitiveTrait from "../Decorators/primitiveTrait"; +import mixTraits from "../mixTraits"; import LocationSearchProviderTraits, { SearchProviderMapCenterTraits } from "./LocationSearchProviderTraits"; diff --git a/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts b/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts index 2dc2ff84927..bac19734806 100644 --- a/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/CatalogSearchProviderTraits.ts @@ -1,6 +1,6 @@ +import primitiveTrait from "../Decorators/primitiveTrait"; import mixTraits from "../mixTraits"; import SearchProviderTraits from "./SearchProviderTraits"; -import primitiveTrait from "../Decorators/primitiveTrait"; export default class CatalogSearchProviderTraits extends mixTraits( SearchProviderTraits diff --git a/lib/Traits/SearchProviders/LocationSearchProviderTraits.ts b/lib/Traits/SearchProviders/LocationSearchProviderTraits.ts index cb00c39d7c8..8c21cf5f93b 100644 --- a/lib/Traits/SearchProviders/LocationSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/LocationSearchProviderTraits.ts @@ -1,8 +1,11 @@ -import ModelTraits from "../ModelTraits"; import primitiveTrait from "../Decorators/primitiveTrait"; +import mixTraits from "../mixTraits"; +import ModelTraits from "../ModelTraits"; import SearchProviderTraits from "./SearchProviderTraits"; -export default class LocationSearchProviderTraits extends SearchProviderTraits { +export default class LocationSearchProviderTraits extends mixTraits( + SearchProviderTraits +) { @primitiveTrait({ type: "string", name: "URL", diff --git a/lib/Traits/SearchProviders/WebFeatureServiceSearchProviderTraits.ts b/lib/Traits/SearchProviders/WebFeatureServiceSearchProviderTraits.ts index 1be4884242e..636585348d3 100644 --- a/lib/Traits/SearchProviders/WebFeatureServiceSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/WebFeatureServiceSearchProviderTraits.ts @@ -1,7 +1,10 @@ import primitiveTrait from "../Decorators/primitiveTrait"; +import mixTraits from "../mixTraits"; import LocationSearchProviderTraits from "./LocationSearchProviderTraits"; -export default class WebFeatureServiceSearchProviderTraits extends LocationSearchProviderTraits { +export default class WebFeatureServiceSearchProviderTraits extends mixTraits( + LocationSearchProviderTraits +) { @primitiveTrait({ type: "string", name: "Search property name", From 9713dedd9199056e6bcef208ce558c9bc7a00f91 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 17:26:38 +0200 Subject: [PATCH 034/129] remove deprecationWarning function as it's not needed anymore --- lib/Core/deprecationWarning.ts | 94 ---------------------------------- 1 file changed, 94 deletions(-) delete mode 100644 lib/Core/deprecationWarning.ts diff --git a/lib/Core/deprecationWarning.ts b/lib/Core/deprecationWarning.ts deleted file mode 100644 index 9b57e432663..00000000000 --- a/lib/Core/deprecationWarning.ts +++ /dev/null @@ -1,94 +0,0 @@ -import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; -import defined from "terriajs-cesium/Source/Core/defined"; -import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; - -/** - * Port of Cesium's functions `deprecationWarning` and `oneTimeWarning`. - */ -const warnings: { [key: string]: boolean } = {}; - -/** - * Logs a one time message to the console. Use this function instead of - * console.log directly since this does not log duplicate messages - * unless it is called from multiple workers. - * - * @function oneTimeWarning - * - * @param {String} identifier The unique identifier for this warning. - * @param {String} [message=identifier] The message to log to the console. - * - * @example - * for(var i=0;i>includeStart('debug', pragmas.debug); - if (!defined(identifier)) { - throw new DeveloperError("identifier is required."); - } - //>>includeEnd('debug'); - - if (!defined(warnings[identifier])) { - warnings[identifier] = true; - console.warn(defaultValue(message, identifier)); - } -} - -/** - * Logs a deprecation message to the console. Use this function instead of - * console.log directly since this does not log duplicate messages - * unless it is called from multiple workers. - * - * @function deprecationWarning - * - * @param {String} identifier The unique identifier for this deprecated API. - * @param {String} message The message to log to the console. - * - * @example - * // Deprecated function or class - * function Foo() { - * deprecationWarning('Foo', 'Foo was deprecated in Cesium 1.01. It will be removed in 1.03. Use newFoo instead.'); - * // ... - * } - * - * // Deprecated function - * Bar.prototype.func = function() { - * deprecationWarning('Bar.func', 'Bar.func() was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newFunc() instead.'); - * // ... - * }; - * - * // Deprecated property - * Object.defineProperties(Bar.prototype, { - * prop : { - * get : function() { - * deprecationWarning('Bar.prop', 'Bar.prop was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newProp instead.'); - * // ... - * }, - * set : function(value) { - * deprecationWarning('Bar.prop', 'Bar.prop was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newProp instead.'); - * // ... - * } - * } - * }); - * - * @private - */ -function deprecationWarning(identifier: string, message: string) { - //>>includeStart('debug', pragmas.debug); - if (!defined(identifier) || !defined(message)) { - throw new DeveloperError("identifier and message are required."); - } - //>>includeEnd('debug'); - - oneTimeWarning(identifier, message); -} - -export default deprecationWarning; From b95cf473604f1c5280b8d0608967ac8003c0f8c1 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 19:32:13 +0200 Subject: [PATCH 035/129] add search bar model and make it use traits --- lib/Core/TerriaError.ts | 2 +- .../SearchProviders/SearchProviderMixin.ts | 2 +- .../WebFeatureServiceSearchProviderMixin.ts | 2 +- .../SearchProviders/CatalogSearchProvider.ts | 5 +- lib/Models/SearchProviders/SearchBarModel.ts | 79 ++++++++++ .../createStubSearchProvider.ts | 2 +- .../upsertSearchProviderFromJson.ts | 13 +- lib/Models/Terria.ts | 143 +++--------------- lib/ReactViews/Mobile/MobileHeader.jsx | 2 +- .../Search/LocationSearchResults.tsx | 27 +++- lib/ReactViews/SidePanel/SidePanel.jsx | 13 +- lib/Traits/SearchProviders/SearchBarTraits.ts | 44 ++++++ 12 files changed, 189 insertions(+), 145 deletions(-) create mode 100644 lib/Models/SearchProviders/SearchBarModel.ts create mode 100644 lib/Traits/SearchProviders/SearchBarTraits.ts diff --git a/lib/Core/TerriaError.ts b/lib/Core/TerriaError.ts index e3dcbd17f12..eb8f1009e00 100644 --- a/lib/Core/TerriaError.ts +++ b/lib/Core/TerriaError.ts @@ -218,7 +218,7 @@ export default class TerriaError { // shouldRaiseToUser will be true if at least one error includes shouldRaiseToUser = true const shouldRaiseToUser = filteredErrors - .map(error => error._shouldRaiseToUser ?? false) + .map(error => error.shouldRaiseToUser ?? false) .includes(true); return new TerriaError({ diff --git a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts index c4c4c015517..36107923d16 100644 --- a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts @@ -48,7 +48,7 @@ function SearchProviderMixin>( (this.minCharacters && searchText.length < this.minCharacters) || (this.minCharacters === undefined && searchText.length < - this.terria.configParameters.searchBar!.minCharacters) + this.terria.configParameters.searchBarModel!.minCharacters) ) { return false; } diff --git a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index 3fa2a49af40..b7c6aa9c600 100644 --- a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -222,7 +222,7 @@ function createZoomToFunction( const flightDurationSeconds: number = model.flightDurationSeconds || - model.terria.configParameters.searchBar!.flightDurationSeconds; + model.terria.configParameters.searchBarModel!.flightDurationSeconds; return function() { model.terria.currentViewer.zoomTo(rectangle, flightDurationSeconds); diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index 81c83c3d34c..b23c93a5d98 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -1,9 +1,8 @@ -import { autorun, computed, observable, runInAction } from "mobx"; +import { autorun, observable, runInAction } from "mobx"; import { Category, SearchAction } from "../../Core/AnalyticEvents/analyticEvents"; -import isDefined from "../../Core/isDefined"; import { TerriaErrorSeverity } from "../../Core/TerriaError"; import GroupMixin from "../../ModelMixins/GroupMixin"; import ReferenceMixin from "../../ModelMixins/ReferenceMixin"; @@ -119,7 +118,7 @@ export default class CatalogSearchProvider extends SearchProviderMixin( this.setTrait( CommonStrata.defaults, "minCharacters", - terria.configParameters.searchBar!.minCharacters + terria.configParameters.searchBarModel!.minCharacters ); } diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts new file mode 100644 index 00000000000..5ba225bce88 --- /dev/null +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -0,0 +1,79 @@ +import { action, isObservableArray, observable } from "mobx"; +import { DeveloperError } from "terriajs-cesium"; +import Result from "../../Core/Result"; +import TerriaError from "../../Core/TerriaError"; +import { SearchBarTraits } from "../../Traits/SearchProviders/SearchBarTraits"; +import CommonStrata from "../Definition/CommonStrata"; +import CreateModel from "../Definition/CreateModel"; +import { BaseModel } from "../Definition/Model"; +import Terria from "../Terria"; +import SearchProviderFactory from "./SearchProviderFactory"; +import upsertSearchProviderFromJson from "./upsertSearchProviderFromJson"; + +export class SearchBarModel extends CreateModel(SearchBarTraits) { + private locationSearchProviders = observable.map(); + + constructor(readonly terria: Terria) { + super("search-bar-model", terria); + } + + initializeSearchProviders() { + const errors: TerriaError[] = []; + + const searchProviders = this.terria.configParameters.searchProviders; + + if (!isObservableArray(searchProviders)) { + errors.push( + new TerriaError({ + sender: SearchProviderFactory, + title: "SearchProviders", + message: { key: "searchProvider.noSearchProviders" } + }) + ); + } + searchProviders?.forEach(searchProvider => { + upsertSearchProviderFromJson( + SearchProviderFactory, + this.terria, + CommonStrata.definition, + searchProvider + ).pushErrorTo(errors); + }); + + return new Result( + undefined, + TerriaError.combine( + errors, + "An error occurred while loading search providers" + ) + ); + } + + /** + * Add new SearchProvider to the list of SearchProviders. + */ + @action + addSearchProvider(model: BaseModel) { + if (model.uniqueId === undefined) { + throw new DeveloperError( + "A SearchProvider without a `uniqueId` cannot be added." + ); + } + + if (this.locationSearchProviders.has(model.uniqueId)) { + console.log( + new DeveloperError( + "A SearchProvider with the specified ID already exists." + ) + ); + } + + this.locationSearchProviders.set(model.uniqueId, model); + } + + get locationSearchProvidersArray() { + return [...this.locationSearchProviders.entries()].map(function(entry) { + return entry[1]; + }); + } +} diff --git a/lib/Models/SearchProviders/createStubSearchProvider.ts b/lib/Models/SearchProviders/createStubSearchProvider.ts index e72e219326f..e7098bad3fb 100644 --- a/lib/Models/SearchProviders/createStubSearchProvider.ts +++ b/lib/Models/SearchProviders/createStubSearchProvider.ts @@ -22,6 +22,6 @@ export default function createStubSearchProvider( const stub = new StubSearchProvider(idToUse, terria); stub.setTrait(CommonStrata.underride, "name", stub.uniqueId); - terria.addSearchProvider(stub); + terria.configParameters.searchBarModel?.addSearchProvider(stub); return stub; } diff --git a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index f583b591a95..4f669113fb8 100644 --- a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -1,6 +1,7 @@ import i18next from "i18next"; import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; +import { useTranslationIfExists } from "../../Language/languageHelpers"; import CommonStrata from "../Definition/CommonStrata"; import { BaseModel } from "../Definition/Model"; import ModelFactory from "../Definition/ModelFactory"; @@ -52,7 +53,7 @@ export default function upsertSearchProviderFromJson( if (model.type !== StubSearchProvider.type) { try { - model.terria.addSearchProvider(model); + model.terria.configParameters.searchBarModel?.addSearchProvider(model); } catch (error) { errors.push(error); } @@ -70,7 +71,9 @@ export default function upsertSearchProviderFromJson( model, TerriaError.combine( errors, - `Error upserting search provider JSON: \`${uniqueId}\`` + `Error upserting search provider JSON: \`${useTranslationIfExists( + uniqueId + )}\`` ) ); } @@ -81,18 +84,18 @@ function setDefaultTraits(model: BaseModel) { model.setTrait( CommonStrata.defaults, "flightDurationSeconds", - terria.configParameters.searchBar!.flightDurationSeconds + terria.configParameters.searchBarModel?.flightDurationSeconds ); model.setTrait( CommonStrata.defaults, "minCharacters", - terria.configParameters.searchBar!.minCharacters + terria.configParameters.searchBarModel?.minCharacters ); model.setTrait( CommonStrata.defaults, "recommendedListLength", - terria.configParameters.searchBar!.recommendedListLength + terria.configParameters.searchBarModel?.recommendedListLength ); } diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 245ab5532bc..7ed0426eccc 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -1,14 +1,6 @@ import { Share } from "catalog-converter"; import i18next from "i18next"; -import { - action, - computed, - isObservableArray, - observable, - runInAction, - toJS, - when -} from "mobx"; +import { action, computed, observable, runInAction, toJS, when } from "mobx"; import { createTransformer } from "mobx-utils"; import Clock from "terriajs-cesium/Source/Core/Clock"; import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; @@ -101,9 +93,8 @@ import Internationalization, { } from "./Internationalization"; import MapInteractionMode from "./MapInteractionMode"; import NoViewer from "./NoViewer"; -import SearchProviderFactory from "./SearchProviders/SearchProviderFactory"; -import upsertSearchProviderFromJson from "./SearchProviders/upsertSearchProviderFromJson"; import CatalogIndex from "./SearchProviders/CatalogIndex"; +import { SearchBarModel } from "./SearchProviders/SearchBarModel"; import ShareDataService from "./ShareDataService"; import TimelineStack from "./TimelineStack"; import ViewerMode from "./ViewerMode"; @@ -292,36 +283,7 @@ interface ConfigParameters { /** * The search bar allows requesting information from various search services at once. */ - searchBar?: SearchBar; -} - -interface SearchBar { - /** - * Input text field placeholder shown when no input has been given yet. The string is translateable. - * @default "translate#search.placeholder" - */ - placeholder: string; - /** - * Maximum amount of entries in the suggestion list. - * @default 5 - */ - recommendedListLength: number; - /** - * The duration of the camera flight to an entered location, in seconds. - * @default 1.5 - */ - flightDurationSeconds: number; - /** - * Minimum number of characters to start search. - */ - minCharacters: number; - /** - * Bounding box limits for the search results. - */ - boundingBoxLimit?: number[]; - /** - * Array of search providers to be used. - */ + searchBarModel?: SearchBarModel; searchProviders: any[]; } @@ -379,7 +341,7 @@ interface HomeCameraInit { export default class Terria { private readonly models = observable.map(); - private locationSearchProviders = observable.map(); + private searchProviders: any[] = []; /** Map from share key -> id */ readonly shareKeysMap = observable.map(); /** Map from id -> share keys */ @@ -499,13 +461,8 @@ export default class Terria { }, { text: "map.extraCreditLinks.disclaimer", url: "about.html#disclaimer" } ], - searchBar: { - placeholder: "translate#search.placeholder", - recommendedListLength: 5, - flightDurationSeconds: 1.5, - minCharacters: 3, - searchProviders: [] - } + searchBarModel: new SearchBarModel(this), + searchProviders: [] }; @observable @@ -702,34 +659,6 @@ export default class Terria { shareKeys?.forEach(shareKey => this.addShareKey(model.uniqueId!, shareKey)); } - /** - * Add new SearchProvider to the list of SearchProviders. - */ - @action - addSearchProvider(model: BaseModel) { - if (model.uniqueId === undefined) { - throw new DeveloperError( - "A SearchProvider without a `uniqueId` cannot be added." - ); - } - - if (this.locationSearchProviders.has(model.uniqueId)) { - console.log( - new DeveloperError( - "A SearchProvider with the specified ID already exists." - ) - ); - } - - this.locationSearchProviders.set(model.uniqueId, model); - } - - get locationSearchProvidersArray() { - return [...this.locationSearchProviders.entries()].map(function(entry) { - return entry[1]; - }); - } - /** * Remove references to a model from Terria. */ @@ -887,6 +816,8 @@ export default class Terria { if (isJsonObject(config) && isJsonObject(config.parameters)) { this.updateParameters(config.parameters); } + if (isJsonObject(config) && Array.isArray(config.searchProviders)) { + } if (this.configParameters.errorService) { this.setupErrorServiceProvider(this.configParameters.errorService); } @@ -934,11 +865,13 @@ export default class Terria { ) ); - this.initializeSearchProviders().catchError(error => - this.raiseErrorToUser( - TerriaError.from(error, "Failed to initialize searchProviders") - ) - ); + this.configParameters.searchBarModel + ?.initializeSearchProviders() + .catchError(error => + this.raiseErrorToUser( + TerriaError.from(error, "Failed to initialize searchProviders") + ) + ); if (options.applicationUrl) { (await this.updateApplicationUrl(options.applicationUrl.href)).raiseError( @@ -975,36 +908,6 @@ export default class Terria { } } - initializeSearchProviders() { - const errors: TerriaError[] = []; - let searchProviders = this.configParameters.searchBar?.searchProviders; - if (!isObservableArray(searchProviders)) { - errors.push( - new TerriaError({ - sender: SearchProviderFactory, - title: "SearchProviders", - message: { key: "searchProvider.noSearchProviders" } - }) - ); - } - searchProviders?.forEach(searchProvider => { - upsertSearchProviderFromJson( - SearchProviderFactory, - this, - CommonStrata.definition, - searchProvider - ).pushErrorTo(errors); - }); - - return new Result( - undefined, - TerriaError.combine( - errors, - "An error occurred while loading search providers" - ) - ); - } - async loadPersistedOrInitBaseMap() { const baseMapItems = this.baseMapsModel.baseMapItems; // Set baseMap fallback to first option @@ -1095,13 +998,15 @@ export default class Terria { updateParameters(parameters: ConfigParameters | JsonObject): void { Object.entries(parameters).forEach(([key, value]) => { if (this.configParameters.hasOwnProperty(key)) { - if (key === "searchBar") { - // merge default and new - //@ts-ignore - this.configParameters[key] = { - ...this.configParameters[key], - ...value - }; + if (key === "searchBarModel") { + if (!isDefined(this.configParameters.searchBarModel)) { + this.configParameters.searchBarModel = new SearchBarModel(this); + } + updateModelFromJson( + this.configParameters.searchBarModel!, + CommonStrata.definition, + value + ); } else { (this.configParameters as any)[key] = value; } diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index c0becf1bb4c..37edae88734 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -245,7 +245,7 @@ const MobileHeader = observer( onSearchTextChanged={this.changeLocationSearchText} onDoSearch={this.searchLocations} placeholder={useTranslationIfExists( - terria.configParameters.searchBar.placeholder + terria.configParameters.searchBarModel.placeholder )} alwaysShowClear={true} onClear={this.closeLocationSearch} diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 4c70a0a9d5a..c51a240aa77 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -62,14 +62,29 @@ class LocationSearchResults extends React.Component { get validResults() { const { search, terria } = this.props; const locationSearchBoundingBox = - terria.configParameters.searchBar?.boundingBoxLimit; - const validResults = isDefined(locationSearchBoundingBox) + terria.configParameters.searchBarModel?.boundingBoxLimit; + let filterResults = false; + let west: number | undefined, + east: number | undefined, + south: number | undefined, + north: number | undefined; + if (locationSearchBoundingBox) { + ({ west, east, south, north } = locationSearchBoundingBox); + + filterResults = + isDefined(west) && + isDefined(east) && + isDefined(south) && + isDefined(north); + } + + const validResults = filterResults ? search.results.filter(function(r: any) { return ( - r.location.longitude > locationSearchBoundingBox[0] && - r.location.longitude < locationSearchBoundingBox[2] && - r.location.latitude > locationSearchBoundingBox[1] && - r.location.latitude < locationSearchBoundingBox[3] + r.location.longitude > west! && + r.location.longitude < east! && + r.location.latitude > south! && + r.location.latitude < north! ); }) : search.results; diff --git a/lib/ReactViews/SidePanel/SidePanel.jsx b/lib/ReactViews/SidePanel/SidePanel.jsx index 56752e409e7..8de9a888293 100644 --- a/lib/ReactViews/SidePanel/SidePanel.jsx +++ b/lib/ReactViews/SidePanel/SidePanel.jsx @@ -5,17 +5,16 @@ import React from "react"; import { withTranslation } from "react-i18next"; import styled, { withTheme } from "styled-components"; import { useTranslationIfExists } from "../../Language/languageHelpers"; -import { useRefForTerria } from "../Hooks/useRefForTerria"; +import Box from "../../Styled/Box"; +import Button from "../../Styled/Button"; import Icon, { StyledIcon } from "../../Styled/Icon"; +import Spacing from "../../Styled/Spacing"; +import Text from "../../Styled/Text"; +import { useRefForTerria } from "../Hooks/useRefForTerria"; import SearchBoxAndResults from "../Search/SearchBoxAndResults"; import Workbench from "../Workbench/Workbench"; import FullScreenButton from "./FullScreenButton"; -import Box from "../../Styled/Box"; -import Spacing from "../../Styled/Spacing"; -import Text from "../../Styled/Text"; -import Button from "../../Styled/Button"; - const BoxHelpfulHints = styled(Box)``; const ResponsiveSpacing = styled(Box)` @@ -172,7 +171,7 @@ const SidePanel = observer( viewState={this.props.viewState} terria={this.props.terria} placeholder={useTranslationIfExists( - this.props.terria.configParameters.searchBar.placeholder + this.props.terria.configParameters.searchBarModel.placeholder )} /> diff --git a/lib/Traits/SearchProviders/SearchBarTraits.ts b/lib/Traits/SearchProviders/SearchBarTraits.ts new file mode 100644 index 00000000000..e54e2b53e67 --- /dev/null +++ b/lib/Traits/SearchProviders/SearchBarTraits.ts @@ -0,0 +1,44 @@ +import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; +import objectTrait from "../Decorators/objectTrait"; +import primitiveTrait from "../Decorators/primitiveTrait"; +import ModelTraits from "../ModelTraits"; +import { RectangleTraits } from "../TraitsClasses/MappableTraits"; + +export class SearchBarTraits extends ModelTraits { + @primitiveTrait({ + type: "string", + name: "placeholder", + description: + "Input text field placeholder shown when no input has been given yet. The string is translateable." + }) + placeholder: string = "translate#search.placeholder"; + + @primitiveTrait({ + type: "number", + name: "Recommended list length", + description: "Maximum amount of entries in the suggestion list." + }) + recommendedListLength: number = 5; + + @primitiveTrait({ + type: "number", + name: "Flight duration seconds", + description: + "The duration of the camera flight to an entered location, in seconds." + }) + flightDurationSeconds: number = 1.5; + + @primitiveTrait({ + type: "number", + name: "Minimum characters", + description: "Minimum number of characters required for search to start" + }) + minCharacters: number = 3; + + @objectTrait({ + type: RectangleTraits, + name: "Minimum characters", + description: "Minimum number of characters required for search to start" + }) + boundingBoxLimit?: RectangleTraits = Rectangle.MAX_VALUE; +} From 5bb733efa74a896afc77914f88c58cc3b8f567d5 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 19:49:58 +0200 Subject: [PATCH 036/129] update docs --- doc/customizing/client-side-config.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/customizing/client-side-config.md b/doc/customizing/client-side-config.md index d3b4c921406..1013fbb661d 100644 --- a/doc/customizing/client-side-config.md +++ b/doc/customizing/client-side-config.md @@ -89,6 +89,8 @@ Specifies various options for configuring TerriaJS: |feedbackPreamble|no|**string**|feedback.feedbackPreamble|Text showing at the top of feedback form, supports the internationalization using the translation key.| |feedbackMinLength|no|**number**|0|Minimum length of feedback comment.| |`theme`|no|**any**|`{}`|An object used to override theme properties - for example `{"logoHeight": "70px"}`.| +|`searchBar`|no|**[SearchBar](#searchbar)**|`new SearchBar()`|Search bar configuration| +|`searchProviders`|no|**[SearchProviders](#searchbarproviders)|`[]`|Search providers that will be used for search| ### MagdaReferenceHeaders @@ -149,6 +151,7 @@ Configuration of items to appear in the search bar *** ### ErrorServiceOptions + |Name|Required|Type|Default|Description| |----|--------|----|-------|-----------| |provider|yes|**string**|`undefined`|A string identifying the error service provider to use. Currently only `rollbar` is supported.| @@ -203,3 +206,16 @@ This file will have to be re-generated manually every time the catalog structure - if items are renamed, or moved - dynamic groups are updated (for example, WMS server publishes new layers) + +### SearchBar + +Configuration for the search bar. Some of the values will be used as default for +search provider values. + +|Name|Required|Type|Default|Description| +|----|--------|----|-------|-----------| +|placeholder|no|**string**|`translate#search.placeholder`|Input text field placeholder shown when no input has been given yet. The string is translateable.| +|recommendedListLength|no|**number**|`5`|Maximum amount of entries in the suggestion list.| +|flightDurationSeconds|no|**number**|`1.5`|The duration of the camera flight to an entered location, in seconds.| +|minCharacters|no|**number**|3|Minimum number of characters required for search to start| +|boundingBoxLimit|no|**Rectangle**|`Cesium.Rectangle.MAX_VALUE`|Bounding box limits for the search results {west, south, east, north}| From 1523c81ede94b5e4cbe757b6de218a4467e7b423 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 19:50:44 +0200 Subject: [PATCH 037/129] update trait description --- lib/Traits/SearchProviders/SearchBarTraits.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Traits/SearchProviders/SearchBarTraits.ts b/lib/Traits/SearchProviders/SearchBarTraits.ts index e54e2b53e67..85c9bbb2da3 100644 --- a/lib/Traits/SearchProviders/SearchBarTraits.ts +++ b/lib/Traits/SearchProviders/SearchBarTraits.ts @@ -37,8 +37,9 @@ export class SearchBarTraits extends ModelTraits { @objectTrait({ type: RectangleTraits, - name: "Minimum characters", - description: "Minimum number of characters required for search to start" + name: "Bounding box limit", + description: + "Bounding box limits for the search results {west, south, east, north}" }) boundingBoxLimit?: RectangleTraits = Rectangle.MAX_VALUE; } From f424f70bf2494b7fc6e7558f54b697d0b9304d36 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 21:54:53 +0200 Subject: [PATCH 038/129] fix import from terriajs-cesium --- lib/Models/SearchProviders/SearchBarModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts index 5ba225bce88..8f4ebec25bc 100644 --- a/lib/Models/SearchProviders/SearchBarModel.ts +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -1,5 +1,5 @@ import { action, isObservableArray, observable } from "mobx"; -import { DeveloperError } from "terriajs-cesium"; +import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; import { SearchBarTraits } from "../../Traits/SearchProviders/SearchBarTraits"; From cc7fabdb09b85c5f395ce0e4a9946573e9f90508 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 17 Oct 2021 23:48:47 +0200 Subject: [PATCH 039/129] add docs --- doc/customizing/client-side-config.md | 29 +----- doc/customizing/search-providers.md | 127 ++++++++++++++++++++++++++ doc/mkdocs.yml | 1 + 3 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 doc/customizing/search-providers.md diff --git a/doc/customizing/client-side-config.md b/doc/customizing/client-side-config.md index 1013fbb661d..4d141dc934f 100644 --- a/doc/customizing/client-side-config.md +++ b/doc/customizing/client-side-config.md @@ -46,7 +46,7 @@ Specifies various options for configuring TerriaJS: |`supportEmail`|no|**string**|`"info@terria.io"`|The email address shown when things go wrong.| |`defaultMaximumShownFeatureInfos`|no|**number**|`100`|The maximum number of "feature info" boxes that can be displayed when clicking a point.| |`regionMappingDefinitionsUrl`|yes|**string**|`"build/TerriaJS/data/regionMapping.json"`|URL of the JSON file that defines region mapping for CSV files. This option only needs to be changed in unusual deployments. It has to be changed if deploying as static site, for instance.| -|`catalogIndexUrl`|no|**string**||URL of the JSON file that contains index of catalog. See [CatalogIndex](#catalogindex)| +|`catalogIndexUrl`|no|**string**||URL of the JSON file that contains index of catalog. See [CatalogIndex](search-providers.md#catalogindex)| |`conversionServiceBaseUrl`|no|**string**|`"convert/"`|URL of OGR2OGR conversion service (part of TerriaJS-Server). This option only needs to be changed in unusual deployments. It has to be changed if deploying as static site, for instance.| |`proj4ServiceBaseUrl`|no|**string**|`"proj4def/"`|URL of Proj4 projection lookup service (part of TerriaJS-Server). This option only needs to be changed in unusual deployments. It has to be changed if deploying as static site, for instance.| |`corsProxyBaseUrl`|no|**string**|`"proxy/"`|URL of CORS proxy service (part of TerriaJS-Server). This option only needs to be changed in unusual deployments. It has to be changed if deploying as static site, for instance.| @@ -90,7 +90,7 @@ Specifies various options for configuring TerriaJS: |feedbackMinLength|no|**number**|0|Minimum length of feedback comment.| |`theme`|no|**any**|`{}`|An object used to override theme properties - for example `{"logoHeight": "70px"}`.| |`searchBar`|no|**[SearchBar](#searchbar)**|`new SearchBar()`|Search bar configuration| -|`searchProviders`|no|**[SearchProviders](#searchbarproviders)|`[]`|Search providers that will be used for search| +|`searchProviders`|no|**[SearchProviders](search-providers.md)|`[]`|Search providers that will be used for search| ### MagdaReferenceHeaders @@ -182,31 +182,6 @@ Configuration of items to appear in the search bar *** -### CatalogIndex - -If your TerriaMap has many (>50) dynamic groups (groups which need to be loaded - for example CKAN, WMS-group...) it may be worth generating a static catalog index JSON file. This file will contain ID, name and description fields of all catalog items, which can be used to search through the catalog very quickly without needing to load dynamic groups. - -The https://github.com/nextapps-de/flexsearch library is used to index and search the catalog index file. - -**Note** NodeJS v10 is not supported, please use v12 or v14. - -To generate the catalog index: - -- `npm run build-tools` -- `node .\build\generateCatalogIndex.js config-url base-url` where - - `config-url` is URL to client-side-config file - - `base-url` is URL to terriajs-server (this is used to load `server-config` and to proxy requests) - - For example `node .\build\generateCatalogIndex.js http://localhost:3001/config.json http://localhost:3001` -- This will output two files - - `catalog-index.json` - - `catalog-index-errors.json` with any error messages which occurred while loading catalog members -- Set `catalogIndexUrl` config parameter - -This file will have to be re-generated manually every time the catalog structure changes - for example: - -- if items are renamed, or moved -- dynamic groups are updated (for example, WMS server publishes new layers) - ### SearchBar Configuration for the search bar. Some of the values will be used as default for diff --git a/doc/customizing/search-providers.md b/doc/customizing/search-providers.md new file mode 100644 index 00000000000..f2d9fa6935e --- /dev/null +++ b/doc/customizing/search-providers.md @@ -0,0 +1,127 @@ +# Search providers + +Terriajs supports 2 types of search providers + +1. Catalog search provider +2. Location search providers + +Each search provider can be configured using following options + +|Name|Required|Type|Default|Description| +|----|--------|----|-------|-----------| +|name|no|**string**|`unknown`|Name of the search provider.| +|minCharacters|no|**number**|`catalogParameters.searchBar.minCharacters`|Minimum number of characters required for search to start.| + +## Catalog search provider + +`type: catalog-search-provider` + +Catalog search provider is used to find the desired dataset. Catalog search provider can be used with or without static catalog index JSON file. Without catalog index each catalog group and item will be dynamically fetched from remote servers in the moment of the search, and for bigger catalog this will cause poor performance of search. For example when having WMS-group in catalog searching in that catalog will cause catalog to issue `getCapabilities` request, wait for response and then perform the search. TerriaJS supports only search provider of type `catalog-search-provider` + +### CatalogIndex + +If your TerriaMap has many (>50) dynamic groups (groups which need to be loaded - for example CKAN, WMS-group...) it may be worth generating a static catalog index JSON file. This file will contain ID, name and description fields of all catalog items, which can be used to search through the catalog very quickly without needing to load dynamic groups. + +The [flexsearch](https://github.com/nextapps-de/flexsearch) library is used to index and search the catalog index file. + +**Note** NodeJS v10 is not supported, please use v12 or v14. + +To generate the catalog index: + +- `npm run build-tools` +- `node .\build\generateCatalogIndex.js config-url base-url` where + - `config-url` is URL to client-side-config file + - `base-url` is URL to terriajs-server (this is used to load `server-config` and to proxy requests) + - For example `node .\build\generateCatalogIndex.js http://localhost:3001/config.json http://localhost:3001` +- This will output two files + - `catalog-index.json` + - `catalog-index-errors.json` with any error messages which occurred while loading catalog members +- Set `catalogIndexUrl` config parameter + +This file will have to be re-generated manually every time the catalog structure changes - for example: + +- if items are renamed, or moved +- dynamic groups are updated (for example, WMS server publishes new layers) + +## Location search providers + +Location search providers are used to search for locations on the map. TerriaJS currently supports two implementations of search providers: + +- [`BingMapsSearchProvider`](#bingmapssearchprovider) - implementation which in background uses Bing Map search API +- [`AustralianGazetteerSearchProvider`](#australiangazetteersearchprovider) - uses `WebFeatureServiceSearchProvider` + +Each `LocationSearchProvider support following confing options + +|Name|Required|Type|Default|Description| +|----|--------|----|-------|-----------| +|url|yes|**string**|`""`|The URL of search provider.| +|recommendedListLength|no|**number**|`5`|Default amount of entries in the suggestion list.| +|flightDurationSeconds|no|**number**|`1.5`|Time to move to the result location.| +|isOpen|no|**boolean**|`true`|True if the search results of this search provider are visible by default; otherwise, false (user manually open search results).| + +### BingMapsSearchProvider + +`type: bing-maps-search-provider` + +Bing maps search provider is based on commercial API which is provided by BingMaps. To enable it, it is necessary to add an apropriate Bing Maps API key as config parameter. This search provider as results returns addresses and a place name locations. + +|Name|Required|Type|Default|Description| +|----|--------|----|-------|-----------| +|`key`|no|**string**|`configParameters.bingMapsKey`|The Bing Maps key.| +|primaryCountry|no|**string**|`Australia`|Name of the country to prioritize the search results.| +|`culture`|no|**string**|`en-au`|Use the culture parameter to specify a culture for your request.The culture parameter provides the result in the language of the culture. For a list of supported cultures, see [Supported Culture Codes](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes)| +|`mapCenter`|no|**boolean**|`true`|Whether the current location of the map center is supplied with search request| + +It provides a default value for `url: https://dev.virtualearth.net/` + +**Example** + +```json +{ + "id": "search-provider/bing-maps", + "type": "bing-maps-search-provider", + "name": "translate#viewModels.searchLocations", + "url": "https://dev.virtualearth.net/", + "flightDurationSeconds": 1.5, + "minCharacters": 5, + "isOpen": true +}, +``` + +### AustralianGazetteerSearchProvider + +`type: australian-gazetteer-search-provider` + +Australian gazzetteer search provider is based on web feature service that uses an official place names of Australia. It is based on `WebFeatureServiceProvider`. +It can be configured using following options + +|Name|Required|Type|Default|Description| +|----|--------|----|-------|-----------| +|`searchPropertyName`|yes|**string**|`undefined`|Which property to look for the search text in| +|`searchPropertyTypeName`|yes|**string**|`undefined`|Type of the properties to search| + +**Example** + +```json +{ + "id": "search-provider/australian-gazetteer", + "type": "australian-gazetteer-search-provider", + "name": "translate#viewModels.searchPlaceNames", + "url": "http://services.ga.gov.au/gis/services/Australian_Gazetteer/MapServer/WFSServer", + "searchPropertyName": "Australian_Gazetteer:NameU", + "searchPropertyTypeName": "Australian_Gazetteer:Gazetteer_of_Australia", + "flightDurationSeconds": 1.5, + "minCharacters": 3, + "recommendedListLength": 3, + "isOpen": false +} +``` + +### Implementing new location search provider + +Implementing new location search provider is similar to implementing new `CatalogItems` and `CatalogGroups`. Each of them should be based on the usage of one of the mixins + +- `LocationSearchProviderMixin` - should be used for API based location search providers. Example of such search provider is `BingMapSearchProvider`. +- `WebFeatureServiceSearchProviderMixin` - should be used for location search providers that will rely on data provided by `WebFeatureService`. Example of such search provider is `AustralianGazetteerSearchProvider`. + +Each new `SearchProvider` should be registered inside `registerSearchProvider` so they can be properly upserted from json definition provider in config file. diff --git a/doc/mkdocs.yml b/doc/mkdocs.yml index 638656a8038..3a7e4b8d141 100644 --- a/doc/mkdocs.yml +++ b/doc/mkdocs.yml @@ -17,6 +17,7 @@ nav: - Server-side Config: customizing/server-side-config.md - Skinning: customizing/skinning.md - Translation guide: customizing/translation-guide.md + - Search Providers: customizing/search-providers.md - Connecting to Data: - Overview: connecting-to-data/README.md - Cross-Origin Resource Sharing: connecting-to-data/cross-origin-resource-sharing.md From 7ee2e3f86cb36b1afa464f9684afec2ba9ca08e3 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Thu, 7 Sep 2023 16:20:43 +1000 Subject: [PATCH 040/129] Fix missing wms `styles` parameter --- lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts index 3ce6fd42aa7..106ac1b1306 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts @@ -523,10 +523,9 @@ class WebMapServiceCatalogItem parameters.COLORSCALERANGE = this.colorScaleRange; } - if (isDefined(this.styles)) { - parameters.styles = this.styles; - getFeatureInfoParameters.styles = this.styles; - } + // Styles parameter is mandatory (for GetMap and GetFeatureInfo requests), but can be empty string to use default style + parameters.styles = this.styles ?? ""; + getFeatureInfoParameters.styles = this.styles ?? ""; Object.assign(parameters, diffModeParameters); From 873d73cfd162ee4f6825b07978fb2d16bee06953 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Tue, 12 Sep 2023 17:52:46 +1000 Subject: [PATCH 041/129] GetTimeSeries hack --- .../Catalog/Ows/WebMapServiceCatalogItem.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts index 3ce6fd42aa7..985e7eb497f 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts @@ -50,6 +50,8 @@ import WebMapServiceCapabilities from "./WebMapServiceCapabilities"; import WebMapServiceCapabilitiesStratum from "./WebMapServiceCapabilitiesStratum"; import WebMapServiceCatalogGroup from "./WebMapServiceCatalogGroup"; +import ImageryLayerFeatureInfo from "terriajs-cesium/Source/Scene/ImageryLayerFeatureInfo"; + /** This LoadableStratum is responsible for setting WMS version based on CatalogItem.url */ export class WebMapServiceUrlStratum extends LoadableStratum( WebMapServiceCatalogItemTraits @@ -512,7 +514,10 @@ class WebMapServiceCatalogItem this.terria.configParameters.defaultMaximumShownFeatureInfos), ...this.parameters, ...this.getFeatureInfoParameters, - ...dimensionParameters + ...dimensionParameters, + request: "GetTimeseries", + info_format: "image/png", + time: "" }; const diffModeParameters = this.isShowingDiff @@ -580,10 +585,13 @@ class WebMapServiceCatalogItem if (isDefined(this.getFeatureInfoFormat?.type)) { imageryOptions.getFeatureInfoFormats = [ - new GetFeatureInfoFormat( - this.getFeatureInfoFormat.type, - this.getFeatureInfoFormat.format - ) + new GetFeatureInfoFormat("image", "blob", (something: Blob) => { + const featureInfo = new ImageryLayerFeatureInfo(); + const text = `![GetFeatureInfo](${URL.createObjectURL(something)})`; + featureInfo.description = text; + featureInfo.data = text; + return [featureInfo]; + }) ]; } From a4464558aa965c174558c0f05efb1ae4c9742ae2 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Thu, 21 Sep 2023 22:22:38 +1000 Subject: [PATCH 042/129] GetTimeseries working --- CHANGES.md | 1 + .../Ows/WebMapServiceCapabilitiesStratum.ts | 60 +++++++-- .../Catalog/Ows/WebMapServiceCatalogItem.ts | 42 +++--- lib/Models/Feature/Feature.ts | 4 +- .../FeatureInfo/FeatureInfoSection.tsx | 11 +- lib/Table/tableFeatureInfoContext.ts | 127 +++++++++++++----- .../WebMapServiceCatalogItemTraits.ts | 12 +- .../Ows/WebMapServiceCatalogItemSpec.ts | 79 +++++++++++ 8 files changed, 266 insertions(+), 70 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ecd634655e3..b5d9cc2be29 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ #### next release (8.3.5) - Add `includeMembersRegex` to `GroupTraits`. This can be used to filter group members by id/name using a regular expression. +- Add `GetTimeseries` support to `WebMapServiceCatalogItem`. This adds a new `supportsGetTimeseries` trait, which when true will replace `GetFeatureInfo` with `GetTimeseries` requests. It will also change `info_format` to `text/csv`, and show a chart in the feature info panel. Servers which advertise `GetTimeseries` capability will have this trait set to true by default. `GetTimeseries` requests will have `time = ""`. - [The next improvement] #### 8.3.4 - 2023-09-15 diff --git a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts index 8cb7bff5b01..5fcdf4fdc5e 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts @@ -3,13 +3,13 @@ import { computed, makeObservable } from "mobx"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import URI from "urijs"; +import { JsonObject, isJsonArray, isJsonString } from "../../../Core/Json"; +import TerriaError from "../../../Core/TerriaError"; import containsAny from "../../../Core/containsAny"; import createDiscreteTimesFromIsoSegments from "../../../Core/createDiscreteTimes"; import filterOutUndefined from "../../../Core/filterOutUndefined"; import isDefined from "../../../Core/isDefined"; import isReadOnlyArray from "../../../Core/isReadOnlyArray"; -import { isJsonArray, isJsonString, JsonObject } from "../../../Core/Json"; -import TerriaError from "../../../Core/TerriaError"; import { terriaTheme } from "../../../ReactViews/StandardUserInterface/StandardTheme"; import { InfoSectionTraits, @@ -19,6 +19,7 @@ import { KeyValueTraits, WebCoverageServiceParameterTraits } from "../../../Traits/TraitsClasses/ExportWebCoverageServiceTraits"; +import { FeatureInfoTemplateTraits } from "../../../Traits/TraitsClasses/FeatureInfoTraits"; import LegendTraits from "../../../Traits/TraitsClasses/LegendTraits"; import { RectangleTraits } from "../../../Traits/TraitsClasses/MappableTraits"; import WebMapServiceCatalogItemTraits, { @@ -29,18 +30,18 @@ import WebMapServiceCatalogItemTraits, { WebMapServiceAvailableLayerStylesTraits, WebMapServiceAvailableStyleTraits } from "../../../Traits/TraitsClasses/WebMapServiceCatalogItemTraits"; -import createStratumInstance from "../../Definition/createStratumInstance"; import LoadableStratum from "../../Definition/LoadableStratum"; import Model, { BaseModel } from "../../Definition/Model"; import StratumFromTraits from "../../Definition/StratumFromTraits"; +import createStratumInstance from "../../Definition/createStratumInstance"; import proxyCatalogItemUrl from "../proxyCatalogItemUrl"; import { CapabilitiesStyle } from "./OwsInterfaces"; import WebMapServiceCapabilities, { CapabilitiesContactInformation, CapabilitiesDimension, CapabilitiesLayer, - getRectangleFromLayer, - MetadataURL + MetadataURL, + getRectangleFromLayer } from "./WebMapServiceCapabilities"; import WebMapServiceCatalogItem from "./WebMapServiceCatalogItem"; @@ -718,16 +719,33 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( @computed get supportsGetLegendGraphic(): boolean { + const capabilities = this.capabilities?.json?.Capability; + return ( isDefined(this.capabilities?.json?.["xmlns:sld"]) || - isDefined( - this.capabilities?.json?.Capability?.Request?.GetLegendGraphic - ) || + isDefined(capabilities?.Request?.GetLegendGraphic) || + (Array.isArray(capabilities?.ExtendedCapabilities?.ExtendedRequest) && + capabilities.ExtendedCapabilities.ExtendedRequest.find( + (r: JsonObject) => r?.Request === "GetLegendGraphic" + )) || (this.catalogItem.isGeoServer ?? false) || (this.catalogItem.isNcWMS ?? false) ); } + @computed + get supportsGetTimeseries() { + const capabilities = this.capabilities?.json?.Capability; + + return ( + isDefined(capabilities?.Request?.GetTimeseries) || + (Array.isArray(capabilities?.ExtendedCapabilities?.ExtendedRequest) && + capabilities.ExtendedCapabilities.ExtendedRequest.find( + (r: JsonObject) => r?.Request === "GetTimeseries" + )) + ); + } + @computed get supportsColorScaleRange(): boolean { return this.catalogItem.isNcWMS; @@ -837,6 +855,8 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( * - Plain text * * If no matching format can be found in GetCapabilities, then Cesium will use defaults (see `WebMapServiceImageryProvider.DefaultGetFeatureInfoFormats`) + * + * If supportsGetTimeseries, use CSV */ @computed get getFeatureInfoFormat(): | StratumFromTraits @@ -850,6 +870,10 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( ? [formats] : []; + if (this.catalogItem.supportsGetTimeseries) { + return { format: "text/csv", type: "text" }; + } + if (formatsArray.includes("application/json")) return { format: "application/json", type: "json" }; if (formatsArray.includes("text/html")) @@ -860,6 +884,26 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( return { format: "text/plain", type: "text" }; } + /** If supportsGetTimeseries, override the "request" parameter in GetFeatureInfo to be "GetTimeseries". + * We also set time to empty, so we get values for all times (as opposed to just the current time) + */ + @computed get getFeatureInfoParameters() { + if (this.catalogItem.supportsGetTimeseries) { + return { request: "GetTimeseries", time: "" }; + } + return undefined; + } + + /** If getFeatureInfoFormat is text/csv, set featureInfoTemplate to show chart. */ + @computed + get featureInfoTemplate() { + if (this.catalogItem.getFeatureInfoFormat.format === "text/csv") + return createStratumInstance(FeatureInfoTemplateTraits, { + template: `{{terria.timeSeries.chart}}`, + showFeatureInfoDownloadWithTemplate: true + }); + } + @computed get linkedWcsParameters() { // Get outputCrs // Note: this will be overridden by `WebCoverageServiceDescribeCoverageStratum` if a better outputCrs is found diff --git a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts index 985e7eb497f..ece1aa38884 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts @@ -8,18 +8,18 @@ // Solution: think in terms of pipelines with computed observables, document patterns. // 4. All code for all catalog item types needs to be loaded before we can do anything. import i18next from "i18next"; -import { computed, runInAction, makeObservable, override } from "mobx"; -import combine from "terriajs-cesium/Source/Core/combine"; +import { computed, makeObservable, override, runInAction } from "mobx"; import GeographicTilingScheme from "terriajs-cesium/Source/Core/GeographicTilingScheme"; import JulianDate from "terriajs-cesium/Source/Core/JulianDate"; import WebMercatorTilingScheme from "terriajs-cesium/Source/Core/WebMercatorTilingScheme"; +import combine from "terriajs-cesium/Source/Core/combine"; import GetFeatureInfoFormat from "terriajs-cesium/Source/Scene/GetFeatureInfoFormat"; import WebMapServiceImageryProvider from "terriajs-cesium/Source/Scene/WebMapServiceImageryProvider"; import URI from "urijs"; +import TerriaError from "../../../Core/TerriaError"; import createTransformerAllowUndefined from "../../../Core/createTransformerAllowUndefined"; import filterOutUndefined from "../../../Core/filterOutUndefined"; import isDefined from "../../../Core/isDefined"; -import TerriaError from "../../../Core/TerriaError"; import CatalogMemberMixin, { getName } from "../../../ModelMixins/CatalogMemberMixin"; @@ -32,6 +32,10 @@ import MappableMixin, { import MinMaxLevelMixin from "../../../ModelMixins/MinMaxLevelMixin"; import TileErrorHandlerMixin from "../../../ModelMixins/TileErrorHandlerMixin"; import UrlMixin from "../../../ModelMixins/UrlMixin"; +import { + TimeSeriesFeatureInfoContext, + csvFeatureInfoContext +} from "../../../Table/tableFeatureInfoContext"; import WebMapServiceCatalogItemTraits, { SUPPORTED_CRS_3857, SUPPORTED_CRS_4326 @@ -41,6 +45,7 @@ import CreateModel from "../../Definition/CreateModel"; import LoadableStratum from "../../Definition/LoadableStratum"; import { BaseModel } from "../../Definition/Model"; import StratumOrder from "../../Definition/StratumOrder"; +import FeatureInfoContext from "../../Feature/FeatureInfoContext"; import SelectableDimensions, { SelectableDimensionEnum } from "../../SelectableDimensions/SelectableDimensions"; @@ -49,8 +54,7 @@ import proxyCatalogItemUrl from "../proxyCatalogItemUrl"; import WebMapServiceCapabilities from "./WebMapServiceCapabilities"; import WebMapServiceCapabilitiesStratum from "./WebMapServiceCapabilitiesStratum"; import WebMapServiceCatalogGroup from "./WebMapServiceCatalogGroup"; - -import ImageryLayerFeatureInfo from "terriajs-cesium/Source/Scene/ImageryLayerFeatureInfo"; +import TerriaFeature from "../../Feature/Feature"; /** This LoadableStratum is responsible for setting WMS version based on CatalogItem.url */ export class WebMapServiceUrlStratum extends LoadableStratum( @@ -96,7 +100,7 @@ class WebMapServiceCatalogItem ) ) ) - implements SelectableDimensions + implements SelectableDimensions, FeatureInfoContext { /** * The collection of strings that indicate an Abstract property should be ignored. If these strings occur anywhere @@ -513,11 +517,9 @@ class WebMapServiceCatalogItem (this.maximumShownFeatureInfos ?? this.terria.configParameters.defaultMaximumShownFeatureInfos), ...this.parameters, - ...this.getFeatureInfoParameters, + // Note order is important here, as getFeatureInfoParameters may override `time` dimension value ...dimensionParameters, - request: "GetTimeseries", - info_format: "image/png", - time: "" + ...this.getFeatureInfoParameters }; const diffModeParameters = this.isShowingDiff @@ -585,13 +587,10 @@ class WebMapServiceCatalogItem if (isDefined(this.getFeatureInfoFormat?.type)) { imageryOptions.getFeatureInfoFormats = [ - new GetFeatureInfoFormat("image", "blob", (something: Blob) => { - const featureInfo = new ImageryLayerFeatureInfo(); - const text = `![GetFeatureInfo](${URL.createObjectURL(something)})`; - featureInfo.description = text; - featureInfo.data = text; - return [featureInfo]; - }) + new GetFeatureInfoFormat( + this.getFeatureInfoFormat.type, + this.getFeatureInfoFormat.format + ) ]; } @@ -750,6 +749,15 @@ class WebMapServiceCatalogItem ...this.styleSelectableDimensions ]); } + + /** If GetFeatureInfo/GetTimeseries request is returning CSV, we need to parse it into TimeSeriesFeatureInfoContext. + */ + @computed get featureInfoContext(): ( + feature: TerriaFeature + ) => TimeSeriesFeatureInfoContext { + if (this.getFeatureInfoFormat.format !== "text/csv") return () => ({}); + return csvFeatureInfoContext(this); + } } /** diff --git a/lib/Models/Feature/Feature.ts b/lib/Models/Feature/Feature.ts index a8f8c431d98..b8243baf511 100644 --- a/lib/Models/Feature/Feature.ts +++ b/lib/Models/Feature/Feature.ts @@ -6,7 +6,7 @@ import Cesium3DTileFeature from "terriajs-cesium/Source/Scene/Cesium3DTileFeatur import Cesium3DTilePointFeature from "terriajs-cesium/Source/Scene/Cesium3DTilePointFeature"; import ImageryLayer from "terriajs-cesium/Source/Scene/ImageryLayer"; import ImageryLayerFeatureInfo from "terriajs-cesium/Source/Scene/ImageryLayerFeatureInfo"; -import { JsonObject } from "../../Core/Json"; +import JsonValue from "../../Core/Json"; import { BaseModel } from "../Definition/Model"; import { TerriaFeatureData } from "./FeatureData"; @@ -19,7 +19,7 @@ const customProperties = ["entityCollection", "properties", "data"]; export default class TerriaFeature extends Entity { /** This object can be used to pass Terria-specific properties */ - data?: TerriaFeatureData | JsonObject; + data?: TerriaFeatureData | JsonValue; cesiumEntity?: Entity; imageryLayer?: ImageryLayer | undefined; diff --git a/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx b/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx index 58ea83eab25..29f4b8847e9 100644 --- a/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx +++ b/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx @@ -19,9 +19,9 @@ import Cartesian3 from "terriajs-cesium/Source/Core/Cartesian3"; import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import JulianDate from "terriajs-cesium/Source/Core/JulianDate"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; +import TerriaError from "../../Core/TerriaError"; import filterOutUndefined from "../../Core/filterOutUndefined"; import isDefined from "../../Core/isDefined"; -import TerriaError from "../../Core/TerriaError"; import { getName } from "../../ModelMixins/CatalogMemberMixin"; import DiscretelyTimeVaryingMixin from "../../ModelMixins/DiscretelyTimeVaryingMixin"; import MappableMixin from "../../ModelMixins/MappableMixin"; @@ -29,18 +29,19 @@ import TimeVarying from "../../ModelMixins/TimeVarying"; import TerriaFeature from "../../Models/Feature/Feature"; import FeatureInfoContext from "../../Models/Feature/FeatureInfoContext"; import Icon from "../../Styled/Icon"; +import { TimeSeriesContext } from "../../Table/tableFeatureInfoContext"; import { FeatureInfoPanelButton as FeatureInfoPanelButtonModel } from "../../ViewModels/FeatureInfoPanel"; +import { WithViewState, withViewState } from "../Context"; import parseCustomMarkdownToReact from "../Custom/parseCustomMarkdownToReact"; -import { withViewState, WithViewState } from "../Context"; -import Styles from "./feature-info-section.scss"; import FeatureInfoDownload from "./FeatureInfoDownload"; import FeatureInfoPanelButton from "./FeatureInfoPanelButton"; +import Styles from "./feature-info-section.scss"; import { generateCesiumInfoHTMLFromProperties } from "./generateCesiumInfoHTMLFromProperties"; import getFeatureProperties from "./getFeatureProperties"; import { + MustacheFunction, mustacheFormatDateTime, mustacheFormatNumberFunction, - MustacheFunction, mustacheRenderPartialByName, mustacheURLEncodeText, mustacheURLEncodeTextComponent @@ -211,7 +212,7 @@ export class FeatureInfoSection extends React.Component { longitude: number; }; currentTime?: Date; - timeSeries?: unknown; + timeSeries?: TimeSeriesContext; rawDataTable?: string; } = { partialByName: mustacheRenderPartialByName( diff --git a/lib/Table/tableFeatureInfoContext.ts b/lib/Table/tableFeatureInfoContext.ts index c3bee8294d6..9b519356fde 100644 --- a/lib/Table/tableFeatureInfoContext.ts +++ b/lib/Table/tableFeatureInfoContext.ts @@ -1,9 +1,31 @@ -import isDefined from "../Core/isDefined"; +import { CatalogMemberMixin } from "terriajs-plugin-api"; import { JsonObject } from "../Core/Json"; +import { getName } from "../ModelMixins/CatalogMemberMixin"; import TableMixin from "../ModelMixins/TableMixin"; import TerriaFeature from "../Models/Feature/Feature"; import { isTerriaFeatureData } from "../Models/Feature/FeatureData"; +export interface TimeSeriesFeatureInfoContext extends JsonObject { + terria?: { timeSeries?: TimeSeriesContext }; +} + +export interface TimeSeriesContext extends JsonObject { + /** Chart titile */ + title?: string; + /** X-column name */ + xName?: string; + /** Y-column name */ + yName?: string; + /** Units for each column */ + units?: string[]; + /** Feature ID */ + id?: string; + /** Csv data */ + data?: string; + /** Chart HTML */ + chart?: string; +} + /** Adds timeseries chart to feature info context (on terria.timeSeries property). * This enables timeseries chart to be used in featureInfoTemplate like so: * - default chart = `{{terria.timeSeries.chart}}` @@ -20,48 +42,81 @@ import { isTerriaFeatureData } from "../Models/Feature/FeatureData"; */ export const tableFeatureInfoContext: ( catalogItem: TableMixin.Instance -) => (feature: TerriaFeature) => JsonObject = (catalogItem) => (feature) => { - if (!catalogItem.isSampled) return {}; +) => (feature: TerriaFeature) => TimeSeriesFeatureInfoContext = + (catalogItem) => (feature) => { + if (!catalogItem.isSampled) return {}; - const style = catalogItem.activeTableStyle; + const style = catalogItem.activeTableStyle; - // Corresponding row IDs for the selected feature are stored in TerriaFeatureData - // See createLongitudeLatitudeFeaturePerId, createLongitudeLatitudeFeaturePerRow and createRegionMappedImageryProvider - const rowIds = isTerriaFeatureData(feature.data) - ? feature.data.rowIds ?? [] - : []; + // Corresponding row IDs for the selected feature are stored in TerriaFeatureData + // See createLongitudeLatitudeFeaturePerId, createLongitudeLatitudeFeaturePerRow and createRegionMappedImageryProvider + const rowIds = isTerriaFeatureData(feature.data) + ? feature.data.rowIds ?? [] + : []; - if (!style.timeColumn || !style.colorColumn || rowIds.length < 2) return {}; + if (!style.timeColumn || !style.colorColumn || rowIds.length < 2) return {}; - const chartColumns = [style.timeColumn, style.colorColumn]; - const csvData = [ - chartColumns.map((col) => col!.title).join(","), - ...rowIds.map((i) => - chartColumns!.map((col) => col.valueFunctionForType(i)).join(",") - ) - ] - .join("\n") - .replace(/\\n/g, "\\n"); + const chartColumns = [style.timeColumn, style.colorColumn]; + const csvData = [ + chartColumns.map((col) => col!.title).join(","), + ...rowIds.map((i) => + chartColumns!.map((col) => col.valueFunctionForType(i)).join(",") + ) + ] + .join("\n") + .replace(/\\n/g, "\\n"); - const title = style.colorColumn?.title; + const title = style.colorColumn?.title; - const featureId = feature.id.replace(/\"/g, ""); + const featureId = feature.id.replace(/\"/g, ""); - const result = { - terria: { - timeSeries: { - title: style.colorColumn?.title, - xName: style.timeColumn?.title, - yName: style.colorColumn?.title, - units: chartColumns.map((column) => column.units || ""), - id: featureId, - data: csvData, - chart: `${csvData}` + const timeSeriesContext: TimeSeriesContext = { + title: style.colorColumn?.title, + xName: style.timeColumn?.title, + yName: style.colorColumn?.title, + units: chartColumns.map((column) => column.units || ""), + id: featureId, + data: csvData, + chart: `${csvData}` + }; + + return { + terria: { + timeSeries: timeSeriesContext } - } + }; }; - return result; -}; +/** Add `TimeSeriesFeatureInfoContext` to features with CSV string data (on `data` property) */ +export const csvFeatureInfoContext: ( + catalogItem: CatalogMemberMixin.Instance +) => (feature: TerriaFeature) => TimeSeriesFeatureInfoContext = + (catalogItem) => (feature) => { + // Check that feature data has return CSV as string + if (typeof feature.data === "string") { + const featureId = feature.id.replace(/\"/g, ""); + // Remove comment lines in CSV (start with # and don't have any commas) + const csvData = feature.data + .split("\n") + .filter((l) => !(l.startsWith("#") && !l.includes(","))) + .join("\n"); + + const title = getName(catalogItem); + return { + terria: { + timeSeries: { + title, + id: featureId, + data: csvData, + chart: `${csvData}` + } + } + }; + } + + return {}; + }; diff --git a/lib/Traits/TraitsClasses/WebMapServiceCatalogItemTraits.ts b/lib/Traits/TraitsClasses/WebMapServiceCatalogItemTraits.ts index 6eda5daaafd..deb3ddffc32 100644 --- a/lib/Traits/TraitsClasses/WebMapServiceCatalogItemTraits.ts +++ b/lib/Traits/TraitsClasses/WebMapServiceCatalogItemTraits.ts @@ -141,9 +141,9 @@ export class GetFeatureInfoFormat extends ModelTraits { type: "string", name: "Type", description: - "The type of response to expect from a GetFeatureInfo request. Valid values are 'json', 'xml', 'html', or 'text'." + "The type of response to expect from a GetFeatureInfo request. Valid values are 'json', 'xml', 'html', 'text' or 'csv'. If type is 'csv', then featureInfoContext will contain timeSeries object (see \"Customizing the Feature Info Template\" in documentation)" }) - type?: "json" | "xml" | "html" | "text" | undefined; + type?: "json" | "xml" | "html" | "text" | "csv" | undefined; @primitiveTrait({ type: "string", @@ -292,6 +292,14 @@ export default class WebMapServiceCatalogItemTraits extends mixTraits( }) supportsGetLegendGraphic: boolean = false; + @primitiveTrait({ + type: "boolean", + name: "Supports GetTimeseries requests", + description: + 'Gets or sets whether this WMS server supports GetTimeseries requests. If true, then GetTimeseries will be used instead of GetFeatureInfo. This will also set default value of `getFeatureInfoFormat` to `{ format: "text/csv", type: "text" }`' + }) + supportsGetTimeseries: boolean = false; + @primitiveTrait({ type: "number", name: "Color scale minimum", diff --git a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts index 05541195379..6789a8904b0 100644 --- a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts +++ b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts @@ -7,6 +7,7 @@ import { ImageryParts } from "../../../../lib/ModelMixins/MappableMixin"; import WebMapServiceCatalogItem from "../../../../lib/Models/Catalog/Ows/WebMapServiceCatalogItem"; import CommonStrata from "../../../../lib/Models/Definition/CommonStrata"; import Terria from "../../../../lib/Models/Terria"; +import TerriaFeature from "../../../../lib/Models/Feature/Feature"; describe("WebMapServiceCatalogItem", function () { it("derives getCapabilitiesUrl from url if getCapabilitiesUrl is not specified", function () { @@ -312,6 +313,84 @@ describe("WebMapServiceCatalogItem", function () { } }); + it("correctly sets supportsGetTimeseries and featureInfoContext", async function () { + let wms: WebMapServiceCatalogItem; + const terria = new Terria(); + wms = new WebMapServiceCatalogItem("test", terria); + runInAction(() => { + wms.setTrait("definition", "url", "test/WMS/colorscalerange.xml"); + wms.setTrait("definition", "layers", "mylayer"); + }); + let mapItems: ImageryParts[] = []; + const cleanup = autorun(() => { + mapItems = wms.mapItems.slice(); + }); + try { + await wms.loadMetadata(); + console.log(wms); + expect(mapItems.length).toBe(1); + expect(mapItems[0].alpha).toBeCloseTo(0.8); + expect( + mapItems[0].imageryProvider instanceof WebMapServiceImageryProvider + ).toBeTruthy(); + expect(wms.supportsGetTimeseries).toBeTruthy(); + expect(wms.getFeatureInfoFormat.format).toBe("text/csv"); + expect(wms.getFeatureInfoFormat.type).toBe("text"); + expect(wms.featureInfoTemplate.template).toBe( + "{{terria.timeSeries.chart}}" + ); + + expect(wms.featureInfoContext).toBeDefined(); + const feature = new TerriaFeature({}); + feature.data = "#some comment\nsome,csv\n1,2"; + expect(wms.featureInfoContext(feature).terria?.timeSeries?.data).toBe( + "some,csv\n1,2" + ); + expect(wms.featureInfoContext(feature).terria?.timeSeries?.title).toBe( + wms.name + ); + + if (mapItems[0].imageryProvider instanceof WebMapServiceImageryProvider) { + expect(mapItems[0].imageryProvider.url).toBe( + "test/WMS/colorscalerange.xml" + ); + + const tileProviderResource: Resource = ( + mapItems[0].imageryProvider as any + )._tileProvider._resource; + + expect(tileProviderResource.queryParameters.version).toBe("1.3.0"); + expect(tileProviderResource.queryParameters.crs).toBe("EPSG:4326"); + expect(tileProviderResource.queryParameters.exceptions).toBe("XML"); + expect(tileProviderResource.queryParameters.service).toBe("WMS"); + expect(tileProviderResource.queryParameters.request).toBe("GetMap"); + expect(tileProviderResource.queryParameters.transparent).toBeTruthy(); + expect(tileProviderResource.queryParameters.format).toBe("image/png"); + + const getFeatureInfoResource: Resource = ( + mapItems[0].imageryProvider as any + )._pickFeaturesResource; + + expect(getFeatureInfoResource.queryParameters.version).toBe("1.3.0"); + expect(getFeatureInfoResource.queryParameters.time).toBe(""); + expect(getFeatureInfoResource.queryParameters.crs).toBe("EPSG:4326"); + expect(getFeatureInfoResource.queryParameters.exceptions).toBe("XML"); + expect(getFeatureInfoResource.queryParameters.service).toBe("WMS"); + expect(getFeatureInfoResource.queryParameters.request).toBe( + "GetTimeseries" + ); + expect(getFeatureInfoResource.queryParameters.feature_count).toBe( + terria.configParameters.defaultMaximumShownFeatureInfos + 1 + ); + + expect(mapItems[0].imageryProvider.tileHeight).toBe(256); + expect(mapItems[0].imageryProvider.tileWidth).toBe(256); + } + } finally { + cleanup(); + } + }); + it("constructs correct ImageryProvider when layers trait provided Title", async function () { let wms: WebMapServiceCatalogItem; const terria = new Terria(); From cf4199224852982773e25b0d245ab3215cf1af88 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Thu, 21 Sep 2023 22:26:12 +1000 Subject: [PATCH 043/129] fix import --- lib/Table/tableFeatureInfoContext.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Table/tableFeatureInfoContext.ts b/lib/Table/tableFeatureInfoContext.ts index 9b519356fde..267f6ba66cd 100644 --- a/lib/Table/tableFeatureInfoContext.ts +++ b/lib/Table/tableFeatureInfoContext.ts @@ -1,6 +1,5 @@ -import { CatalogMemberMixin } from "terriajs-plugin-api"; import { JsonObject } from "../Core/Json"; -import { getName } from "../ModelMixins/CatalogMemberMixin"; +import CatalogMemberMixin, { getName } from "../ModelMixins/CatalogMemberMixin"; import TableMixin from "../ModelMixins/TableMixin"; import TerriaFeature from "../Models/Feature/Feature"; import { isTerriaFeatureData } from "../Models/Feature/FeatureData"; From 2069044877e02423417f1488c22a5fcb290a815c Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 5 Oct 2023 14:22:35 +1000 Subject: [PATCH 044/129] Remove usages of construct --- .../Analytics/GeoJsonParameterEditor.jsx | 12 +- lib/ReactViews/Analytics/InvokeFunction.jsx | 4 +- lib/ReactViews/Analytics/ParameterEditor.jsx | 23 +- .../Analytics/PointParameterEditor.jsx | 4 +- .../BottomDock/Timeline/Timeline.jsx | 32 +- .../CleanDropdownPanel/CleanDropdownPanel.jsx | 4 +- lib/ReactViews/DataCatalog/CatalogGroup.jsx | 16 +- lib/ReactViews/DataCatalog/DataCatalog.jsx | 20 +- .../DataCatalog/DataCatalogGroup.jsx | 4 +- .../ExplorerWindow/Tabs/MyDataTab/AddData.jsx | 154 +++---- .../Tabs/MyDataTab/MyDataTab.jsx | 72 ++-- lib/ReactViews/Map/MenuBar/MenuBar.jsx | 12 +- lib/ReactViews/Map/Panels/DropdownPanel.jsx | 12 +- .../Map/Panels/HelpPanel/HelpVideoPanel.jsx | 68 +-- lib/ReactViews/Map/Panels/InnerPanel.jsx | 8 +- lib/ReactViews/Map/Panels/MobilePanel.jsx | 30 +- .../Map/Panels/SharePanel/StorySharePanel.jsx | 4 +- .../Map/Panels/ToolsPanel/ToolsPanel.jsx | 4 +- lib/ReactViews/Mobile/MobileHeader.jsx | 4 +- lib/ReactViews/Mobile/MobileMenu.jsx | 21 +- lib/ReactViews/Mobile/MobileModalWindow.jsx | 8 +- lib/ReactViews/Mobile/MobileSearch.jsx | 37 +- .../Notification/NotificationWindow.jsx | 4 +- lib/ReactViews/Preview/DataPreviewUrl.jsx | 24 +- lib/ReactViews/Preview/Description.jsx | 389 +++++++++--------- lib/ReactViews/Preview/GroupPreview.jsx | 26 +- lib/ReactViews/Preview/MappablePreview.jsx | 62 ++- lib/ReactViews/Preview/MetadataTable.jsx | 20 +- lib/ReactViews/Search/Breadcrumbs.jsx | 4 +- lib/ReactViews/Search/SearchBoxAndResults.jsx | 4 +- .../WelcomeMessage/WelcomeMessage.jsx | 132 +++--- .../Workbench/Controls/TimerSection.jsx | 4 +- 32 files changed, 591 insertions(+), 631 deletions(-) diff --git a/lib/ReactViews/Analytics/GeoJsonParameterEditor.jsx b/lib/ReactViews/Analytics/GeoJsonParameterEditor.jsx index b04ddc5efa8..fe9b4000a30 100644 --- a/lib/ReactViews/Analytics/GeoJsonParameterEditor.jsx +++ b/lib/ReactViews/Analytics/GeoJsonParameterEditor.jsx @@ -121,16 +121,8 @@ class GeoJsonParameterEditor extends React.Component { this.props.parameter )} /> - -
    {t("analytics.nothingSelected")}
    -
    + {getDisplayValue(this.props.parameter.value, this.props.parameter) === + "" &&
    {t("analytics.nothingSelected")}
    } ); } diff --git a/lib/ReactViews/Analytics/InvokeFunction.jsx b/lib/ReactViews/Analytics/InvokeFunction.jsx index 322f417f5cc..b58172ff7ec 100644 --- a/lib/ReactViews/Analytics/InvokeFunction.jsx +++ b/lib/ReactViews/Analytics/InvokeFunction.jsx @@ -128,12 +128,12 @@ class InvokeFunction extends React.Component {

    {this.props.previewed.name}

    - + {this.props.previewed.loadMetadataResult?.error && ( - + )}
    {parseCustomMarkdownToReact(this.props.previewed.description, { catalogItem: this.props.previewed diff --git a/lib/ReactViews/Analytics/ParameterEditor.jsx b/lib/ReactViews/Analytics/ParameterEditor.jsx index efd9ce3a684..6f11239bcff 100644 --- a/lib/ReactViews/Analytics/ParameterEditor.jsx +++ b/lib/ReactViews/Analytics/ParameterEditor.jsx @@ -265,17 +265,20 @@ ParameterEditor.parameterTypeConverters = [ )[0]; return (
    - - {parameterEditor.renderLabel()} - - - + {regionParam === undefined && ( + <> + {parameterEditor.renderLabel()} + + + )} + + {!parameterEditor.props.parameter.showInUi && (
    - + )}
    ); } diff --git a/lib/ReactViews/Analytics/PointParameterEditor.jsx b/lib/ReactViews/Analytics/PointParameterEditor.jsx index 874271c1843..2fcec4baf8e 100644 --- a/lib/ReactViews/Analytics/PointParameterEditor.jsx +++ b/lib/ReactViews/Analytics/PointParameterEditor.jsx @@ -71,11 +71,11 @@ const PointParameterEditor = createReactClass({ const { t } = this.props; return (
    - + {showErrorMessage && (
    {t("analytics.enterValidCoords")}
    -
    + )} - - this.changeDateTime()} - openDirection="up" - isOpen={this.state.isPickerOpen} - onOpen={() => this.onOpenPicker()} - onClose={() => this.onClosePicker()} - dateFormat={catalogItem.dateFormat} - /> - + {defined(discreteTimes) && + discreteTimes.length !== 0 && + defined(currentDiscreteJulianDate) && ( + this.changeDateTime()} + openDirection="up" + isOpen={this.state.isPickerOpen} + onOpen={() => this.onOpenPicker()} + onClose={() => this.onClosePicker()} + dateFormat={catalogItem.dateFormat} + /> + )}
    diff --git a/lib/ReactViews/CleanDropdownPanel/CleanDropdownPanel.jsx b/lib/ReactViews/CleanDropdownPanel/CleanDropdownPanel.jsx index e35901ac0de..e40d58b2ff2 100644 --- a/lib/ReactViews/CleanDropdownPanel/CleanDropdownPanel.jsx +++ b/lib/ReactViews/CleanDropdownPanel/CleanDropdownPanel.jsx @@ -93,7 +93,7 @@ const CleanDropdownPanel = createReactClass({ ${this.props.cleanDropdownPanelStyles} `} > - + {this.isOpen() && ( {this.props.children} - + )} ); } diff --git a/lib/ReactViews/DataCatalog/CatalogGroup.jsx b/lib/ReactViews/DataCatalog/CatalogGroup.jsx index 58ef95040d4..8307c38d53e 100644 --- a/lib/ReactViews/DataCatalog/CatalogGroup.jsx +++ b/lib/ReactViews/DataCatalog/CatalogGroup.jsx @@ -39,9 +39,9 @@ function CatalogGroup(props) {
  • {/* If this is a display group, show the "PlusList" button */} - {/* TODO: This should be superimposed on the above button. + {/* TODO: This should be superimposed on the above button. We cannot have a button within a button, so maybe use z-values and superimpose */} - {/* TODO: Maybe this should be a component with a 'mode' flag. + {/* TODO: Maybe this should be a component with a 'mode' flag. With a different appearance and onClick function depending on the mode */} {props.displayGroup === true && ( @@ -79,7 +79,7 @@ function CatalogGroup(props) { onClick={props.onClick} active={props.selected} > - + {!props.topLevel && ( {props.open ? ( @@ -87,7 +87,7 @@ function CatalogGroup(props) { )} - + )} {props.text} @@ -104,7 +104,7 @@ function CatalogGroup(props) { )} {/* This next button is for user added data, and perhaps should be called 'trashable' instead of 'removable' */} - + {props.removable && ( - + )} - + {props.open && (
      {props.children}
    -
    + )}
  • ); } diff --git a/lib/ReactViews/DataCatalog/DataCatalog.jsx b/lib/ReactViews/DataCatalog/DataCatalog.jsx index 65690cef0aa..cf0fe0a197b 100644 --- a/lib/ReactViews/DataCatalog/DataCatalog.jsx +++ b/lib/ReactViews/DataCatalog/DataCatalog.jsx @@ -39,15 +39,17 @@ class DataCatalog extends React.Component { const { t } = this.props; return (
      - - - - + {isSearching && catalogSearchProvider && ( + <> + + + + )} {item !== this.props.terria.catalog.userAddedDataGroup && ( - + {this.isOpen() && ( - + )} ); } diff --git a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx index c1c148943e3..10d48f53479 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx +++ b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx @@ -216,83 +216,87 @@ const AddData = createReactClass({ return (
      - -
      {t("addData.localAdd")}
      -
      - - - {this.state.localDataType?.description - ? parseCustomMarkdownToReactWithOptions( - this.state.localDataType?.description - ) - : null} - - - {this.state.isLoading && } -
      -
      - -
      {t("addData.webAdd")}
      -
      - - - {this.state.remoteDataType?.description - ? parseCustomMarkdownToReactWithOptions( - this.state.remoteDataType?.description - ) - : null} - -
      - +
      {t("addData.localAdd")}
      +
      + + + {this.state.localDataType?.description + ? parseCustomMarkdownToReactWithOptions( + this.state.localDataType?.description + ) + : null} + + - {this.state.isLoading && } - -
      - +
      + + )} + {this.props.activeTab === "web" && ( + <> +
      {t("addData.webAdd")}
      +
      + + + {this.state.remoteDataType?.description + ? parseCustomMarkdownToReactWithOptions( + this.state.remoteDataType?.description + ) + : null} + +
      + + + {this.state.isLoading && } + +
      + + )}
      ); }, diff --git a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx index 2331e66977b..de13e91044f 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx +++ b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx @@ -137,37 +137,39 @@ class MyDataTab extends React.Component { [Styles.oneCol]: !showTwoColumn })} > - - - this.resetTab()} - onFileAddFinished={this.props.onFileAddFinished} - onUrlAddFinished={this.props.onUrlAddFinished} - localDataTypes={this.props.localDataTypes} - remoteDataTypes={this.props.remoteDataTypes} - /> - - + {this.state.activeTab && ( + <> + + this.resetTab()} + onFileAddFinished={this.props.onFileAddFinished} + onUrlAddFinished={this.props.onUrlAddFinished} + localDataTypes={this.props.localDataTypes} + remoteDataTypes={this.props.remoteDataTypes} + /> + + )} + {showTwoColumn && (

      @@ -188,10 +190,10 @@ class MyDataTab extends React.Component { />

    - - {this.renderPromptBox()} + )} + {!this.state.activeTab && this.renderPromptBox()}
    - + {showTwoColumn && ( - + )} ); } diff --git a/lib/ReactViews/Map/MenuBar/MenuBar.jsx b/lib/ReactViews/Map/MenuBar/MenuBar.jsx index 51466898134..80831e80cc2 100644 --- a/lib/ReactViews/Map/MenuBar/MenuBar.jsx +++ b/lib/ReactViews/Map/MenuBar/MenuBar.jsx @@ -56,13 +56,13 @@ const MenuBar = observer((props) => { )} - + {!viewState.useSmallScreenInterface && (
  • {element}
  • -
    + )}
    @@ -83,7 +83,7 @@ const MenuBar = observer((props) => { ) : null} - + {storyEnabled && (
    • { />
    -
    + )}
    • { />
    - + {!viewState.useSmallScreenInterface && (
  • {element}
  • -
    + )}
    ); diff --git a/lib/ReactViews/Map/Panels/DropdownPanel.jsx b/lib/ReactViews/Map/Panels/DropdownPanel.jsx index de08a135ec0..2ecd9bbc4fa 100644 --- a/lib/ReactViews/Map/Panels/DropdownPanel.jsx +++ b/lib/ReactViews/Map/Panels/DropdownPanel.jsx @@ -111,14 +111,10 @@ const DropdownPanel = createReactClass({ }`} `} > - - - - - {this.props.btnText} - + {this.props.theme.icon && } + {this.props.btnText && {this.props.btnText}} - + {this.isOpen() && ( {this.props.children} - + )}
    ); } diff --git a/lib/ReactViews/Map/Panels/HelpPanel/HelpVideoPanel.jsx b/lib/ReactViews/Map/Panels/HelpPanel/HelpVideoPanel.jsx index 1a4aac2c5e5..93101fe8cfc 100644 --- a/lib/ReactViews/Map/Panels/HelpPanel/HelpVideoPanel.jsx +++ b/lib/ReactViews/Map/Panels/HelpPanel/HelpVideoPanel.jsx @@ -76,50 +76,52 @@ class HelpVideoPanel extends React.Component { `} scroll > - - {this.props.videoUrl && this.props.placeholderImage && ( -
    -
    - + +
    +
    - -
    - )} - {this.props.markdownContent && ( - - )} -
    - + )} + {this.props.markdownContent && ( + + )} + + )} + {helpItemType === "slider" && ( - - + )} + {helpItemType === "trainer" && ( - + )} ) diff --git a/lib/ReactViews/Map/Panels/InnerPanel.jsx b/lib/ReactViews/Map/Panels/InnerPanel.jsx index 5eec2290d3b..c2594e35187 100644 --- a/lib/ReactViews/Map/Panels/InnerPanel.jsx +++ b/lib/ReactViews/Map/Panels/InnerPanel.jsx @@ -161,11 +161,7 @@ const InnerPanel = createReactClass({ > - + {defined(this.props.caretOffset) && !this.props.showDropdownAsModal && ( p.theme.dark}; `} /> - + )}
    {this.props.children}
    ); diff --git a/lib/ReactViews/Map/Panels/MobilePanel.jsx b/lib/ReactViews/Map/Panels/MobilePanel.jsx index bf5f1f72189..39c2e0220ce 100644 --- a/lib/ReactViews/Map/Panels/MobilePanel.jsx +++ b/lib/ReactViews/Map/Panels/MobilePanel.jsx @@ -29,20 +29,22 @@ const MobilePanel = createReactClass({ caption={this.props.btnText} icon={this.props.mobileIcon} /> - - {/* The overlay doesn't actually need to do anything except block clicks, as InnerPanel will listen to the window */} -
    - - - {this.props.children} - - + {this.isOpen() && ( + <> + {/* The overlay doesn't actually need to do anything except block clicks, as InnerPanel will listen to the window */} +
    + + + {this.props.children} + + + )}
    ); } diff --git a/lib/ReactViews/Map/Panels/SharePanel/StorySharePanel.jsx b/lib/ReactViews/Map/Panels/SharePanel/StorySharePanel.jsx index 084178915bf..9b80b1c78fc 100644 --- a/lib/ReactViews/Map/Panels/SharePanel/StorySharePanel.jsx +++ b/lib/ReactViews/Map/Panels/SharePanel/StorySharePanel.jsx @@ -76,7 +76,7 @@ const StorySharePanel = createReactClass({ > {this.props.btnText ? this.props.btnText : ""} - + {this.isOpen() && (
    -
    + )} ); } diff --git a/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx b/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx index 397c7e51cf3..634f6ac8165 100644 --- a/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx +++ b/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx @@ -30,13 +30,13 @@ const ToolsPanel = observer(() => { isOpen={isOpen} smallScreen={viewState.useSmallScreenInterface} > - + {isOpen && (
    -
    + )}
    diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index 63800fa92a2..0c24824cace 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -194,7 +194,7 @@ class MobileHeader extends React.Component { styledHeight="20px" /> - 0}> + {nowViewingLength > 0 && ( - + )} - + )}
    - + {denyText && ( - + )} diff --git a/lib/ReactViews/Preview/DataPreviewUrl.jsx b/lib/ReactViews/Preview/DataPreviewUrl.jsx index 83ae3bbc605..420d29b98ec 100644 --- a/lib/ReactViews/Preview/DataPreviewUrl.jsx +++ b/lib/ReactViews/Preview/DataPreviewUrl.jsx @@ -23,7 +23,7 @@ const DataPreviewUrl = createReactClass({ return (

    {this.props.metadataItem.typeName} URL

    - + {this.props.metadataItem.type === "wms" && (

    This is a @@ -38,8 +38,8 @@ const DataPreviewUrl = createReactClass({ software with this URL:

    -
    - + )} + {this.props.metadataItem.type === "wfs" && (

    This is a @@ -54,7 +54,7 @@ const DataPreviewUrl = createReactClass({ GIS software with this URL:

    -
    + )} - + {(this.props.metadataItem.type === "wms" || + (this.props.metadataItem.type === "esri-mapServer" && + this.props.metadataItem.layers)) && (

    Layer name {this.props.metadataItem.layers.split(",").length > 1 ? "s" : ""}: {this.props.metadataItem.layers}

    -
    - + )} + {this.props.metadataItem.type === "wfs" && (

    Type name {this.props.metadataItem.typeNames.split(",").length > 1 ? "s" : ""} : {this.props.metadataItem.typeNames}

    -
    + )}
    ); } diff --git a/lib/ReactViews/Preview/Description.jsx b/lib/ReactViews/Preview/Description.jsx index cfb32fdd38c..444b71a5316 100644 --- a/lib/ReactViews/Preview/Description.jsx +++ b/lib/ReactViews/Preview/Description.jsx @@ -42,164 +42,159 @@ class Description extends React.Component { } `} > - + {catalogItem.isExperiencingIssues && ( {t("preview.mayBeExperiencingIssues")} - + )} - 0 - } - > + {catalogItem.description && catalogItem.description.length > 0 && (

    {t("description.name")}

    {parseCustomMarkdownToReact(catalogItem.description, { catalogItem: catalogItem })}
    -
    + )} - -

    {t("description.dataLocal")}

    -
    + {catalogItem.hasLocalData &&

    {t("description.dataLocal")}

    } - + {!catalogItem.hasLocalData && !catalogItem.hasDescription && (

    {t("description.dataNotLocal")}

    -
    + )} - 0}> -

    {t("description.metadataUrls")}

    - - - p.theme.colorPrimary}; - `} - > - - - - {metadataUrl.url} - - - -
    + {metadataUrls && metadataUrls.length > 0 && ( + <> +

    {t("description.metadataUrls")}

    + + + p.theme.colorPrimary}; + `} + > + {metadataUrl.title && ( + + )} + {!metadataUrl.title ? metadataUrl.url : null} + + + + + )} - 0 - } - > + {catalogItem.dataCustodian && catalogItem.dataCustodian.length > 0 && (

    {t("description.dataCustodian")}

    {parseCustomMarkdownToReact(catalogItem.dataCustodian, { catalogItem: catalogItem })}
    -
    + )} - - -

    {catalogItem.typeName} URL

    - - -

    - - This is a - - WMS service - - , which generates map images on request. It can be used in - GIS software with this URL: - -

    -
    - -

    - - This is a - - WFS service - - , which transfers raw spatial data on request. It can be - used in GIS software with this URL: - -

    -
    -
    + {!catalogItem.hideSource && ( + <> + {catalogItem.url && ( + <> +

    {catalogItem.typeName} URL

    + + +

    + + This is a + + WMS service + + , which generates map images on request. It can be used + in GIS software with this URL: + +

    +
    + +

    + + This is a + + WFS service + + , which transfers raw spatial data on request. It can be + used in GIS software with this URL: + +

    +
    +
    - - - {catalogItem.url} - - - e.target.select()} - /> - - + + + {catalogItem.url} + + + e.target.select()} + /> + + - - -

    - {t("description.layerName")} - {(catalogItem.layers || "").split(",").length > 1 - ? "s" - : ""}: {catalogItem.layers} -

    -
    - -

    - {t("description.typeName")} - {(catalogItem.typeNames || "").split(",").length > 1 - ? "s" - : ""} - : {catalogItem.typeNames} -

    -
    -
    -
    + + +

    + {t("description.layerName")} + {(catalogItem.layers || "").split(",").length > 1 + ? "s" + : ""} + : {catalogItem.layers} +

    +
    + +

    + {t("description.typeName")} + {(catalogItem.typeNames || "").split(",").length > 1 + ? "s" + : ""} + : {catalogItem.typeNames} +

    +
    +
    + + )} - 0}> -

    {t("description.dataUrl")}

    - - - - {dataUrl.type?.startsWith("wfs") && - parseCustomMarkdownToReact( - t("description.useLinkBelow", { - link: ` + {dataUrls && dataUrls.length > 0 && ( + <> +

    {t("description.dataUrl")}

    + + + + {dataUrl.type?.startsWith("wfs") && + parseCustomMarkdownToReact( + t("description.useLinkBelow", { + link: ` ` - }) - )} - {dataUrl.type?.startsWith("wcs") && - parseCustomMarkdownToReact( - t("description.useLinkBelow", { - link: ` + }) + )} + {dataUrl.type?.startsWith("wcs") && + parseCustomMarkdownToReact( + t("description.useLinkBelow", { + link: ` ` - }) - )} - - - - p.theme.colorPrimary}; - `} - > - - - - {dataUrl.url} - - - -
    + }) + )} + + + + p.theme.colorPrimary}; + `} + > + {dataUrl.title && ( + + )} + {!dataUrl.title ? dataUrl.url : null} + + + + + )} - - 0 - } - > -
    - - - -
    -
    - 0 - } - > -
    - - - -
    -
    -
    -
    + {!this.props.printView && defined(catalogItem.metadata) && ( + <> + {defined(catalogItem.metadata.dataSourceMetadata) && + catalogItem.metadata.dataSourceMetadata.items.length > 0 && ( +
    + + + +
    + )} + {defined(catalogItem.metadata.dataSourceMetadata) && + catalogItem.metadata.dataSourceMetadata.items.length > 0 && ( +
    + + + +
    + )} + + )} + + )} {!this.props.printView ? ( ) : null} diff --git a/lib/ReactViews/Preview/GroupPreview.jsx b/lib/ReactViews/Preview/GroupPreview.jsx index c412a96ca37..65f49d82c88 100644 --- a/lib/ReactViews/Preview/GroupPreview.jsx +++ b/lib/ReactViews/Preview/GroupPreview.jsx @@ -74,18 +74,18 @@ class GroupPreview extends React.Component { />
    - + {this.props.previewed.loadMetadataResult?.error && ( - - + )} + {this.props.previewed.loadMembersResult?.error && ( - + )}
    @@ -107,24 +107,20 @@ class GroupPreview extends React.Component { - + {metadataItem.dataCustodian && (

    {t("preview.dataCustodian")}

    {parseCustomMarkdownToReact(metadataItem.dataCustodian, { catalogItem: metadataItem })}
    -
    + )} - - - + {metadataItem.url && + metadataItem.url.length && + !metadataItem.hideSource && ( + + )}
    diff --git a/lib/ReactViews/Preview/MappablePreview.jsx b/lib/ReactViews/Preview/MappablePreview.jsx index 61b7a8c3541..2d21f3d34b5 100644 --- a/lib/ReactViews/Preview/MappablePreview.jsx +++ b/lib/ReactViews/Preview/MappablePreview.jsx @@ -66,21 +66,17 @@ class MappablePreview extends React.Component { const catalogItem = this.props.previewed; return (
    - - - + {MappableMixin.isMixedInto(catalogItem) && + !catalogItem.disablePreview && ( + + )}
    - + {catalogItem.loadMetadataResult?.error && ( - - + )} + {catalogItem.loadMapItemsResult?.error && ( - + )} diff --git a/lib/ReactViews/Preview/MetadataTable.jsx b/lib/ReactViews/Preview/MetadataTable.jsx index 89ba5d80ba4..1f270582971 100644 --- a/lib/ReactViews/Preview/MetadataTable.jsx +++ b/lib/ReactViews/Preview/MetadataTable.jsx @@ -29,15 +29,11 @@ const MetadataTable = createReactClass({ - 0 && isJoinable(metadataItem) - } - > + {metadataItem.length > 0 && isJoinable(metadataItem) && ( {metadataItem.join(", ")} - + )} 0 && !isArr}> @@ -54,14 +50,10 @@ const MetadataTable = createReactClass({ isObservableArray(metadataItem[key]) } > - 0 && - isJoinable(metadataItem[key]) - } - > - {metadataItem[key].join(", ")} - + {metadataItem[key].length > 0 && + isJoinable(metadataItem[key]) + ? metadataItem[key].join(", ") + : null} {metadataItem[key]} diff --git a/lib/ReactViews/Search/Breadcrumbs.jsx b/lib/ReactViews/Search/Breadcrumbs.jsx index c70435a7b3d..c863f43054d 100644 --- a/lib/ReactViews/Search/Breadcrumbs.jsx +++ b/lib/ReactViews/Search/Breadcrumbs.jsx @@ -99,13 +99,13 @@ class Breadcrumbs extends React.Component { - + {i !== parentGroups.length - 1 && ( {">"} - + )} )} diff --git a/lib/ReactViews/Search/SearchBoxAndResults.jsx b/lib/ReactViews/Search/SearchBoxAndResults.jsx index 36f5fd82645..73de17a905e 100644 --- a/lib/ReactViews/Search/SearchBoxAndResults.jsx +++ b/lib/ReactViews/Search/SearchBoxAndResults.jsx @@ -165,7 +165,7 @@ export class SearchBoxAndResultsRaw extends React.Component { /> {/* Results */} - + {shouldShowResults && ( - + )} ); diff --git a/lib/ReactViews/WelcomeMessage/WelcomeMessage.jsx b/lib/ReactViews/WelcomeMessage/WelcomeMessage.jsx index 5ef747bb44f..1df80969196 100644 --- a/lib/ReactViews/WelcomeMessage/WelcomeMessage.jsx +++ b/lib/ReactViews/WelcomeMessage/WelcomeMessage.jsx @@ -241,72 +241,78 @@ export const WelcomeMessagePure = (props) => { - - - { - viewState.terria.configParameters.welcomeMessageVideo - .videoTitle - } - - - - - - + + { viewState.terria.configParameters.welcomeMessageVideo - .placeholderImage + .videoTitle } - backgroundBlackOverlay={"50%"} - > - - viewState.setVideoGuideVisible(WELCOME_MESSAGE_VIDEO) + + + + )} + + {!viewState.useSmallScreenInterface && ( + <> + - -
    - - -
    + + viewState.setVideoGuideVisible(WELCOME_MESSAGE_VIDEO) + } + > + + + + + + )} - - { - handleClose(false); - // not sure if we should wait for the exit animation, - // if we don't, we have a flicker due to the difference - // in overlay darkness - but if we wait, it goes - // dark -> light -> dark anyway.. - setShouldTakeTour(true); - viewState.setTourIndex(0); - viewState.setShowTour(true); - viewState.setTopElement(TourPortalDisplayName); - }} - buttonText={t("welcomeMessage.tourBtnText")} - buttonIcon={Icon.GLYPHS.tour} - /> - - { - handleClose(false); - setShouldOpenHelp(true); - }} - /> - + {!viewState.useSmallScreenInterface && ( + <> + { + handleClose(false); + // not sure if we should wait for the exit animation, + // if we don't, we have a flicker due to the difference + // in overlay darkness - but if we wait, it goes + // dark -> light -> dark anyway.. + setShouldTakeTour(true); + viewState.setTourIndex(0); + viewState.setShowTour(true); + viewState.setTopElement(TourPortalDisplayName); + }} + buttonText={t("welcomeMessage.tourBtnText")} + buttonIcon={Icon.GLYPHS.tour} + /> + + { + handleClose(false); + setShouldOpenHelp(true); + }} + /> + + )} { )} - - - + {!viewState.useSmallScreenInterface && } diff --git a/lib/ReactViews/Workbench/Controls/TimerSection.jsx b/lib/ReactViews/Workbench/Controls/TimerSection.jsx index e6a0d07399f..f72abafe627 100644 --- a/lib/ReactViews/Workbench/Controls/TimerSection.jsx +++ b/lib/ReactViews/Workbench/Controls/TimerSection.jsx @@ -110,7 +110,7 @@ class TimerSection extends React.Component { const { t } = this.props; return ( <> - + {this.isEnabled() && (
    - + )} ); } From 271396bfaf94e3d43fdb3922f7b641454d7a4a4b Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Wed, 11 Oct 2023 20:11:48 +1100 Subject: [PATCH 045/129] Tweak ArcGis WMS getFeatureInfoFormat --- CHANGES.md | 4 + .../Ows/WebMapServiceCapabilitiesStratum.ts | 21 +- .../Ows/WebMapServiceCatalogItemSpec.ts | 54 +++ wwwroot/test/WMS/wms_esri.xml | 150 ++++++++ wwwroot/test/WMS/wms_esri_2.xml | 328 ++++++++++++++++++ 5 files changed, 555 insertions(+), 2 deletions(-) create mode 100644 wwwroot/test/WMS/wms_esri.xml create mode 100644 wwwroot/test/WMS/wms_esri_2.xml diff --git a/CHANGES.md b/CHANGES.md index 47acbcbb217..047dd022636 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,10 @@ - Allow translation of TableStylingWorkflow. - Fix "Remove all" not removing selected/picked features +- WMS `isEsri` default value will now check for case-insensitive `mapserver/wmsserver` (instead of `MapServer/WMSServer`) +- Tweak ArcGis MapServer WMS `GetFeatureInfo` default behaviour + - Add `application/geo+json` and `application/vnd.geo+json` default `GetFeatureInfo` (after `application/json` in priority list) + - Add `application/xml` default `GetFeatureInfo`. (if `isEsri` is true, then this will be used before `text/html`) - [The next improvement] #### 8.3.6 - 2023-10-03 diff --git a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts index 8cb7bff5b01..32b8737cc18 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts @@ -712,7 +712,9 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( @computed get isEsri(): boolean { if (this.catalogItem.url !== undefined) - return this.catalogItem.url.indexOf("MapServer/WMSServer") > -1; + return ( + this.catalogItem.url.toLowerCase().indexOf("mapserver/wmsserver") > -1 + ); return false; } @@ -831,9 +833,11 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( } /** Prioritize format of GetFeatureInfo: - * - JSON + * - JSON/GeoJSON + * - If ESRI, then we prioritise XML next * - HTML * - GML + * - XML * - Plain text * * If no matching format can be found in GetCapabilities, then Cesium will use defaults (see `WebMapServiceImageryProvider.DefaultGetFeatureInfoFormats`) @@ -852,10 +856,23 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( if (formatsArray.includes("application/json")) return { format: "application/json", type: "json" }; + if (formatsArray.includes("application/geo+json")) + return { format: "application/geo+json", type: "json" }; + if (formatsArray.includes("application/vnd.geo+json")) + return { format: "application/vnd.geo+json", type: "json" }; + + // Special case for Esri WMS, use XML before HTML + // as HTML includes with rowbg that is hard to read + if (this.isEsri && formatsArray.includes("text/xml")) { + return { format: "text/xml", type: "xml" }; + } if (formatsArray.includes("text/html")) return { format: "text/html", type: "html" }; if (formatsArray.includes("application/vnd.ogc.gml")) return { format: "application/vnd.ogc.gml", type: "xml" }; + if (formatsArray.includes("text/xml")) { + return { format: "text/xml", type: "xml" }; + } if (formatsArray.includes("text/plain")) return { format: "text/plain", type: "text" }; } diff --git a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts index 05541195379..a037149fa86 100644 --- a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts +++ b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts @@ -836,6 +836,60 @@ describe("WebMapServiceCatalogItem", function () { .catch(done.fail); }); + it("sets isEsri from URL", async function () { + let wms: WebMapServiceCatalogItem; + const terria = new Terria(); + wms = new WebMapServiceCatalogItem("test", terria); + runInAction(() => { + wms.setTrait( + CommonStrata.definition, + "url", + "http://gaservices.ga.gov.au/site_1/services/Geomorphology_Landform_Type_WM/MapServer/WMSServer?request=GetCapabilities&service=WMS" + ); + wms.setTrait( + CommonStrata.definition, + "getCapabilitiesUrl", + "test/WMS/wms_esri.xml" + ); + wms.setTrait(CommonStrata.definition, "layers", "0"); + }); + + await wms.loadMetadata(); + + expect(wms.isEsri).toBe(true); + expect(wms.getFeatureInfoFormat.type).toBe("json"); + expect(wms.getFeatureInfoFormat.format).toBe("application/geo+json"); + }); + + it("sets isEsri from URL - and uses XML over HTML", async function () { + let wms: WebMapServiceCatalogItem; + const terria = new Terria(); + wms = new WebMapServiceCatalogItem("test", terria); + runInAction(() => { + wms.setTrait( + CommonStrata.definition, + "url", + "http://gaservices.ga.gov.au/site_1/services/Geomorphology_Landform_Type_WM/MapServer/WMSServer?request=GetCapabilities&service=WMS" + ); + wms.setTrait( + CommonStrata.definition, + "getCapabilitiesUrl", + "test/WMS/wms_esri_2.xml" + ); + wms.setTrait( + CommonStrata.definition, + "layers", + "Topographic_Maps_Index_100k" + ); + }); + + await wms.loadMetadata(); + + expect(wms.isEsri).toBe(true); + expect(wms.getFeatureInfoFormat.type).toBe("xml"); + expect(wms.getFeatureInfoFormat.format).toBe("text/xml"); + }); + describe("imageryProvider", () => { let item: WebMapServiceCatalogItem; let imageryProvider: WebMapServiceImageryProvider; diff --git a/wwwroot/test/WMS/wms_esri.xml b/wwwroot/test/WMS/wms_esri.xml new file mode 100644 index 00000000000..69855cc3b20 --- /dev/null +++ b/wwwroot/test/WMS/wms_esri.xml @@ -0,0 +1,150 @@ + + + + + WMS + WMS + + + + + + + + + + + + +
    + + + + +
    + + + +
    + + + 4096 + 4096 +
    + + + + application/vnd.ogc.wms_xml + text/xml + + + + + + + + + + image/bmp + image/jpeg + image/tiff + image/png + image/png8 + image/png24 + image/png32 + image/gif + image/svg+xml + + + + + + + + + + application/vnd.esri.wms_raw_xml + application/vnd.esri.wms_featureinfo_xml + application/vnd.ogc.wms_xml + application/geo+json + text/xml + text/html + text/plain + + + + + + + + + + application/vnd.ogc.sld+xml + + + + + + + + + + + application/vnd.ogc.se_xml + application/vnd.ogc.se_inimage + application/vnd.ogc.se_blank + text/xml + XML + + + <![CDATA[National Broadband Network - Connections by technology type – July 2020]]> + CRS:84 + EPSG:4326 + EPSG:3857 + + EPSG:102100 + + 96.807660 + 168.005803 + -43.751975 + -9.210950 + + + + + + + 0 + <![CDATA[NBN - Satellite]]> + Test Abstract + CRS:84 + EPSG:4326 + EPSG:3857 + + EPSG:102100 + + 96.807660 + 168.005803 + -43.751975 + -9.210950 + + + + + + + + + +
    diff --git a/wwwroot/test/WMS/wms_esri_2.xml b/wwwroot/test/WMS/wms_esri_2.xml new file mode 100644 index 00000000000..685f116e8f8 --- /dev/null +++ b/wwwroot/test/WMS/wms_esri_2.xml @@ -0,0 +1,328 @@ + + + + + WMS + Topographic Map Indexes + Test Abstract + + + Map Index + topographic + 250k + 100k + 1 Million + web service + Australia + + + + + + Geoscience Australia + + + + Postal +
    GPO Box 378
    + Canberra + ACT + 2601 + Australia +
    + +61 2 6249 9111 + + clientservices@ga.gov.au +
    + NONE + © Commonwealth of Australia (Geoscience Australia) 2023. This product is released under the Creative Commons Attribution 4.0 International Licence. http://creativecommons.org/licenses/by/4.0/legalcode + 4096 + 4096 +
    + + + + application/vnd.ogc.wms_xml + text/xml + + + + + + + + + + image/bmp + image/jpeg + image/tiff + image/png + image/png8 + image/png24 + image/png32 + image/gif + image/svg+xml + + + + + + + + + + application/vnd.esri.wms_raw_xml + application/vnd.esri.wms_featureinfo_xml + application/vnd.ogc.wms_xml + text/xml + text/html + text/plain + + + + + + + + + + application/vnd.ogc.sld+xml + + + + + + + + + + + application/vnd.ogc.se_xml + application/vnd.ogc.se_inimage + application/vnd.ogc.se_blank + text/xml + XML + + + Topographic Map Indexes + EPSG:4283 + EPSG:4326 + CRS:84 + EPSG:3857 + EPSG:28348 + EPSG:28349 + EPSG:28350 + EPSG:28351 + EPSG:28352 + EPSG:28353 + EPSG:28354 + EPSG:28355 + EPSG:28356 + EPSG:28357 + EPSG:28358 + EPSG:32748 + EPSG:32749 + EPSG:32750 + EPSG:32751 + EPSG:32752 + EPSG:32753 + EPSG:32754 + EPSG:32755 + EPSG:32756 + EPSG:32757 + EPSG:32758 + EPSG:3031 + EPSG:3032 + EPSG:102100 + + + 108.000001 + 155.999999 + -43.999988 + -7.999985 + + + + + + + Topographic_Maps_Index_100k + Topographic Maps Index 100k + + EPSG:4283 + EPSG:4326 + CRS:84 + EPSG:3857 + EPSG:28348 + EPSG:28349 + EPSG:28350 + EPSG:28351 + EPSG:28352 + EPSG:28353 + EPSG:28354 + EPSG:28355 + EPSG:28356 + EPSG:28357 + EPSG:28358 + EPSG:32748 + EPSG:32749 + EPSG:32750 + EPSG:32751 + EPSG:32752 + EPSG:32753 + EPSG:32754 + EPSG:32755 + EPSG:32756 + EPSG:32757 + EPSG:32758 + EPSG:3031 + EPSG:3032 + EPSG:102100 + + + 112.500009 + 154.000008 + -43.849986 + -8.999986 + + + + + + + text/html + + + + 14174107.142857 + + + Topographic_Maps_Index_250k + Topographic Maps Index 250k + + EPSG:4283 + EPSG:4326 + CRS:84 + EPSG:3857 + EPSG:28348 + EPSG:28349 + EPSG:28350 + EPSG:28351 + EPSG:28352 + EPSG:28353 + EPSG:28354 + EPSG:28355 + EPSG:28356 + EPSG:28357 + EPSG:28358 + EPSG:32748 + EPSG:32749 + EPSG:32750 + EPSG:32751 + EPSG:32752 + EPSG:32753 + EPSG:32754 + EPSG:32755 + EPSG:32756 + EPSG:32757 + EPSG:32758 + EPSG:3031 + EPSG:3032 + EPSG:102100 + + + 112.883339 + 154.100008 + -43.999985 + -8.933316 + + + + + + + text/html + + + + 566964.285714 + + + AUSTopo_Australian_Digital_Map_Series_Index_250k + AUSTopo Australian Digital Map Series Index 250k + + Test Abstract + + EPSG:4283 + EPSG:4326 + CRS:84 + EPSG:3857 + EPSG:28348 + EPSG:28349 + EPSG:28350 + EPSG:28351 + EPSG:28352 + EPSG:28353 + EPSG:28354 + EPSG:28355 + EPSG:28356 + EPSG:28357 + EPSG:28358 + EPSG:32748 + EPSG:32749 + EPSG:32750 + EPSG:32751 + EPSG:32752 + EPSG:32753 + EPSG:32754 + EPSG:32755 + EPSG:32756 + EPSG:32757 + EPSG:32758 + EPSG:3031 + EPSG:3032 + EPSG:102100 + + + 112.883339 + 154.000008 + -43.999988 + -8.999986 + + + + + + + text/html + + + + 566964.285714 + + + +
    From 2d4b00bc18f0b0df6b7b6d0173435a984263dfa7 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Wed, 11 Oct 2023 21:01:27 +1100 Subject: [PATCH 046/129] Add changes and update spec --- CHANGES.md | 1 + .../Catalog/Ows/WebMapServiceCatalogItemSpec.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 47acbcbb217..6d958bd6891 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ - Allow translation of TableStylingWorkflow. - Fix "Remove all" not removing selected/picked features +- Fix WMS `GetMap`/`GetFeatureInfo` requests not having `styles` parameter (will use empty string instead of `undefined`) - [The next improvement] #### 8.3.6 - 2023-10-03 diff --git a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts index 05541195379..2c7b4bbb999 100644 --- a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts +++ b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts @@ -153,6 +153,7 @@ describe("WebMapServiceCatalogItem", function () { expect(tileProviderResource.queryParameters.request).toBe("GetMap"); expect(tileProviderResource.queryParameters.transparent).toBeTruthy(); expect(tileProviderResource.queryParameters.format).toBe("image/png"); + expect(tileProviderResource.queryParameters.styles).toBe(""); const getFeatureInfoResource: Resource = ( mapItems[0].imageryProvider as any @@ -168,6 +169,7 @@ describe("WebMapServiceCatalogItem", function () { expect(getFeatureInfoResource.queryParameters.feature_count).toBe( terria.configParameters.defaultMaximumShownFeatureInfos + 1 ); + expect(getFeatureInfoResource.queryParameters.styles).toBe(""); expect(mapItems[0].imageryProvider.tileHeight).toBe(256); expect(mapItems[0].imageryProvider.tileWidth).toBe(256); @@ -214,6 +216,7 @@ describe("WebMapServiceCatalogItem", function () { expect(tileProviderResource.queryParameters.format).toBe("image/png"); expect(tileProviderResource.queryParameters.tiled).toBeTruthy(); expect(tileProviderResource.queryParameters.transparent).toBeTruthy(); + expect(tileProviderResource.queryParameters.styles).toBe(""); const getFeatureInfoResource: Resource = ( mapItems[0].imageryProvider as any @@ -232,6 +235,7 @@ describe("WebMapServiceCatalogItem", function () { expect(getFeatureInfoResource.queryParameters.feature_count).toBe( terria.configParameters.defaultMaximumShownFeatureInfos + 1 ); + expect(getFeatureInfoResource.queryParameters.styles).toBe(""); expect(mapItems[0].imageryProvider.tileHeight).toBe(256); expect(mapItems[0].imageryProvider.tileWidth).toBe(256); @@ -248,6 +252,7 @@ describe("WebMapServiceCatalogItem", function () { runInAction(() => { wms.setTrait("definition", "url", "test/WMS/single_metadata_url.xml"); wms.setTrait("definition", "layers", "single_period"); + wms.setTrait("definition", "styles", "jet"); wms.setTrait("definition", "parameters", { some: "thing", another: "value" @@ -284,6 +289,10 @@ describe("WebMapServiceCatalogItem", function () { expect(tileProviderResource.queryParameters.request).toBe("GetMap"); expect(tileProviderResource.queryParameters.transparent).toBeTruthy(); expect(tileProviderResource.queryParameters.format).toBe("image/png"); + expect(tileProviderResource.queryParameters.layers).toBe( + "single_period" + ); + expect(tileProviderResource.queryParameters.styles).toBe("jet"); expect(tileProviderResource.queryParameters.some).toBe("thing"); expect(tileProviderResource.queryParameters.another).toBe("value"); @@ -301,6 +310,10 @@ describe("WebMapServiceCatalogItem", function () { expect(getFeatureInfoResource.queryParameters.feature_count).toBe( terria.configParameters.defaultMaximumShownFeatureInfos + 1 ); + expect(getFeatureInfoResource.queryParameters.layers).toBe( + "single_period" + ); + expect(getFeatureInfoResource.queryParameters.styles).toBe("jet"); expect(getFeatureInfoResource.queryParameters.some).toBe("thing else"); expect(getFeatureInfoResource.queryParameters.another).toBe("value"); From 5dc00330a5859b056e34f80f6544a2ffdbe2048e Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 5 Oct 2023 15:21:45 +1000 Subject: [PATCH 047/129] Replace usages of construct --- .../Analytics/RegionDataParameterEditor.jsx | 8 +- .../Custom/Chart/BottomDockChart.jsx | 8 +- .../Custom/Chart/MomentPointsChart.jsx | 4 +- lib/ReactViews/Custom/Chart/Tooltip.jsx | 8 +- lib/ReactViews/DataCatalog/DataCatalog.jsx | 37 ++++---- .../DataCatalog/DataCatalogGroup.jsx | 7 +- lib/ReactViews/ExplorerWindow/Tabs.jsx | 4 +- .../Tabs/MyDataTab/MyDataTab.jsx | 4 +- lib/ReactViews/Generic/Dropdown.jsx | 6 +- lib/ReactViews/Map/MenuBar/MenuBar.jsx | 14 ++- .../Map/Panels/HelpPanel/HelpPanel.jsx | 7 +- .../Map/Panels/HelpPanel/StyledHtml.jsx | 90 +++++++++---------- lib/ReactViews/Mobile/MobileMenu.jsx | 8 +- .../Preview/DataPreviewSections.jsx | 4 +- lib/ReactViews/Preview/Description.jsx | 82 ++++++++--------- lib/ReactViews/Preview/MetadataTable.jsx | 4 +- lib/ReactViews/Search/Breadcrumbs.jsx | 75 ++++++++-------- lib/ReactViews/Search/SearchBoxAndResults.jsx | 43 +++++---- 18 files changed, 203 insertions(+), 210 deletions(-) diff --git a/lib/ReactViews/Analytics/RegionDataParameterEditor.jsx b/lib/ReactViews/Analytics/RegionDataParameterEditor.jsx index e9879f8075a..ff35de5c814 100644 --- a/lib/ReactViews/Analytics/RegionDataParameterEditor.jsx +++ b/lib/ReactViews/Analytics/RegionDataParameterEditor.jsx @@ -190,11 +190,7 @@ const RegionDataParameterEditor = createReactClass({ return (
      - + {this.catalogItemsWithMatchingRegion().map((catalogItem, i) => ( {this.renderItemChildren(catalogItem)} - + ))}
    ); diff --git a/lib/ReactViews/Custom/Chart/BottomDockChart.jsx b/lib/ReactViews/Custom/Chart/BottomDockChart.jsx index 9dcaf4444b8..b46ff3e545e 100644 --- a/lib/ReactViews/Custom/Chart/BottomDockChart.jsx +++ b/lib/ReactViews/Custom/Chart/BottomDockChart.jsx @@ -281,15 +281,15 @@ class Chart extends React.Component { scale={this.xScale} label={xAxis.units || (xAxis.scale === "time" && "Date")} /> - + {this.yAxes.map((y, i) => ( 1 ? y.color : defaultGridColor} offset={i * 50} /> - - + ))} + {this.yAxes.map((y, i) => ( 1 ? y.color : defaultGridColor} lineStyle={{ opacity: 0.3 }} /> - + ))} - + {this.points.map((p, i) => ( - + ))} ); } diff --git a/lib/ReactViews/Custom/Chart/Tooltip.jsx b/lib/ReactViews/Custom/Chart/Tooltip.jsx index 75e68e744cd..35288c9565c 100644 --- a/lib/ReactViews/Custom/Chart/Tooltip.jsx +++ b/lib/ReactViews/Custom/Chart/Tooltip.jsx @@ -94,13 +94,13 @@ class Tooltip extends React.Component { >
    {this.title}
    - + {this.groups.map((group) => ( 1 ? group.name : undefined} items={group.items} /> - + ))}
    @@ -119,9 +119,9 @@ class TooltipGroup extends React.PureComponent { return (
    {name &&
    {name}
    } - + {items.map((item) => ( - + ))}
    ); } diff --git a/lib/ReactViews/DataCatalog/DataCatalog.jsx b/lib/ReactViews/DataCatalog/DataCatalog.jsx index cf0fe0a197b..e0f10c0cc9f 100644 --- a/lib/ReactViews/DataCatalog/DataCatalog.jsx +++ b/lib/ReactViews/DataCatalog/DataCatalog.jsx @@ -50,24 +50,25 @@ class DataCatalog extends React.Component { /> )} - - {item !== this.props.terria.catalog.userAddedDataGroup && ( - - )} - + {items.map( + (item) => + item !== this.props.terria.catalog.userAddedDataGroup && ( + + ) + )} ); } diff --git a/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx b/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx index 95a4eb54c8b..db6135185f5 100644 --- a/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx +++ b/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx @@ -134,8 +134,8 @@ class DataCatalogGroup extends React.Component { this.props.terria )} > - {this.isOpen() && ( - + {this.isOpen() && + group.memberModels.map((item) => ( - - )} + ))} ); } diff --git a/lib/ReactViews/ExplorerWindow/Tabs.jsx b/lib/ReactViews/ExplorerWindow/Tabs.jsx index fc7ec3814cb..021414241ca 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs.jsx +++ b/lib/ReactViews/ExplorerWindow/Tabs.jsx @@ -143,7 +143,7 @@ class Tabs extends React.Component { background-color: ${(p) => p.theme.colorPrimary}; `} > - + {tabs.map((item, i) => (
  • -
    + ))}
    - + {tabs.map((tab) => (
  • -
    + ))} ); } diff --git a/lib/ReactViews/Generic/Dropdown.jsx b/lib/ReactViews/Generic/Dropdown.jsx index a5b43dc16b7..f352d1422e4 100644 --- a/lib/ReactViews/Generic/Dropdown.jsx +++ b/lib/ReactViews/Generic/Dropdown.jsx @@ -138,7 +138,7 @@ const Dropdown = createReactClass({ [isOpenStyle]: this.state.isOpen })} > - + {this.props.options.map((option, i) => (
  • @@ -164,14 +164,14 @@ const Dropdown = createReactClass({ this.props.theme.btnOption || "", { [Styles.isSelected]: option === this.props.selected } )} - onClick={() => this.select(option, index)} + onClick={() => this.select(option, i)} > {option[this.props.textProperty]}
  • -
    + ))} ); diff --git a/lib/ReactViews/Map/MenuBar/MenuBar.jsx b/lib/ReactViews/Map/MenuBar/MenuBar.jsx index 80831e80cc2..52d6ab42eeb 100644 --- a/lib/ReactViews/Map/MenuBar/MenuBar.jsx +++ b/lib/ReactViews/Map/MenuBar/MenuBar.jsx @@ -56,13 +56,12 @@ const MenuBar = observer((props) => { )} - {!viewState.useSmallScreenInterface && ( - + {!viewState.useSmallScreenInterface && + props.menuLeftItems.map((element, i) => (
  • {element}
  • -
    - )} + ))}
    @@ -103,13 +102,12 @@ const MenuBar = observer((props) => { /> - {!viewState.useSmallScreenInterface && ( - + {!viewState.useSmallScreenInterface && + menuItems.map((element, i) => (
  • {element}
  • -
    - )} + ))}
    ); diff --git a/lib/ReactViews/Map/Panels/HelpPanel/HelpPanel.jsx b/lib/ReactViews/Map/Panels/HelpPanel/HelpPanel.jsx index aa6e72ee181..6d31dba92ab 100644 --- a/lib/ReactViews/Map/Panels/HelpPanel/HelpPanel.jsx +++ b/lib/ReactViews/Map/Panels/HelpPanel/HelpPanel.jsx @@ -139,16 +139,15 @@ class HelpPanel extends React.Component { - {helpItems && ( - + {helpItems && + helpItems.map((item, i) => ( - - )} + ))} ); diff --git a/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx b/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx index 88c4fb495b3..496cd6bf9b2 100644 --- a/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx +++ b/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx @@ -20,21 +20,19 @@ const Numbers = styled(Text)` `; const renderOrderedList = function (contents) { - return ( - - - - - {i + 1} - - - - - {content} - + return contents.map((content, i) => ( + + + + {i + 1} + + - - ); + + {content} + + + )); }; export class StyledHtmlRaw extends React.Component { @@ -73,42 +71,42 @@ export class StyledHtmlRaw extends React.Component { return (
    - {content?.map && ( - - {item && ( - - {/* Either a header or paragraph tag */} - - - {item.props.children} - - - - {renderOrderedList( - item.props.children.map((point) => point.props.children) - )} - - - - {/* If it's none of the above tags, just render as + {content?.map && + content.map( + (item, i) => + item && ( + + {/* Either a header or paragraph tag */} + + + {item.props.children} + + + + {renderOrderedList( + item.props.children.map((point) => point.props.children) + )} + + + + {/* If it's none of the above tags, just render as {/* If it's none of the above tags, just render as {/* If it's none of the above tags, just render as normal html but with the same text formatting. We can style more tags as necessary */} - - {item} - - - - )} - - )} + + {item} + + + + ) + )}
    ); } diff --git a/lib/ReactViews/Mobile/MobileMenu.jsx b/lib/ReactViews/Mobile/MobileMenu.jsx index 35ed2de201c..319aabeace5 100644 --- a/lib/ReactViews/Mobile/MobileMenu.jsx +++ b/lib/ReactViews/Mobile/MobileMenu.jsx @@ -116,14 +116,14 @@ class MobileMenu extends React.Component { [Styles.mobileNavHidden]: !this.props.viewState.mobileMenuVisible })} > - + {this.props.menuLeftItems.map((menuItem) => (
    this.hideMenu()} key={menuItem ? menuItem.key : undefined} > {menuItem}
    -
    + ))}
    this.hideMenu()}>
    - + {this.props.menuItems.map((menuItem) => (
    this.hideMenu()} key={menuItem ? menuItem.key : undefined} > {menuItem}
    -
    + ))} {mapUserGuide && } {this.props.showFeedback && ( - + {this.sortInfoSections(items).map((item, i) => ( - + ))} ); } diff --git a/lib/ReactViews/Preview/Description.jsx b/lib/ReactViews/Preview/Description.jsx index 444b71a5316..c98208321b4 100644 --- a/lib/ReactViews/Preview/Description.jsx +++ b/lib/ReactViews/Preview/Description.jsx @@ -64,7 +64,7 @@ class Description extends React.Component { {metadataUrls && metadataUrls.length > 0 && ( <>

    {t("description.metadataUrls")}

    - + {metadataUrls.map((metadataUrl, i) => ( - + ))} )} @@ -183,18 +183,19 @@ class Description extends React.Component { {dataUrls && dataUrls.length > 0 && ( <>

    {t("description.dataUrl")}

    - - - - {dataUrl.type?.startsWith("wfs") && - parseCustomMarkdownToReact( - t("description.useLinkBelow", { - link: ` + {dataUrls.map((dataUrl, i) => ( + <> + + + {dataUrl.type?.startsWith("wfs") && + parseCustomMarkdownToReact( + t("description.useLinkBelow", { + link: `
    ` - }) - )} - {dataUrl.type?.startsWith("wcs") && - parseCustomMarkdownToReact( - t("description.useLinkBelow", { - link: ` + }) + )} + {dataUrl.type?.startsWith("wcs") && + parseCustomMarkdownToReact( + t("description.useLinkBelow", { + link: ` ` - }) + }) + )} + + + + p.theme.colorPrimary}; + `} + > + {dataUrl.title && ( + )} - - - - p.theme.colorPrimary}; - `} - > - {dataUrl.title && ( - - )} - {!dataUrl.title ? dataUrl.url : null} - - -
    + {!dataUrl.title ? dataUrl.url : null} + + {" "} + + ))} )} diff --git a/lib/ReactViews/Preview/MetadataTable.jsx b/lib/ReactViews/Preview/MetadataTable.jsx index 1f270582971..7ca9cbba847 100644 --- a/lib/ReactViews/Preview/MetadataTable.jsx +++ b/lib/ReactViews/Preview/MetadataTable.jsx @@ -36,7 +36,7 @@ const MetadataTable = createReactClass({ )} 0 && !isArr}> - + {keys.map((key, i) => (
    - + ))} diff --git a/lib/ReactViews/Search/Breadcrumbs.jsx b/lib/ReactViews/Search/Breadcrumbs.jsx index c863f43054d..12b464daeef 100644 --- a/lib/ReactViews/Search/Breadcrumbs.jsx +++ b/lib/ReactViews/Search/Breadcrumbs.jsx @@ -69,45 +69,46 @@ class Breadcrumbs extends React.Component { /> - {parentGroups && ( - - - {/* No link when it's the current member */} - - - {parent} - - - {/* The first and last two groups use the full name */} - = parentGroups.length - 2}> - - this.openInCatalog(ancestors.slice(i, i + 1)) - } - > - + {parentGroups && + parentGroups.map((parent, i) => ( + <> + + {/* No link when it's the current member */} + + {parent} - - - - {/* The remainder are just '..' to prevent/minimise overflowing */} - 1 && i < parentGroups.length - 2}> - - {"..."} - - - + + + {/* The first and last two groups use the full name */} + = parentGroups.length - 2}> + + this.openInCatalog(ancestors.slice(i, i + 1)) + } + > + + {parent} + + + + {/* The remainder are just '..' to prevent/minimise overflowing */} + 1 && i < parentGroups.length - 2}> + + {"..."} + + + - {i !== parentGroups.length - 1 && ( - - - {">"} - - - )} - - )} + {i !== parentGroups.length - 1 && ( + + + {">"} + + + )} + + ))} ); diff --git a/lib/ReactViews/Search/SearchBoxAndResults.jsx b/lib/ReactViews/Search/SearchBoxAndResults.jsx index 73de17a905e..2116884da06 100644 --- a/lib/ReactViews/Search/SearchBoxAndResults.jsx +++ b/lib/ReactViews/Search/SearchBoxAndResults.jsx @@ -197,28 +197,27 @@ export class SearchBoxAndResultsRaw extends React.Component { overflow-y: auto; `} > - - { - addMarker(this.props.terria, result); - result.clickAction(); - runInAction(() => { - searchState.showLocationSearchResults = false; - }); - }} - isWaitingForSearchToStart={ - searchState.isWaitingToStartLocationSearch - } - /> - + {this.props.viewState.searchState.locationSearchResults.map( + (search) => ( + { + addMarker(this.props.terria, result); + result.clickAction(); + runInAction(() => { + searchState.showLocationSearchResults = false; + }); + }} + isWaitingForSearchToStart={ + searchState.isWaitingToStartLocationSearch + } + /> + ) + )} )} From 7e53ae0852257f249ada5880a23a16b6ca5ea542 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 5 Oct 2023 16:54:36 +1000 Subject: [PATCH 048/129] Remove unused import --- lib/ReactViews/Preview/DataPreviewSections.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ReactViews/Preview/DataPreviewSections.jsx b/lib/ReactViews/Preview/DataPreviewSections.jsx index 4cae014bd95..f7f3e8277b3 100644 --- a/lib/ReactViews/Preview/DataPreviewSections.jsx +++ b/lib/ReactViews/Preview/DataPreviewSections.jsx @@ -8,7 +8,6 @@ import { withTranslation } from "react-i18next"; import isDefined from "../../Core/isDefined"; import CommonStrata from "../../Models/Definition/CommonStrata"; import Box from "../../Styled/Box"; -import { item } from "../Custom/Chart/tooltip.scss"; import Collapsible from "../Custom/Collapsible/Collapsible"; import parseCustomMarkdownToReact from "../Custom/parseCustomMarkdownToReact"; import MetadataTable from "./MetadataTable"; From 7df27d749660fc23d84e9dece4980247c7dc3eda Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 12 Oct 2023 15:46:25 +1000 Subject: [PATCH 049/129] Deprecate use of - Add unit test for CatalogGroup component Where single path or exclusive conditions replace with && Where two paths use ternary operator ? : For more paths extract out into sub render function --- lib/ReactViews/DataCatalog/CatalogGroup.jsx | 19 +- lib/ReactViews/Disclaimer.jsx | 13 +- lib/ReactViews/Generic/Dropdown.jsx | 57 +++-- .../Map/Panels/HelpPanel/StyledHtml.jsx | 69 +++--- lib/ReactViews/Mobile/MobileHeader.jsx | 76 ++++--- lib/ReactViews/Preview/DataPreview.jsx | 137 ++++++------ lib/ReactViews/Preview/DataPreviewMap.jsx | 24 +-- .../Preview/DataPreviewSections.jsx | 17 +- lib/ReactViews/Preview/Description.jsx | 200 +++++++++--------- lib/ReactViews/Preview/GroupPreview.jsx | 13 +- lib/ReactViews/Preview/MetadataTable.jsx | 73 +++---- lib/ReactViews/Search/Breadcrumbs.jsx | 69 +++--- .../customizable/ResponsiveSwitch.jsx | 13 +- .../DataCatalog/CatalogGroupSpec.tsx | 53 +++++ 14 files changed, 428 insertions(+), 405 deletions(-) create mode 100644 test/ReactViews/DataCatalog/CatalogGroupSpec.tsx diff --git a/lib/ReactViews/DataCatalog/CatalogGroup.jsx b/lib/ReactViews/DataCatalog/CatalogGroup.jsx index 8307c38d53e..4028e0f082b 100644 --- a/lib/ReactViews/DataCatalog/CatalogGroup.jsx +++ b/lib/ReactViews/DataCatalog/CatalogGroup.jsx @@ -124,21 +124,22 @@ function CatalogGroup(props) { [Styles.catalogGroupLowerLevel]: !props.topLevel })} > - - -
  • - -
  • -
    - + {props.loading && ( +
  • + +
  • + )} + {props.loading === false && + props.children.length === 0 && + props.emptyMessage && (
  • {props.emptyMessage}
  • -
    -
    + )} + {props.children} )} diff --git a/lib/ReactViews/Disclaimer.jsx b/lib/ReactViews/Disclaimer.jsx index 656ec1ef8f7..a6b4479c0f1 100644 --- a/lib/ReactViews/Disclaimer.jsx +++ b/lib/ReactViews/Disclaimer.jsx @@ -144,14 +144,11 @@ class Disclaimer extends React.Component { {disclaimerDeny} )} - - - - - - - - + {useSmallScreenInterface ? ( + + ) : ( + + )} this.confirm(disclaimer.confirmAction)} fullWidth={useSmallScreenInterface || !renderDenyButton} diff --git a/lib/ReactViews/Generic/Dropdown.jsx b/lib/ReactViews/Generic/Dropdown.jsx index f352d1422e4..8fa71647881 100644 --- a/lib/ReactViews/Generic/Dropdown.jsx +++ b/lib/ReactViews/Generic/Dropdown.jsx @@ -140,36 +140,33 @@ const Dropdown = createReactClass({ > {this.props.options.map((option, i) => (
  • - - - - {option[this.props.textProperty]} - - - - - - + {option.href ? ( + + {option[this.props.textProperty]} + + ) : ( + + )}
  • ))} diff --git a/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx b/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx index 496cd6bf9b2..f1a019e774f 100644 --- a/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx +++ b/lib/ReactViews/Map/Panels/HelpPanel/StyledHtml.jsx @@ -72,41 +72,42 @@ export class StyledHtmlRaw extends React.Component { return (
    {content?.map && - content.map( - (item, i) => - item && ( - - {/* Either a header or paragraph tag */} - - - {item.props.children} - - - - {renderOrderedList( - item.props.children.map((point) => point.props.children) - )} - - - - {/* If it's none of the above tags, just render as - {/* If it's none of the above tags, just render as - {/* If it's none of the above tags, just render as + content.map((item, i) => { + if (!item) return null; + + /* Either a header or paragraph tag */ + if (/(h[0-6]|p)/i.test(item.type)) { + return ( + + {item.props.children} + + ); + } else if (item.type === "ol") { + return ( + <> + {renderOrderedList( + item.props.children.map((point) => point.props.children) + )} + + + ); + /* If it's none of the above tags, just render as normal html but with the same text formatting. - We can style more tags as necessary */} - - {item} - - - - ) - )} + We can style more tags as necessary */ + } else { + return ( + + {item} + + ); + } + })}
    ); } diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index 0c24824cace..b95adb30105 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -126,6 +126,35 @@ class MobileHeader extends React.Component { }); } + renderSearch() { + const { t } = this.props; + + const searchState = this.props.viewState.searchState; +
    + {searchState.showMobileLocationSearch && ( + + )} + {searchState.showMobileCatalogSearch && ( + + )} +
    ; + } + render() { const searchState = this.props.viewState.searchState; const { t } = this.props; @@ -143,13 +172,9 @@ class MobileHeader extends React.Component { paddedRatio={1} backgroundColor={this.props.theme.dark} > - - + {!searchState.showMobileLocationSearch && + !searchState.showMobileCatalogSearch ? ( + <> - - -
    - - - - - - - - -
    -
    -
    + + ) : ( + this.renderSearch() + )} - - -
    -

    {previewed.name}

    - -
    -
    - -
    - -
    -
    - -
    -

    {previewed.name}

    -

    {t("preview.doesNotContainGeospatialData")}

    -
    - {/* TODO: Show a preview chart - - */} -
    - -
    -
    - +

    {previewed.name}

    + + + ); + } else if (previewed && previewed.isMappable) { + return ( +
    + +
    + ); + } else if (chartData) { +
    +

    {previewed.name}

    +

    {t("preview.doesNotContainGeospatialData")}

    +
    + {/* TODO: Show a preview chart + + */} +
    + +
    ; + } else if (previewed && CatalogFunctionMixin.isMixedInto(previewed)) { + return ( + + ); + } else if (previewed && previewed.isGroup) { + return ( +
    + +
    + ); + } else { +
    + +

    Select a dataset to see a preview

    +

    - OR -

    + -
    -
    - -
    - - ); + Go to the map + + + ; + } + } + + render() { + return
    {this.renderInner()}
    ; } renderUnloadedReference() { diff --git a/lib/ReactViews/Preview/DataPreviewMap.jsx b/lib/ReactViews/Preview/DataPreviewMap.jsx index 0886628e410..19381be68a7 100644 --- a/lib/ReactViews/Preview/DataPreviewMap.jsx +++ b/lib/ReactViews/Preview/DataPreviewMap.jsx @@ -271,20 +271,16 @@ class DataPreviewMap extends React.Component { }; return (
    - - -
    - - -
    - - - + {this.props.showMap ? ( +
    + ) : ( +
    + )} diff --git a/lib/ReactViews/Preview/DataPreviewSections.jsx b/lib/ReactViews/Preview/DataPreviewSections.jsx index f7f3e8277b3..5f8d52f88e5 100644 --- a/lib/ReactViews/Preview/DataPreviewSections.jsx +++ b/lib/ReactViews/Preview/DataPreviewSections.jsx @@ -100,16 +100,13 @@ class DataPreviewSections extends React.Component { } bodyTextProps={{ medium: true }} > - - 0}> - {renderSection(item)} - - - - - - - + {item.content?.length > 0 + ? renderSection(item) + : item.contentAsObject !== undefined && ( + + + + )} ))} diff --git a/lib/ReactViews/Preview/Description.jsx b/lib/ReactViews/Preview/Description.jsx index c98208321b4..60b36068698 100644 --- a/lib/ReactViews/Preview/Description.jsx +++ b/lib/ReactViews/Preview/Description.jsx @@ -24,6 +24,45 @@ class Description extends React.Component { t: PropTypes.func.isRequired }; + renderDescription(catalogItem) { + if (catalogItem.type === "wms") { + return ( +

    + + This is a + + WMS service + + , which generates map images on request. It can be used in GIS + software with this URL: + +

    + ); + } else if (catalogItem.type === "wfs") { + return ( +

    + + This is a + + WFS service + + , which transfers raw spatial data on request. It can be used in GIS + software with this URL: + +

    + ); + } + return null; + } + render() { const { t } = this.props; const catalogItem = this.props.item; @@ -101,97 +140,53 @@ class Description extends React.Component { {catalogItem.url && ( <>

    {catalogItem.typeName} URL

    - - -

    - - This is a - - WMS service - - , which generates map images on request. It can be used - in GIS software with this URL: - -

    -
    - -

    - - This is a - - WFS service - - , which transfers raw spatial data on request. It can be - used in GIS software with this URL: - -

    -
    -
    - - - {catalogItem.url} - - - e.target.select()} - /> - - + {this.renderDescription(catalogItem)} + + {this.props.printView ? ( + {catalogItem.url} + ) : ( + e.target.select()} + /> + )} - - -

    - {t("description.layerName")} - {(catalogItem.layers || "").split(",").length > 1 - ? "s" - : ""} - : {catalogItem.layers} -

    -
    - -

    - {t("description.typeName")} - {(catalogItem.typeNames || "").split(",").length > 1 - ? "s" - : ""} - : {catalogItem.typeNames} -

    -
    -
    + {catalogItem.type === "wms" || + (catalogItem.type === "esri-mapServer" && + defined(catalogItem.layers) && ( +

    + {t("description.layerName")} + {(catalogItem.layers || "").split(",").length > 1 + ? "s" + : ""} + : {catalogItem.layers} +

    + ))} + + {catalogItem.type === "wfs" && ( +

    + {t("description.typeName")} + {(catalogItem.typeNames || "").split(",").length > 1 + ? "s" + : ""} + : {catalogItem.typeNames} +

    + )} )} {dataUrls && dataUrls.length > 0 && ( <>

    {t("description.dataUrl")}

    - {dataUrls.map((dataUrl, i) => ( - <> - - + {dataUrls.map( + (dataUrl, i) => + (dataUrl.type?.startsWith("wfs") || + dataUrl.type?.startsWith("wcs")) && ( + <> {dataUrl.type?.startsWith("wfs") && parseCustomMarkdownToReact( t("description.useLinkBelow", { @@ -222,26 +217,25 @@ class Description extends React.Component { ` }) )} - - - - p.theme.colorPrimary}; - `} - > - {dataUrl.title && ( - - )} - {!dataUrl.title ? dataUrl.url : null} - - {" "} - - ))} + + p.theme.colorPrimary}; + `} + > + {dataUrl.title && ( + + )} + {!dataUrl.title ? dataUrl.url : null} + + {" "} + + ) + )} )} diff --git a/lib/ReactViews/Preview/GroupPreview.jsx b/lib/ReactViews/Preview/GroupPreview.jsx index 65f49d82c88..38b0c80442d 100644 --- a/lib/ReactViews/Preview/GroupPreview.jsx +++ b/lib/ReactViews/Preview/GroupPreview.jsx @@ -88,13 +88,8 @@ class GroupPreview extends React.Component { )}
    - - 0 - } - > + {this.props.previewed.description && + this.props.previewed.description.length > 0 && (

    {t("description.name")}

    {parseCustomMarkdownToReact( @@ -102,9 +97,7 @@ class GroupPreview extends React.Component { { catalogItem: this.props.previewed } )}
    -
    -
    - + )} {metadataItem.dataCustodian && ( diff --git a/lib/ReactViews/Preview/MetadataTable.jsx b/lib/ReactViews/Preview/MetadataTable.jsx index 7ca9cbba847..4dbafe69c60 100644 --- a/lib/ReactViews/Preview/MetadataTable.jsx +++ b/lib/ReactViews/Preview/MetadataTable.jsx @@ -1,9 +1,7 @@ import React from "react"; -import { isObservableArray } from "mobx"; import createReactClass from "create-react-class"; - +import { isObservableArray } from "mobx"; import PropTypes from "prop-types"; - import Styles from "./metadata-table.scss"; /** @@ -16,6 +14,31 @@ const MetadataTable = createReactClass({ metadataItem: PropTypes.object.isRequired // A MetadataItem instance. }, + renderDataCell(metadataItem, key) { + if (typeof metadataItem[key] === "object") { + return ; + } else if ( + Array.isArray(metadataItem[key]) || + isObservableArray(metadataItem[key]) + ) { + return metadataItem[key].length > 0 && isJoinable(metadataItem[key]) + ? metadataItem[key].join(", ") + : null; + } else return metadataItem[key]; + }, + + renderObjectItemRow(key, i) { + const metadataItem = this.props.metadataItem; + return ( +
    + + + + ); + }, + render() { const metadataItem = this.props.metadataItem; const keys = Object.keys(metadataItem); @@ -27,41 +50,15 @@ const MetadataTable = createReactClass({
    {key} @@ -59,7 +59,7 @@ const MetadataTable = createReactClass({
    {key} + {this.renderDataCell(metadataItem, key)} +
    - - - {metadataItem.length > 0 && isJoinable(metadataItem) && ( - - - - )} - - 0 && !isArr}> - {keys.map((key, i) => ( - - - - - ))} - - + {isArr && metadataItem.length > 0 && isJoinable(metadataItem) && ( + + + + )} + + {!isArr && + keys.length > 0 && + keys.map((key, i) => this.renderObjectItemRow(key, i))}
    {metadataItem.join(", ")}
    {key} - - - - - - {metadataItem[key].length > 0 && - isJoinable(metadataItem[key]) - ? metadataItem[key].join(", ") - : null} - - {metadataItem[key]} - -
    {metadataItem.join(", ")}
    diff --git a/lib/ReactViews/Search/Breadcrumbs.jsx b/lib/ReactViews/Search/Breadcrumbs.jsx index 12b464daeef..c13284549be 100644 --- a/lib/ReactViews/Search/Breadcrumbs.jsx +++ b/lib/ReactViews/Search/Breadcrumbs.jsx @@ -44,13 +44,47 @@ class Breadcrumbs extends React.Component { this.props.viewState.changeSearchState(""); } + renderCrumb(parent, i, parentGroups) { + const ancestors = getAncestors(this.props.previewed).map((ancestor) => + getDereferencedIfExists(ancestor) + ); + + /* No link when it's the current member */ + if (i === parentGroups.length - 1) { + return ( + + {parent} + + ); + /* The first and last two groups use the full name */ + } else if (i <= 1 || i >= parentGroups.length - 2) { + return ( + this.openInCatalog(ancestors.slice(i, i + 1))} + > + + {parent} + + + ); + /* The remainder are just '..' to prevent/minimise overflowing */ + } else if (i > 1 && i < parentGroups.length - 2) { + return ( + + {"..."} + + ); + } + + return null; + } + render() { const parentGroups = this.props.previewed ? getParentGroups(this.props.previewed) : undefined; - const ancestors = getAncestors(this.props.previewed).map((ancestor) => - getDereferencedIfExists(ancestor) - ); + return ( // Note: should it reset the text if a person deletes current search and starts a new search? ( <> - - {/* No link when it's the current member */} - - - {parent} - - - {/* The first and last two groups use the full name */} - = parentGroups.length - 2}> - - this.openInCatalog(ancestors.slice(i, i + 1)) - } - > - - {parent} - - - - {/* The remainder are just '..' to prevent/minimise overflowing */} - 1 && i < parentGroups.length - 2}> - - {"..."} - - - - + {this.renderCrumb(parent, i, parentGroups)} {i !== parentGroups.length - 1 && ( diff --git a/lib/ReactViews/StandardUserInterface/customizable/ResponsiveSwitch.jsx b/lib/ReactViews/StandardUserInterface/customizable/ResponsiveSwitch.jsx index 0952345bc37..5414458697c 100644 --- a/lib/ReactViews/StandardUserInterface/customizable/ResponsiveSwitch.jsx +++ b/lib/ReactViews/StandardUserInterface/customizable/ResponsiveSwitch.jsx @@ -9,15 +9,10 @@ import PropTypes from "prop-types"; export default (LargeScreenComponent, SmallScreenComponent) => { // eslint-disable-next-line require-jsdoc function ResponsiveSwitch(props) { - return ( - - - - - - - - + return props.smallScreen ? ( + + ) : ( + ); } diff --git a/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx b/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx new file mode 100644 index 00000000000..8aa83fb516c --- /dev/null +++ b/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import CatalogGroup from "../../../lib/ReactViews/DataCatalog/CatalogGroup"; +import Loader from "../../../lib/ReactViews/Loader"; +import { ThemeProvider } from "styled-components"; +import { terriaTheme } from "../../../lib/ReactViews/StandardUserInterface"; +import { create } from "react-test-renderer"; +import { act } from "react-dom/test-utils"; + +fdescribe("CatalogGroup", () => { + let testRenderer: ReturnType; + + describe("Loading", () => { + it("Shows loader", () => { + act(() => { + testRenderer = create( + + {}} + loading={true} + > + + ); + }); + expect(testRenderer.root.findByType(Loader)).toBeTruthy(); + expect(testRenderer.root.findAllByProps({ key: "empty" }).length).toEqual( + 0 + ); + }); + }); + describe("Empty", () => { + it("Shows empty message", () => { + act(() => { + testRenderer = create( + {}} + open={true} + emptyMessage="nothing here" + loading={false} + children={[]} + > + ); + }); + + expect( + testRenderer.root + .findAllByType("li") + .some((e) => e.children[0] === "nothing here") + ).toBe(true); + expect(testRenderer.root.findAllByType(Loader).length).toBe(0); + }); + }); +}); From 76dae0b0732c0916abd8ccc07354b37df9a275d0 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 12 Oct 2023 16:21:09 +1000 Subject: [PATCH 050/129] Remove jsx-control-flow-statements - Breadcrumbs.jsx: Fix missing key in Fragment - DataCatalogGroup.jsx: Explicit null to pass propType validation --- .babelrc | 1 - .eslintrc | 9 ++----- CHANGES.md | 1 + architecture/0001-babel-and-ts-loader.md | 2 +- buildprocess/configureWebpack.js | 1 - doc/contributing/frontend-style-guide.md | 2 -- .../DataCatalog/DataCatalogGroup.jsx | 25 ++++++++++--------- lib/ReactViews/Search/Breadcrumbs.jsx | 4 +-- package.json | 2 -- 9 files changed, 19 insertions(+), 28 deletions(-) diff --git a/.babelrc b/.babelrc index 36ddeb06e18..fcc5468eca1 100644 --- a/.babelrc +++ b/.babelrc @@ -11,7 +11,6 @@ ["@babel/typescript", { "allowNamespaces": true }] ], "plugins": [ - "babel-plugin-jsx-control-statements", "@babel/plugin-transform-modules-commonjs", ["@babel/plugin-proposal-decorators", { "legacy": true }], "@babel/plugin-proposal-class-properties", diff --git a/.eslintrc b/.eslintrc index 76596bb585b..c0d2b2cc5f3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,9 +1,5 @@ { - "extends": [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:jsx-control-statements/recommended" - ], + "extends": ["eslint:recommended", "plugin:react/recommended"], "parser": "@babel/eslint-parser", "parserOptions": { "requireConfigFile": false, @@ -19,7 +15,7 @@ "commonjs": true, "es6": true }, - "plugins": ["react", "jsx-control-statements"], + "plugins": ["react"], "globals": { "process": true }, @@ -29,7 +25,6 @@ } }, "rules": { - "jsx-control-statements/jsx-use-if-tag": 0, "react/jsx-no-undef": 0, /*Possible Errors */ diff --git a/CHANGES.md b/CHANGES.md index 490d3bb5d76..77f44582a37 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ #### next release (8.3.7) - [The next improvement] +- Remove `jsx-control-statements` dependency #### 8.3.6 - 2023-10-03 diff --git a/architecture/0001-babel-and-ts-loader.md b/architecture/0001-babel-and-ts-loader.md index 6f1688c6011..457ab66f66e 100644 --- a/architecture/0001-babel-and-ts-loader.md +++ b/architecture/0001-babel-and-ts-loader.md @@ -24,7 +24,7 @@ support for it covers everything we need to use it in terriajs, including build times. - Using _only_ ts-loader is out of the question, as we rely on tools inside the - babel ecosystem including jsx-control-statements & styled-components to name a + babel ecosystem including styled-components to name a few. - Using _only_ babel - Using babel _with_ ts-loader in `transpileOnly` mode, if there are TypeScript diff --git a/buildprocess/configureWebpack.js b/buildprocess/configureWebpack.js index 32320e1d38b..b4ca392a087 100644 --- a/buildprocess/configureWebpack.js +++ b/buildprocess/configureWebpack.js @@ -130,7 +130,6 @@ function configureWebpack( ["@babel/typescript", { allowNamespaces: true }] ], plugins: [ - "babel-plugin-jsx-control-statements", "@babel/plugin-transform-modules-commonjs", ["@babel/plugin-proposal-decorators", { legacy: true }], "@babel/plugin-proposal-class-properties", diff --git a/doc/contributing/frontend-style-guide.md b/doc/contributing/frontend-style-guide.md index 4e78d106dc4..afcd37c2eff 100644 --- a/doc/contributing/frontend-style-guide.md +++ b/doc/contributing/frontend-style-guide.md @@ -167,5 +167,3 @@ const BoxSpan: any = require("../../../Styled/Box").BoxSpan; Components written in TypeScript will not need `PropTypes` defined on them, as type errors on props will be caught at compilation rather than a runtime check. - -All jsx-control-statements should be removed when migrating a .jsx file to .tsx. diff --git a/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx b/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx index db6135185f5..626069488e0 100644 --- a/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx +++ b/lib/ReactViews/DataCatalog/DataCatalogGroup.jsx @@ -134,18 +134,19 @@ class DataCatalogGroup extends React.Component { this.props.terria )} > - {this.isOpen() && - group.memberModels.map((item) => ( - - ))} + {this.isOpen() + ? group.memberModels.map((item) => ( + + )) + : null} ); } diff --git a/lib/ReactViews/Search/Breadcrumbs.jsx b/lib/ReactViews/Search/Breadcrumbs.jsx index c13284549be..90d8c0c2e20 100644 --- a/lib/ReactViews/Search/Breadcrumbs.jsx +++ b/lib/ReactViews/Search/Breadcrumbs.jsx @@ -105,7 +105,7 @@ class Breadcrumbs extends React.Component { {parentGroups && parentGroups.map((parent, i) => ( - <> + {this.renderCrumb(parent, i, parentGroups)} {i !== parentGroups.length - 1 && ( @@ -114,7 +114,7 @@ class Breadcrumbs extends React.Component { )} - + ))}
    diff --git a/package.json b/package.json index 35fc2b61343..0b982e71840 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ "@visx/tooltip": "^2.1.0", "assimpjs": "^0.0.7", "babel-loader": "^8.2.3", - "babel-plugin-jsx-control-statements": "^4.0.0", "babel-plugin-lodash": "^3.3.4", "bottleneck": "^2.19.5", "catalog-converter": "^0.0.9", @@ -194,7 +193,6 @@ "@types/webpack": "4.41.33", "babel-plugin-styled-components": "^1.10.7", "eslint": "^7.20.0", - "eslint-plugin-jsx-control-statements": "^2.2.1", "eslint-plugin-react": "^7.19.0", "fork-ts-checker-notifier-webpack-plugin": "^6.0.0", "fork-ts-checker-webpack-plugin": "^6.0.0", From 49d259f97bd56216051313b7312cd9170beef7b0 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Tue, 17 Oct 2023 10:39:49 +1000 Subject: [PATCH 051/129] Update architecture/0001-babel-and-ts-loader.md Co-authored-by: Stephen Davies --- architecture/0001-babel-and-ts-loader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/architecture/0001-babel-and-ts-loader.md b/architecture/0001-babel-and-ts-loader.md index 457ab66f66e..4e2255ee8c1 100644 --- a/architecture/0001-babel-and-ts-loader.md +++ b/architecture/0001-babel-and-ts-loader.md @@ -24,7 +24,7 @@ support for it covers everything we need to use it in terriajs, including build times. - Using _only_ ts-loader is out of the question, as we rely on tools inside the - babel ecosystem including styled-components to name a + babel ecosystem including ~jsx-control-statements~ (removed 2023-10-16) & styled-components to name a few. - Using _only_ babel - Using babel _with_ ts-loader in `transpileOnly` mode, if there are TypeScript From 18e8eb0a6a07305766ccb2878133bccc3a290924 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Tue, 17 Oct 2023 10:47:32 +1000 Subject: [PATCH 052/129] Fix: nullish check on loading prop --- lib/ReactViews/DataCatalog/CatalogGroup.jsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/ReactViews/DataCatalog/CatalogGroup.jsx b/lib/ReactViews/DataCatalog/CatalogGroup.jsx index 4028e0f082b..ceddfb2a093 100644 --- a/lib/ReactViews/DataCatalog/CatalogGroup.jsx +++ b/lib/ReactViews/DataCatalog/CatalogGroup.jsx @@ -129,16 +129,14 @@ function CatalogGroup(props) { )} - {props.loading === false && - props.children.length === 0 && - props.emptyMessage && ( -
  • - {props.emptyMessage} -
  • - )} + {!props.loading && props.children.length === 0 && props.emptyMessage && ( +
  • + {props.emptyMessage} +
  • + )} {props.children} From d6074d6085a800023a9dceb6b28db040ce688f7d Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Mon, 23 Oct 2023 21:28:26 +1100 Subject: [PATCH 053/129] Add geojson/protomaps time-series support --- CHANGES.md | 3 + .../ProtomapsImageryProvider.ts | 90 +++++++++++-------- lib/ModelMixins/GeojsonMixin.ts | 69 +++++++++++--- lib/Table/TableStyle.ts | 33 ++++--- 4 files changed, 135 insertions(+), 60 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e3233e787d2..f382297869f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,9 @@ - Fix `WebMapServiceCatalogItem` `allowFeaturePicking` - Allow translation of TableStylingWorkflow. - Fix "Remove all" not removing selected/picked features +- Fix crash on empty GeoJSON features +- Add `tableFeatureInfoContext` support to `GeoJsonMixin.createProtomapsImageryProvider` +- Fix `GeoJsonMixin` timeline animation for lines/polygons - [The next improvement] #### 8.3.6 - 2023-10-03 diff --git a/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts b/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts index b4a7a546144..cf29e8f651a 100644 --- a/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts +++ b/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts @@ -96,6 +96,10 @@ interface Options { /** The name of the property that is a unique ID for features */ idProperty?: string; + + processPickedFeatures?: ( + features: ImageryLayerFeatureInfo[] + ) => Promise; } /** Buffer (in pixels) used when rendering (and generating - through geojson-vt) vector tiles */ @@ -288,11 +292,14 @@ export default class ProtomapsImageryProvider // Protomaps properties /** Data object from constructor options (this is transformed into `source`) */ private readonly data: ProtomapsData; - readonly maximumNativeZoom: number; private readonly labelers: Labelers; private readonly view: View | undefined; - readonly idProperty: string; + private readonly processPickedFeatures?: ( + features: ImageryLayerFeatureInfo[] + ) => Promise; + readonly maximumNativeZoom: number; + readonly idProperty: string; readonly source: Source; readonly paintRules: PaintRule[]; readonly labelRules: LabelRule[]; @@ -399,6 +406,8 @@ export default class ProtomapsImageryProvider 16, () => undefined ); + + this.processPickedFeatures = options.processPickedFeatures; } getTileCredits(x: number, y: number, level: number): Credit[] { @@ -490,6 +499,7 @@ export default class ProtomapsImageryProvider longitude: number, latitude: number ): Promise { + const featureInfos: ImageryLayerFeatureInfo[] = []; // If view is set - this means we are using actual vector tiles (that is not GeoJson object) // So we use this.view.queryFeatures if (this.view) { @@ -498,37 +508,36 @@ export default class ProtomapsImageryProvider (r) => r.dataLayer ); - return filterOutUndefined( - this.view - .queryFeatures( - CesiumMath.toDegrees(longitude), - CesiumMath.toDegrees(latitude), - level + this.view + .queryFeatures( + CesiumMath.toDegrees(longitude), + CesiumMath.toDegrees(latitude), + level + ) + .forEach((f) => { + // Only create FeatureInfo for visible features with properties + if ( + !f.feature.props || + isEmpty(f.feature.props) || + !renderedLayers.includes(f.layerName) ) - .map((f) => { - // Only create FeatureInfo for visible features with properties - if ( - !f.feature.props || - isEmpty(f.feature.props) || - !renderedLayers.includes(f.layerName) - ) - return; - - const featureInfo = new ImageryLayerFeatureInfo(); - - // Add Layer name property - featureInfo.properties = Object.assign( - { [LAYER_NAME_PROP]: f.layerName }, - f.feature.props ?? {} - ); - featureInfo.position = new Cartographic(longitude, latitude); + return; - featureInfo.configureDescriptionFromProperties(f.feature.props); - featureInfo.configureNameFromProperties(f.feature.props); + const featureInfo = new ImageryLayerFeatureInfo(); + + // Add Layer name property + featureInfo.properties = Object.assign( + { [LAYER_NAME_PROP]: f.layerName }, + f.feature.props ?? {} + ); + featureInfo.position = new Cartographic(longitude, latitude); + + featureInfo.configureDescriptionFromProperties(f.feature.props); + featureInfo.configureNameFromProperties(f.feature.props); + + featureInfos.push(featureInfo); + }); - return featureInfo; - }) - ); // No view is set and we have geoJSON object // So we pick features manually } else if ( @@ -556,12 +565,12 @@ export default class ProtomapsImageryProvider const bufferBbox = bbox(buffer); // Get array of all features - let features: Feature[] = this.source.geojsonObject.features; + let geojsonFeatures: Feature[] = this.source.geojsonObject.features; const pickedFeatures: Feature[] = []; - for (let index = 0; index < features.length; index++) { - const feature = features[index]; + for (let index = 0; index < geojsonFeatures.length; index++) { + const feature = geojsonFeatures[index]; if (!feature.bbox) { feature.bbox = bbox(feature); } @@ -591,7 +600,7 @@ export default class ProtomapsImageryProvider } // Convert pickedFeatures to ImageryLayerFeatureInfos - return pickedFeatures.map((f) => { + pickedFeatures.forEach((f) => { const featureInfo = new ImageryLayerFeatureInfo(); featureInfo.data = f; @@ -611,10 +620,15 @@ export default class ProtomapsImageryProvider featureInfo.configureDescriptionFromProperties(f.properties); featureInfo.configureNameFromProperties(f.properties); - return featureInfo; + featureInfos.push(featureInfo); }); } - return []; + + if (this.processPickedFeatures) { + return await this.processPickedFeatures(featureInfos); + } + + return featureInfos; } private clone(options?: Partial) { @@ -655,7 +669,9 @@ export default class ProtomapsImageryProvider rectangle: options?.rectangle ?? this.rectangle, credit: options?.credit ?? this.credit, paintRules: options?.paintRules ?? this.paintRules, - labelRules: options?.labelRules ?? this.labelRules + labelRules: options?.labelRules ?? this.labelRules, + processPickedFeatures: + options?.processPickedFeatures ?? this.processPickedFeatures }); } diff --git a/lib/ModelMixins/GeojsonMixin.ts b/lib/ModelMixins/GeojsonMixin.ts index 902b133fa09..6ce753bd160 100644 --- a/lib/ModelMixins/GeojsonMixin.ts +++ b/lib/ModelMixins/GeojsonMixin.ts @@ -18,21 +18,21 @@ import { action, computed, IReactionDisposer, + makeObservable, observable, onBecomeObserved, onBecomeUnobserved, + override, reaction, runInAction, - toJS, - makeObservable, - override + toJS } from "mobx"; import { createTransformer } from "mobx-utils"; import { - Feature as ProtomapsFeature, GeomType, LineSymbolizer, - PolygonSymbolizer + PolygonSymbolizer, + Feature as ProtomapsFeature } from "protomaps"; import Cartesian2 from "terriajs-cesium/Source/Core/Cartesian2"; import Cartesian3 from "terriajs-cesium/Source/Core/Cartesian3"; @@ -58,12 +58,14 @@ import PolygonGraphics from "terriajs-cesium/Source/DataSources/PolygonGraphics" import PolylineGraphics from "terriajs-cesium/Source/DataSources/PolylineGraphics"; import Property from "terriajs-cesium/Source/DataSources/Property"; import HeightReference from "terriajs-cesium/Source/Scene/HeightReference"; +import ImageryLayerFeatureInfo from "terriajs-cesium/Source/Scene/ImageryLayerFeatureInfo"; import AbstractConstructor from "../Core/AbstractConstructor"; import filterOutUndefined from "../Core/filterOutUndefined"; import formatPropertyValue from "../Core/formatPropertyValue"; import hashFromString from "../Core/hashFromString"; import isDefined from "../Core/isDefined"; import { + isJsonArray, isJsonNumber, isJsonObject, isJsonString, @@ -73,8 +75,8 @@ import { isJson } from "../Core/loadBlob"; import StandardCssColors from "../Core/StandardCssColors"; import TerriaError, { networkRequestError } from "../Core/TerriaError"; import ProtomapsImageryProvider, { - GeojsonSource, GEOJSON_SOURCE_LAYER_NAME, + GeojsonSource, ProtomapsData } from "../Map/ImageryProvider/ProtomapsImageryProvider"; import Reproject from "../Map/Vector/Reproject"; @@ -90,7 +92,7 @@ import { ViewingControl } from "../Models/ViewingControls"; import TableStylingWorkflow from "../Models/Workflows/TableStylingWorkflow"; import createLongitudeLatitudeFeaturePerRow from "../Table/createLongitudeLatitudeFeaturePerRow"; import TableAutomaticStylesStratum from "../Table/TableAutomaticStylesStratum"; -import TableStyle from "../Table/TableStyle"; +import TableStyle, { createRowGroupId } from "../Table/TableStyle"; import { isConstantStyleMap } from "../Table/TableStyleMap"; import { GeoJsonTraits } from "../Traits/TraitsClasses/GeoJsonTraits"; import { RectangleTraits } from "../Traits/TraitsClasses/MappableTraits"; @@ -100,6 +102,7 @@ import { ExportData } from "./ExportableMixin"; import FeatureInfoUrlTemplateMixin from "./FeatureInfoUrlTemplateMixin"; import { isDataSource } from "./MappableMixin"; import TableMixin from "./TableMixin"; +import { TerriaFeatureData } from "../Models/Feature/FeatureData"; export const FEATURE_ID_PROP = "_id_"; @@ -295,7 +298,7 @@ function GeoJsonMixin>(Base: T) { () => [ this.useTableStylingAndProtomaps, this.readyData, - this.currentTimeAsJulianDate, + this.currentDiscreteTimeIndex, this.activeTableStyle.timeIntervals, this.activeTableStyle.colorMap, this.activeTableStyle.pointSizeMap, @@ -502,6 +505,7 @@ function GeoJsonMixin>(Base: T) { const features = geoJsonWgs84.features; geoJsonWgs84.features = []; + let currentFeatureId = 0; for (let i = 0; i < features.length; i++) { const feature = features[i]; @@ -509,6 +513,13 @@ function GeoJsonMixin>(Base: T) { if (!isJsonObject(feature.geometry, false) || !feature.geometry.type) continue; + // Ignore features with invalid coordinates + if ( + !isJsonArray(feature.geometry.coordinates, false) || + feature.geometry.coordinates.length === 0 + ) + continue; + if (!feature.properties) { feature.properties = {}; } @@ -528,7 +539,7 @@ function GeoJsonMixin>(Base: T) { // Add feature index to FEATURE_ID_PROP ("_id_") feature property // This is used to refer to each feature in TableMixin (as row ID) const properties = feature.properties!; - properties[FEATURE_ID_PROP] = i; + properties[FEATURE_ID_PROP] = currentFeatureId; // Count features types if (feature.geometry.type === "Point") { @@ -553,11 +564,17 @@ function GeoJsonMixin>(Base: T) { } featureCounts.total++; + // Note it is important to increment currentFeatureId only if we are including the feature - as this needs to match the row ID in TableMixin (through dataColumnMajor) + currentFeatureId++; } runInAction(() => { this.featureCounts = featureCounts; - this._readyData = geoJsonWgs84; + if (featureCounts.total === 0) { + this._readyData = undefined; + } else { + this._readyData = geoJsonWgs84; + } }); if (isDefined(czmlTemplate)) { @@ -800,7 +817,37 @@ function GeoJsonMixin>(Base: T) { } // See `createPoints` for Point features - they are handled by Cesium ], - labelRules: [] + labelRules: [], + + // Process picked features to add terriaFeatureData (with rowIds) + // This is used by tableFeatureInfoContext to add time-series chart + processPickedFeatures: async (features) => { + if (!currentTimeRows) return features; + const processedFeatures: ImageryLayerFeatureInfo[] = []; + features.forEach((f) => { + const rowId = f.properties?.[FEATURE_ID_PROP]; + + if (isDefined(rowId) && currentTimeRows?.includes(rowId)) { + // To find rowIds for all features in a row group: + // re-create the rowGroupId and then look up in the activeTableStyle.rowGroups + const rowGroupId = createRowGroupId( + rowId, + this.activeTableStyle.groupByColumns + ); + const terriaFeatureData: TerriaFeatureData = { + ...f.data, + type: "terriaFeatureData", + rowIds: this.activeTableStyle.rowGroups.find( + (group) => group[0] === rowGroupId + )?.[1] + }; + f.data = terriaFeatureData; + + processedFeatures.push(f); + } + }); + return processedFeatures; + } }); provider = this.wrapImageryPickFeatures(provider); diff --git a/lib/Table/TableStyle.ts b/lib/Table/TableStyle.ts index e6d26a1b0e2..77a7bd6dc41 100644 --- a/lib/Table/TableStyle.ts +++ b/lib/Table/TableStyle.ts @@ -605,9 +605,9 @@ export default class TableStyle { return finishDates; } - /** Get rows grouped by id. Id will be calculated using idColumns, latitude/longitude columns or region column + /** Columns used in rowGroups - idColumns, latitude/longitude columns or region column */ - @computed get rowGroups() { + @computed get groupByColumns() { let groupByCols = this.idColumns; if (!groupByCols) { @@ -618,22 +618,18 @@ export default class TableStyle { } else if (this.regionColumn) groupByCols = [this.regionColumn]; } - if (!groupByCols) groupByCols = []; + return groupByCols ?? []; + } + /** Get rows grouped by id. + */ + @computed get rowGroups() { const tableRowIds = this.tableModel.rowIds; return ( Object.entries( groupBy(tableRowIds, (rowId) => - groupByCols! - .map((col) => { - // If using region column as ID - only use valid regions - if (col.type === TableColumnType.region) { - return col.valuesAsRegions.regionIds[rowId]; - } - return col.values[rowId]; - }) - .join("-") + createRowGroupId(rowId, this.groupByColumns) ) ) // Filter out bad IDs @@ -746,6 +742,19 @@ export default class TableStyle { } } +/** Create row group ID by concatenating values for columns */ +export function createRowGroupId(rowId: number, columns: TableColumn[]) { + return columns + .map((col) => { + // If using region column as ID - only use valid regions + if (col.type === TableColumnType.region) { + return col.valuesAsRegions.regionIds[rowId]; + } + return col.values[rowId]; + }) + .join("-"); +} + /** * Returns an array of sorted unique dates */ From fca4c8e511cec9356c191f709a98c4afdfceaa17 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Mon, 23 Oct 2023 21:36:05 +1100 Subject: [PATCH 054/129] add to changes --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index f382297869f..c97179ae786 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ - Fix crash on empty GeoJSON features - Add `tableFeatureInfoContext` support to `GeoJsonMixin.createProtomapsImageryProvider` - Fix `GeoJsonMixin` timeline animation for lines/polygons +- Fix bug in mismatched GeoJSON Feature `_id_` and TableMixin `rowId` - this was causing incorrect styling when using `filterByProperties` or features had `null` geometry - [The next improvement] #### 8.3.6 - 2023-10-03 From f98b8d7121a8c262d46d93d3819e1a26b7ab04a2 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Tue, 24 Oct 2023 13:52:02 +1100 Subject: [PATCH 055/129] Fix geojson splitter --- CHANGES.md | 1 + lib/Table/TableAutomaticStylesStratum.ts | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c97179ae786..b435f7cf136 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ - Add `tableFeatureInfoContext` support to `GeoJsonMixin.createProtomapsImageryProvider` - Fix `GeoJsonMixin` timeline animation for lines/polygons - Fix bug in mismatched GeoJSON Feature `_id_` and TableMixin `rowId` - this was causing incorrect styling when using `filterByProperties` or features had `null` geometry +- Fix splitter for `GeoJsonMixin` (lines and polygon features only) - [The next improvement] #### 8.3.6 - 2023-10-03 diff --git a/lib/Table/TableAutomaticStylesStratum.ts b/lib/Table/TableAutomaticStylesStratum.ts index 0e111331f89..6c4705efe4e 100644 --- a/lib/Table/TableAutomaticStylesStratum.ts +++ b/lib/Table/TableAutomaticStylesStratum.ts @@ -18,6 +18,7 @@ import TableStyleTraits from "../Traits/TraitsClasses/Table/StyleTraits"; import TableTimeStyleTraits from "../Traits/TraitsClasses/Table/TimeStyleTraits"; import TableTraits from "../Traits/TraitsClasses/Table/TableTraits"; import TableColumnType from "./TableColumnType"; +import { ImageryParts } from "../ModelMixins/MappableMixin"; const DEFAULT_ID_COLUMN = "id"; @@ -55,9 +56,7 @@ export default class TableAutomaticStylesStratum extends LoadableStratum( @computed get disableSplitter() { - return !isDefined(this.catalogItem.activeTableStyle.regionColumn) - ? true - : undefined; + return !this.catalogItem.mapItems.find(ImageryParts.is) ? true : undefined; } /** From 5154c420baf1842cf9e191b81a80bcdc96df1ca0 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Tue, 24 Oct 2023 16:28:56 +1100 Subject: [PATCH 056/129] Add tests --- .../CatalogItems/GeoJsonCatalogItemSpec.ts | 292 +++++++++++++++++- wwwroot/test/GeoJSON/empty-geoms.geojson | 124 ++++++++ .../time-based-automatic-styles.geojson | 220 ++++++++++--- 3 files changed, 577 insertions(+), 59 deletions(-) create mode 100644 wwwroot/test/GeoJSON/empty-geoms.geojson diff --git a/test/Models/Catalog/CatalogItems/GeoJsonCatalogItemSpec.ts b/test/Models/Catalog/CatalogItems/GeoJsonCatalogItemSpec.ts index a2c62a71caf..3326b0705d4 100644 --- a/test/Models/Catalog/CatalogItems/GeoJsonCatalogItemSpec.ts +++ b/test/Models/Catalog/CatalogItems/GeoJsonCatalogItemSpec.ts @@ -1,11 +1,11 @@ -import { runInAction } from "mobx"; +import { reaction, runInAction } from "mobx"; import { GeomType, LineSymbolizer, PolygonSymbolizer } from "protomaps"; import { CustomDataSource } from "terriajs-cesium"; import Cartesian2 from "terriajs-cesium/Source/Core/Cartesian2"; import Cartesian3 from "terriajs-cesium/Source/Core/Cartesian3"; -import createGuid from "terriajs-cesium/Source/Core/createGuid"; import Iso8601 from "terriajs-cesium/Source/Core/Iso8601"; import JulianDate from "terriajs-cesium/Source/Core/JulianDate"; +import createGuid from "terriajs-cesium/Source/Core/createGuid"; import Entity from "terriajs-cesium/Source/DataSources/Entity"; import GeoJsonDataSource from "terriajs-cesium/Source/DataSources/GeoJsonDataSource"; import HeightReference from "terriajs-cesium/Source/Scene/HeightReference"; @@ -20,12 +20,19 @@ import { FEATURE_ID_PROP, getColor } from "../../../../lib/ModelMixins/GeojsonMixin"; -import { isDataSource } from "../../../../lib/ModelMixins/MappableMixin"; +import { + ImageryParts, + isDataSource +} from "../../../../lib/ModelMixins/MappableMixin"; import GeoJsonCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/GeoJsonCatalogItem"; import SplitItemReference from "../../../../lib/Models/Catalog/CatalogReferences/SplitItemReference"; import CommonStrata from "../../../../lib/Models/Definition/CommonStrata"; import updateModelFromJson from "../../../../lib/Models/Definition/updateModelFromJson"; import TerriaFeature from "../../../../lib/Models/Feature/Feature"; +import { + TerriaFeatureData, + isTerriaFeatureData +} from "../../../../lib/Models/Feature/FeatureData"; import Terria from "../../../../lib/Models/Terria"; describe("GeoJsonCatalogItemSpec", () => { @@ -497,9 +504,7 @@ describe("GeoJsonCatalogItemSpec", () => { expect(entities.length).toEqual(1); const entity1 = entities[0]; - console.log( - entity1.properties?.getValue(terria.timelineClock.currentTime).year - ); + expect( entity1.properties?.getValue(terria.timelineClock.currentTime).year ).toBe(2019); @@ -823,6 +828,8 @@ describe("GeoJsonCatalogItemSpec", () => { ?.getValue(terria.timelineClock.currentTime) ?.toCssColorString() ).toBe("rgb(103,0,13)"); + + expect(geojson.disableSplitter).toBeTruthy(); }); it("Supports LegendOwnerTraits to override TableMixin.legends", async () => { @@ -922,12 +929,13 @@ describe("GeoJsonCatalogItemSpec", () => { geojson.setTrait( CommonStrata.user, "geoJsonString", - `{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"stroke":"#555555","stroke-width":2,"stroke-opacity":1,"fill":"#ff0051","fill-opacity":0.5},"geometry":{"type":"Polygon","coordinates":[[[35.859375,53.54030739150022],[11.25,40.17887331434696],[15.1171875,14.604847155053898],[53.4375,44.84029065139799],[35.859375,53.54030739150022]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[85.4296875,66.93006025862448],[53.4375,43.83452678223682],[89.296875,34.88593094075317],[91.40625,50.958426723359935],[85.4296875,66.93006025862448]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[119.17968749999999,66.79190947341796],[100.1953125,53.74871079689897],[109.3359375,47.517200697839414],[119.17968749999999,66.79190947341796]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[30.585937499999996,-2.108898659243126]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[71.015625,-2.811371193331128],[99.49218749999999,-2.811371193331128],[99.49218749999999,18.646245142670608],[71.015625,18.646245142670608],[71.015625,-2.811371193331128]]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[140.9765625,19.642587534013032],[134.296875,-17.978733095556155],[88.9453125,-36.597889133070204],[119.53125,15.961329081596647],[130.078125,27.371767300523047]]}}]}` + `{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"stroke":"#555555","stroke-width":2,"stroke-opacity":1,"fill":"#ff0051","fill-opacity":0.5},"geometry":{"type":"Polygon","coordinates":[[[35.859375,53.54030739150022],[11.25,40.17887331434696],[15.1171875,14.604847155053898],[53.4375,44.84029065139799],[35.859375,53.54030739150022]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[85.4296875,66.93006025862448],[53.4375,43.83452678223682],[89.296875,34.88593094075317],[91.40625,50.958426723359935],[85.4296875,66.93006025862448]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[119.17968749999999,66.79190947341796],[100.1953125,53.74871079689897],[109.3359375,47.517200697839414],[119.17968749999999,66.79190947341796]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[71.015625,-2.811371193331128],[99.49218749999999,-2.811371193331128],[99.49218749999999,18.646245142670608],[71.015625,18.646245142670608],[71.015625,-2.811371193331128]]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[140.9765625,19.642587534013032],[134.296875,-17.978733095556155],[88.9453125,-36.597889133070204],[119.53125,15.961329081596647],[130.078125,27.371767300523047]]}}]}` ); await geojson.loadMapItems(); expect(geojson.mapItems[0] instanceof GeoJsonDataSource).toBeFalsy(); expect(geojson.useTableStylingAndProtomaps).toBeTruthy(); expect(geojson.legends.length).toBe(1); + expect(geojson.disableSplitter).toBeFalsy(); }); it("Disabled protomaps - More than 50% features detected", async () => { @@ -942,6 +950,143 @@ describe("GeoJsonCatalogItemSpec", () => { expect(geojson.useTableStylingAndProtomaps).toBeFalsy(); expect(geojson.legends.length).toBe(0); + expect(geojson.disableSplitter).toBeTruthy(); + }); + + it("correctly matches feature _id_ with table rowId - with features with empty geoms", async () => { + geojson.setTrait( + CommonStrata.user, + "url", + "test/GeoJSON/empty-geoms.geojson" + ); + + await geojson.loadMapItems(); + expect(geojson.readyData?.features.length).toBe(4); + // Check _id_ vs rowIds + expect( + geojson.readyData?.features.map((f) => f.properties?.[FEATURE_ID_PROP]) + ).toEqual(geojson.rowIds); + // Check "someOtherProp" column + expect( + geojson.readyData?.features.map((f) => f.properties?.someOtherProp) + ).toEqual( + geojson.tableColumns.find((c) => c.name === "someOtherProp") + ?.values as string[] + ); + }); + + it("correctly matches feature _id_ with table rowId - with filterByProperties", async () => { + geojson.setTrait( + CommonStrata.user, + "url", + "test/GeoJSON/time-based.geojson" + ); + geojson.setTrait(CommonStrata.user, "filterByProperties", { + year: 2019 + }); + await geojson.loadMapItems(); + + expect(geojson.readyData?.features.length).toBe(1); + expect( + geojson.readyData?.features.map((f) => f.properties?.[FEATURE_ID_PROP]) + ).toEqual(geojson.rowIds); + }); + + it("supports time", async function () { + geojson.setTrait( + CommonStrata.definition, + "url", + "test/GeoJSON/time-based-automatic-styles.geojson" + ); + + updateModelFromJson(geojson, CommonStrata.definition, { + currentTime: "2018-01-01", + defaultStyle: { + time: { timeColumn: "date", idColumns: ["idProperty"] } + } + }); + + const observeMapItems = reaction( + () => [geojson.mapItems], + () => {} + ); + + (await geojson.loadMapItems()).throwIfError(); + + expect(geojson.activeTableStyle.timeColumn?.name).toBe("date"); + + const firstProtomapsImageryProvider = + "imageryProvider" in geojson.mapItems[0] + ? (geojson.mapItems[0].imageryProvider as ProtomapsImageryProvider) + : undefined; + + if (!firstProtomapsImageryProvider) throw "protomaps should be defined"; + + const testFeature = { + props: {}, + geomType: GeomType.Polygon, + numVertices: 0, + geom: [], + bbox: { minX: 0, minY: 0, maxX: 0, maxY: 0 } + }; + + const firstFilter = firstProtomapsImageryProvider.paintRules[0].filter; + + if (!firstFilter) { + throw "filter should be defined"; + } + + // Current time is 2018-01-01 + // First feature maps to 2018-01-01 + testFeature.props = { [FEATURE_ID_PROP]: 0 }; + expect(firstFilter(0, testFeature)).toBeTruthy(); + + // Second feature maps to 2019-01-01 + testFeature.props = { [FEATURE_ID_PROP]: 1 }; + expect(firstFilter(0, testFeature)).toBeFalsy(); + + // Change time to 2019-01-01 + geojson.setTrait(CommonStrata.definition, "currentTime", "2019-01-01"); + + // Check new imagery provider + const nextProtomapsImageryProvider = + "imageryProvider" in geojson.mapItems[0] + ? (geojson.mapItems[0].imageryProvider as ProtomapsImageryProvider) + : undefined; + + if (!nextProtomapsImageryProvider) throw "protomaps should be defined"; + + const nextFilter = nextProtomapsImageryProvider.paintRules[0].filter; + + if (!nextFilter) { + throw "filter should be defined"; + } + + testFeature.props = { [FEATURE_ID_PROP]: 0 }; + expect(nextFilter(0, testFeature)).toBeFalsy(); + testFeature.props = { [FEATURE_ID_PROP]: 1 }; + expect(nextFilter(0, testFeature)).toBeTruthy(); + + expect( + firstProtomapsImageryProvider === nextProtomapsImageryProvider + ).toBeFalsy(); + + // Now change the currentTime to 2019- g01-02 - this should not trigger a new imagery provider - as it within the current time interval + geojson.setTrait(CommonStrata.definition, "currentTime", "2019-01-02"); + + // Check new imagery provider + const lastProtomapsImageryProvider = + "imageryProvider" in geojson.mapItems[0] + ? (geojson.mapItems[0].imageryProvider as ProtomapsImageryProvider) + : undefined; + + if (!lastProtomapsImageryProvider) throw "protomaps should be defined"; + + expect( + nextProtomapsImageryProvider === lastProtomapsImageryProvider + ).toBeTruthy(); + + observeMapItems(); }); }); @@ -986,12 +1131,14 @@ describe("GeoJsonCatalogItemSpec", () => { geojson = new GeoJsonCatalogItem("test-geojson", terria); }); - it("protomaps-mvt", async function () { + it("protomaps-mvt - polygons/lines", async function () { terria.addModel(geojson); - const geojsonString = await loadText("test/GeoJSON/cemeteries.geojson"); + const geojsonString = await loadText("test/GeoJSON/time-based.geojson"); geojson.setTrait(CommonStrata.user, "geoJsonString", geojsonString); await geojson.loadMapItems(); + expect(geojson.disableSplitter).toBeFalsy(); + const split = new SplitItemReference(createGuid(), terria); split.setTrait( CommonStrata.definition, @@ -1009,6 +1156,15 @@ describe("GeoJsonCatalogItemSpec", () => { (await (split.target as GeoJsonCatalogItem).loadMapItems()).error ).toBeUndefined(); }); + + it("cesium - points - splitter disabled", async function () { + terria.addModel(geojson); + const geojsonString = await loadText("test/GeoJSON/cemeteries.geojson"); + geojson.setTrait(CommonStrata.user, "geoJsonString", geojsonString); + await geojson.loadMapItems(); + + expect(geojson.disableSplitter).toBeTruthy(); + }); }); describe("geojson handles reprojection", function () { @@ -1218,5 +1374,123 @@ describe("GeoJsonCatalogItemSpec", () => { throw "Invalid geojson.mapItems"; } }); + + it("ProtomapsImageryProvider pickFeatures", async function () { + const geojsonData = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: {}, + geometry: { + type: "Polygon", + coordinates: [ + [ + [145.5908203125, -40.17887331434695], + [143.349609375, -42.08191667830631], + [146.35986328124997, -44.040218713142124], + [149.08447265625, -42.859859815062784], + [148.55712890625, -41.36031866306708], + [145.5908203125, -40.17887331434695] + ] + ] + } + }, + { + type: "Feature", + properties: {}, + geometry: { + type: "Polygon", + coordinates: [ + [ + [75.9375, 51.069016659603896], + [59.94140624999999, 39.095962936305476], + [79.453125, 42.032974332441405], + [80.15625, 46.800059446787316], + [75.673828125, 51.45400691005982], + [75.9375, 51.069016659603896] + ] + ] + } + } + ] + }; + geojson.setTrait( + CommonStrata.definition, + "geoJsonString", + JSON.stringify(geojsonData) + ); + + (await geojson.loadMapItems()).throwIfError(); + + const imagery = geojson.mapItems[0] as ImageryParts; + + expect( + imagery.imageryProvider instanceof ProtomapsImageryProvider + ).toBeTruthy(); + + const spyOnProcessPickedFeatures = spyOn( + imagery.imageryProvider, + "pickFeatures" + ).and.callThrough(); + + const features = + (await imagery.imageryProvider.pickFeatures( + 1, + 1, + 3, + 1.2946797849754814, + 0.7826107094181278 + )) ?? []; + + expect(spyOnProcessPickedFeatures).toHaveBeenCalledTimes(1); + expect(features.length).toBe(1); + expect(features[0].data.geometry).toEqual( + geojsonData.features[1].geometry + ); + }); + + it("ProtomapsImageryProvider pickFeatures - with time", async function () { + geojson.setTrait( + CommonStrata.definition, + "url", + "test/GeoJSON/time-based-automatic-styles.geojson" + ); + + updateModelFromJson(geojson, CommonStrata.definition, { + defaultStyle: { + time: { timeColumn: "date", idColumns: ["idProperty"] } + } + }); + + (await geojson.loadMapItems()).throwIfError(); + + const imagery = geojson.mapItems[0] as ImageryParts; + + expect( + imagery.imageryProvider instanceof ProtomapsImageryProvider + ).toBeTruthy(); + + const spyOnProcessPickedFeatures = spyOn( + imagery.imageryProvider, + "pickFeatures" + ).and.callThrough(); + + const features = + (await imagery.imageryProvider.pickFeatures( + 59166, + 40202, + 16, + 2.5309053894540012, + -0.6590723957845167 + )) ?? []; + + expect(spyOnProcessPickedFeatures).toHaveBeenCalledTimes(1); + expect(features.length).toBe(1); + expect(isTerriaFeatureData(features[0].data)).toBeTruthy(); + + const terriaFeatureData = features[0].data as TerriaFeatureData; + expect(terriaFeatureData.rowIds).toEqual([4, 5, 6, 7, 8]); + }); }); }); diff --git a/wwwroot/test/GeoJSON/empty-geoms.geojson b/wwwroot/test/GeoJSON/empty-geoms.geojson new file mode 100644 index 00000000000..ea0050ec794 --- /dev/null +++ b/wwwroot/test/GeoJSON/empty-geoms.geojson @@ -0,0 +1,124 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "height": 10, + "radius": 10, + "someOtherProp": "what" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 144.91734981536865, + -37.824700770115996 + ] + } + }, + { + "type": "Feature", + "properties": { + "height": 20, + "radius": 5, + "someOtherProp": "ok" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 144.92305755615234, + -37.82453127776299 + ] + } + }, + { + "type": "Feature", + "properties": { + "someProperty": 10, + "someOtherProp": "hey" + }, + "geometry": { + "type": "Polygon", + "coordinates": [] + } + }, + { + "type": "Feature", + "properties": { + "someProperty": 10, + "someOtherProp": "yo" + }, + "geometry": null + }, + { + "type": "Feature", + "properties": { + "someProperty": 20, + "someOtherProp": "what" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 145.0100040435791, + -37.76080849651723 + ], + [ + 145.00873804092407, + -37.76342088777352 + ], + [ + 145.0157332420349, + -37.76292895101701 + ], + [ + 145.0100040435791, + -37.76080849651723 + ] + ] + ] + } + }, + { + "type": "Feature", + "bbox": [ + -10.0, + -10.0, + 10.0, + 10.0 + ], + "properties": { + "foo": "hi", + "bar": "bye", + "stroke-width": 1, + "someOtherProp": "is" + }, + "geometry": { + "type": "MultiLineString", + "coordinates": [ + [ + [ + 100.0, + 0.0 + ], + [ + 101.0, + 1.0 + ] + ], + [ + [ + 102.0, + 2.0 + ], + [ + 103.0, + 3.0 + ] + ] + ] + } + } + ] +} \ No newline at end of file diff --git a/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson b/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson index cda41a1888a..4e9d8791a9d 100644 --- a/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson +++ b/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson @@ -6,17 +6,32 @@ "properties": { "date": 2018, "someProperty": 3, - "id": 0 + "idProperty": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0130295753479, -37.77042639061412], - [145.0200891494751, -37.77042639061412], - [145.0200891494751, -37.76543949054887], - [145.0130295753479, -37.76543949054887], - [145.0130295753479, -37.77042639061412] + [ + 145.0130295753479, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.77042639061412 + ] ] ] } @@ -26,17 +41,32 @@ "properties": { "date": 2019, "someProperty": 6, - "id": 0 + "idProperty": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0130295753479, -37.77042639061412], - [145.0200891494751, -37.77042639061412], - [145.0200891494751, -37.76543949054887], - [145.0130295753479, -37.76543949054887], - [145.0130295753479, -37.77042639061412] + [ + 145.0130295753479, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.77042639061412 + ] ] ] } @@ -46,17 +76,32 @@ "properties": { "date": 2020, "someProperty": 10, - "id": 0 + "idProperty": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0130295753479, -37.77042639061412], - [145.0200891494751, -37.77042639061412], - [145.0200891494751, -37.76543949054887], - [145.0130295753479, -37.76543949054887], - [145.0130295753479, -37.77042639061412] + [ + 145.0130295753479, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.77042639061412 + ] ] ] } @@ -66,17 +111,32 @@ "properties": { "date": 2021, "someProperty": 0, - "id": 0 + "idProperty": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0130295753479, -37.77042639061412], - [145.0200891494751, -37.77042639061412], - [145.0200891494751, -37.76543949054887], - [145.0130295753479, -37.76543949054887], - [145.0130295753479, -37.77042639061412] + [ + 145.0130295753479, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.77042639061412 + ], + [ + 145.0200891494751, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.76543949054887 + ], + [ + 145.0130295753479, + -37.77042639061412 + ] ] ] } @@ -86,16 +146,28 @@ "properties": { "date": 2018, "someProperty": 3, - "id": 1 + "idProperty": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0100040435791, -37.76080849651723], - [145.00873804092407, -37.76342088777352], - [145.0157332420349, -37.76292895101701], - [145.0100040435791, -37.76080849651723] + [ + 145.0100040435791, + -37.76080849651723 + ], + [ + 145.00873804092407, + -37.76342088777352 + ], + [ + 145.0157332420349, + -37.76292895101701 + ], + [ + 145.0100040435791, + -37.76080849651723 + ] ] ] } @@ -105,16 +177,28 @@ "properties": { "date": 2019, "someProperty": 4, - "id": 1 + "idProperty": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0100040435791, -37.76080849651723], - [145.00873804092407, -37.76342088777352], - [145.0157332420349, -37.76292895101701], - [145.0100040435791, -37.76080849651723] + [ + 145.0100040435791, + -37.76080849651723 + ], + [ + 145.00873804092407, + -37.76342088777352 + ], + [ + 145.0157332420349, + -37.76292895101701 + ], + [ + 145.0100040435791, + -37.76080849651723 + ] ] ] } @@ -124,16 +208,28 @@ "properties": { "date": 2020, "someProperty": 1, - "id": 1 + "idProperty": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0100040435791, -37.76080849651723], - [145.00873804092407, -37.76342088777352], - [145.0157332420349, -37.76292895101701], - [145.0100040435791, -37.76080849651723] + [ + 145.0100040435791, + -37.76080849651723 + ], + [ + 145.00873804092407, + -37.76342088777352 + ], + [ + 145.0157332420349, + -37.76292895101701 + ], + [ + 145.0100040435791, + -37.76080849651723 + ] ] ] } @@ -143,16 +239,28 @@ "properties": { "date": 2021, "someProperty": 10, - "id": 1 + "idProperty": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0100040435791, -37.76080849651723], - [145.00873804092407, -37.76342088777352], - [145.0157332420349, -37.76292895101701], - [145.0100040435791, -37.76080849651723] + [ + 145.0100040435791, + -37.76080849651723 + ], + [ + 145.00873804092407, + -37.76342088777352 + ], + [ + 145.0157332420349, + -37.76292895101701 + ], + [ + 145.0100040435791, + -37.76080849651723 + ] ] ] } @@ -162,19 +270,31 @@ "properties": { "date": 2022, "someProperty": 7, - "id": 1 + "idProperty": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ - [145.0100040435791, -37.76080849651723], - [145.00873804092407, -37.76342088777352], - [145.0157332420349, -37.76292895101701], - [145.0100040435791, -37.76080849651723] + [ + 145.0100040435791, + -37.76080849651723 + ], + [ + 145.00873804092407, + -37.76342088777352 + ], + [ + 145.0157332420349, + -37.76292895101701 + ], + [ + 145.0100040435791, + -37.76080849651723 + ] ] ] } } ] -} +} \ No newline at end of file From 735b1bda646f672edc5823c7000496816c148909 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Tue, 24 Oct 2023 16:34:30 +1100 Subject: [PATCH 057/129] prettier --- wwwroot/test/GeoJSON/empty-geoms.geojson | 59 ++--- .../time-based-automatic-styles.geojson | 202 ++++-------------- 2 files changed, 53 insertions(+), 208 deletions(-) diff --git a/wwwroot/test/GeoJSON/empty-geoms.geojson b/wwwroot/test/GeoJSON/empty-geoms.geojson index ea0050ec794..f327c7ae095 100644 --- a/wwwroot/test/GeoJSON/empty-geoms.geojson +++ b/wwwroot/test/GeoJSON/empty-geoms.geojson @@ -10,10 +10,7 @@ }, "geometry": { "type": "Point", - "coordinates": [ - 144.91734981536865, - -37.824700770115996 - ] + "coordinates": [144.91734981536865, -37.824700770115996] } }, { @@ -25,10 +22,7 @@ }, "geometry": { "type": "Point", - "coordinates": [ - 144.92305755615234, - -37.82453127776299 - ] + "coordinates": [144.92305755615234, -37.82453127776299] } }, { @@ -60,34 +54,17 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0100040435791, - -37.76080849651723 - ], - [ - 145.00873804092407, - -37.76342088777352 - ], - [ - 145.0157332420349, - -37.76292895101701 - ], - [ - 145.0100040435791, - -37.76080849651723 - ] + [145.0100040435791, -37.76080849651723], + [145.00873804092407, -37.76342088777352], + [145.0157332420349, -37.76292895101701], + [145.0100040435791, -37.76080849651723] ] ] } }, { "type": "Feature", - "bbox": [ - -10.0, - -10.0, - 10.0, - 10.0 - ], + "bbox": [-10.0, -10.0, 10.0, 10.0], "properties": { "foo": "hi", "bar": "bye", @@ -98,27 +75,15 @@ "type": "MultiLineString", "coordinates": [ [ - [ - 100.0, - 0.0 - ], - [ - 101.0, - 1.0 - ] + [100.0, 0.0], + [101.0, 1.0] ], [ - [ - 102.0, - 2.0 - ], - [ - 103.0, - 3.0 - ] + [102.0, 2.0], + [103.0, 3.0] ] ] } } ] -} \ No newline at end of file +} diff --git a/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson b/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson index 4e9d8791a9d..8cd654ffe39 100644 --- a/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson +++ b/wwwroot/test/GeoJSON/time-based-automatic-styles.geojson @@ -12,26 +12,11 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0130295753479, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.77042639061412 - ] + [145.0130295753479, -37.77042639061412], + [145.0200891494751, -37.77042639061412], + [145.0200891494751, -37.76543949054887], + [145.0130295753479, -37.76543949054887], + [145.0130295753479, -37.77042639061412] ] ] } @@ -47,26 +32,11 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0130295753479, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.77042639061412 - ] + [145.0130295753479, -37.77042639061412], + [145.0200891494751, -37.77042639061412], + [145.0200891494751, -37.76543949054887], + [145.0130295753479, -37.76543949054887], + [145.0130295753479, -37.77042639061412] ] ] } @@ -82,26 +52,11 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0130295753479, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.77042639061412 - ] + [145.0130295753479, -37.77042639061412], + [145.0200891494751, -37.77042639061412], + [145.0200891494751, -37.76543949054887], + [145.0130295753479, -37.76543949054887], + [145.0130295753479, -37.77042639061412] ] ] } @@ -117,26 +72,11 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0130295753479, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.77042639061412 - ], - [ - 145.0200891494751, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.76543949054887 - ], - [ - 145.0130295753479, - -37.77042639061412 - ] + [145.0130295753479, -37.77042639061412], + [145.0200891494751, -37.77042639061412], + [145.0200891494751, -37.76543949054887], + [145.0130295753479, -37.76543949054887], + [145.0130295753479, -37.77042639061412] ] ] } @@ -152,22 +92,10 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0100040435791, - -37.76080849651723 - ], - [ - 145.00873804092407, - -37.76342088777352 - ], - [ - 145.0157332420349, - -37.76292895101701 - ], - [ - 145.0100040435791, - -37.76080849651723 - ] + [145.0100040435791, -37.76080849651723], + [145.00873804092407, -37.76342088777352], + [145.0157332420349, -37.76292895101701], + [145.0100040435791, -37.76080849651723] ] ] } @@ -183,22 +111,10 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0100040435791, - -37.76080849651723 - ], - [ - 145.00873804092407, - -37.76342088777352 - ], - [ - 145.0157332420349, - -37.76292895101701 - ], - [ - 145.0100040435791, - -37.76080849651723 - ] + [145.0100040435791, -37.76080849651723], + [145.00873804092407, -37.76342088777352], + [145.0157332420349, -37.76292895101701], + [145.0100040435791, -37.76080849651723] ] ] } @@ -214,22 +130,10 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0100040435791, - -37.76080849651723 - ], - [ - 145.00873804092407, - -37.76342088777352 - ], - [ - 145.0157332420349, - -37.76292895101701 - ], - [ - 145.0100040435791, - -37.76080849651723 - ] + [145.0100040435791, -37.76080849651723], + [145.00873804092407, -37.76342088777352], + [145.0157332420349, -37.76292895101701], + [145.0100040435791, -37.76080849651723] ] ] } @@ -245,22 +149,10 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0100040435791, - -37.76080849651723 - ], - [ - 145.00873804092407, - -37.76342088777352 - ], - [ - 145.0157332420349, - -37.76292895101701 - ], - [ - 145.0100040435791, - -37.76080849651723 - ] + [145.0100040435791, -37.76080849651723], + [145.00873804092407, -37.76342088777352], + [145.0157332420349, -37.76292895101701], + [145.0100040435791, -37.76080849651723] ] ] } @@ -276,25 +168,13 @@ "type": "Polygon", "coordinates": [ [ - [ - 145.0100040435791, - -37.76080849651723 - ], - [ - 145.00873804092407, - -37.76342088777352 - ], - [ - 145.0157332420349, - -37.76292895101701 - ], - [ - 145.0100040435791, - -37.76080849651723 - ] + [145.0100040435791, -37.76080849651723], + [145.00873804092407, -37.76342088777352], + [145.0157332420349, -37.76292895101701], + [145.0100040435791, -37.76080849651723] ] ] } } ] -} \ No newline at end of file +} From 4b61b00d0d0c37587a20d3514fd80037dad0e5fa Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Tue, 24 Oct 2023 16:44:53 +1100 Subject: [PATCH 058/129] replace currentDiscreteTimeIndex with currentDiscreteJulianDate --- lib/ModelMixins/GeojsonMixin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ModelMixins/GeojsonMixin.ts b/lib/ModelMixins/GeojsonMixin.ts index 6ce753bd160..df72d0fe261 100644 --- a/lib/ModelMixins/GeojsonMixin.ts +++ b/lib/ModelMixins/GeojsonMixin.ts @@ -298,7 +298,7 @@ function GeoJsonMixin>(Base: T) { () => [ this.useTableStylingAndProtomaps, this.readyData, - this.currentDiscreteTimeIndex, + this.currentDiscreteJulianDate, this.activeTableStyle.timeIntervals, this.activeTableStyle.colorMap, this.activeTableStyle.pointSizeMap, From 728840214f62e3684286efca7dbaf0878500b560 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Tue, 24 Oct 2023 17:51:50 +1100 Subject: [PATCH 059/129] Fix share links with picked features for protomaps --- CHANGES.md | 1 + .../ProtomapsImageryProvider.ts | 29 ++++++++++++------- lib/ModelMixins/GeojsonMixin.ts | 4 ++- .../MapboxVectorTileCatalogItem.ts | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b435f7cf136..1ee97136874 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ - Fix `GeoJsonMixin` timeline animation for lines/polygons - Fix bug in mismatched GeoJSON Feature `_id_` and TableMixin `rowId` - this was causing incorrect styling when using `filterByProperties` or features had `null` geometry - Fix splitter for `GeoJsonMixin` (lines and polygon features only) +- Fix share links with picked features from `ProtomapsImageryProvider` - [The next improvement] #### 8.3.6 - 2023-10-03 diff --git a/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts b/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts index cf29e8f651a..20fd81084c6 100644 --- a/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts +++ b/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts @@ -5,36 +5,35 @@ import circle from "@turf/circle"; import { Feature } from "@turf/helpers"; import i18next from "i18next"; import { cloneDeep, isEmpty } from "lodash-es"; -import { action, observable, runInAction, makeObservable } from "mobx"; +import { action, makeObservable, observable, runInAction } from "mobx"; import { Bbox, - Feature as ProtomapsFeature, GeomType, - Labelers, LabelRule, + Labelers, LineSymbolizer, - painter, + Rule as PaintRule, PmtilesSource, PreparedTile, - Rule as PaintRule, + Feature as ProtomapsFeature, TileCache, TileSource, View, Zxy, - ZxySource + ZxySource, + painter } from "protomaps"; import Cartographic from "terriajs-cesium/Source/Core/Cartographic"; import Credit from "terriajs-cesium/Source/Core/Credit"; -import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; import CesiumEvent from "terriajs-cesium/Source/Core/Event"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import WebMercatorTilingScheme from "terriajs-cesium/Source/Core/WebMercatorTilingScheme"; +import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; import ImageryLayerFeatureInfo from "terriajs-cesium/Source/Scene/ImageryLayerFeatureInfo"; -import filterOutUndefined from "../../Core/filterOutUndefined"; -import isDefined from "../../Core/isDefined"; import TerriaError from "../../Core/TerriaError"; +import isDefined from "../../Core/isDefined"; import { FeatureCollectionWithCrs, FEATURE_ID_PROP as GEOJSON_FEATURE_ID_PROP, @@ -85,6 +84,8 @@ export type ProtomapsData = string | FeatureCollectionWithCrs | Source; interface Options { terria: Terria; + /** This must be defined to support pickedFeatures in share links */ + id?: string; data: ProtomapsData; minimumZoom?: number; maximumZoom?: number; @@ -272,6 +273,10 @@ export default class ProtomapsImageryProvider readonly errorEvent = new CesiumEvent(); readonly ready = true; readonly credit: Credit; + /** This is only used for Terria feature picking - as we track ImageryProvider feature picking by url (See PickedFeatures/Cesium._attachProviderCoordHooks). This URL is never called. + * This is set using the `id` property in the constructor options + */ + readonly url?: string; // Set values to please poor cesium types readonly defaultNightAlpha = undefined; @@ -349,6 +354,7 @@ export default class ProtomapsImageryProvider } this.errorEvent = new CesiumEvent(); + this.url = options.id; this.ready = true; @@ -544,10 +550,12 @@ export default class ProtomapsImageryProvider this.source instanceof GeojsonSource && this.source.geojsonObject ) { + // Get rough meters per pixel (at equator) for given zoom level + const zoomMeters = 156543 / Math.pow(2, level); // Create circle with 10 pixel radius to pick features const buffer = circle( [CesiumMath.toDegrees(longitude), CesiumMath.toDegrees(latitude)], - 10 * this.terria.mainViewer.scale, + 10 * zoomMeters, { steps: 10, units: "meters" @@ -662,6 +670,7 @@ export default class ProtomapsImageryProvider return new ProtomapsImageryProvider({ terria: options?.terria ?? this.terria, + id: options?.id ?? this.url, data, minimumZoom: options?.minimumZoom ?? this.minimumLevel, maximumZoom: options?.maximumZoom ?? this.maximumLevel, diff --git a/lib/ModelMixins/GeojsonMixin.ts b/lib/ModelMixins/GeojsonMixin.ts index df72d0fe261..8a5ca3e7169 100644 --- a/lib/ModelMixins/GeojsonMixin.ts +++ b/lib/ModelMixins/GeojsonMixin.ts @@ -88,6 +88,7 @@ import LoadableStratum from "../Models/Definition/LoadableStratum"; import Model, { BaseModel } from "../Models/Definition/Model"; import StratumOrder from "../Models/Definition/StratumOrder"; import TerriaFeature from "../Models/Feature/Feature"; +import { TerriaFeatureData } from "../Models/Feature/FeatureData"; import { ViewingControl } from "../Models/ViewingControls"; import TableStylingWorkflow from "../Models/Workflows/TableStylingWorkflow"; import createLongitudeLatitudeFeaturePerRow from "../Table/createLongitudeLatitudeFeaturePerRow"; @@ -102,7 +103,6 @@ import { ExportData } from "./ExportableMixin"; import FeatureInfoUrlTemplateMixin from "./FeatureInfoUrlTemplateMixin"; import { isDataSource } from "./MappableMixin"; import TableMixin from "./TableMixin"; -import { TerriaFeatureData } from "../Models/Feature/FeatureData"; export const FEATURE_ID_PROP = "_id_"; @@ -775,6 +775,8 @@ function GeoJsonMixin>(Base: T) { let provider = new ProtomapsImageryProvider({ terria: this.terria, data: protomapsData, + // Note: this URL is only used for Terria feature picking (see PickedFeatures.ProviderCoordsMap) + id: this.uniqueId, paintRules: [ // Polygon features { diff --git a/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts b/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts index 7e9ee34aefa..6d21833590f 100644 --- a/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts +++ b/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts @@ -206,6 +206,7 @@ class MapboxVectorTileCatalogItem extends MappableMixin( return new ProtomapsImageryProvider({ terria: this.terria, + id: this.uniqueId, data: this.url, minimumZoom: this.minimumZoom, maximumNativeZoom: this.maximumNativeZoom, From dc01dff1b4d865de3ce0549a38f9b8ca57f03326 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Wed, 25 Oct 2023 11:32:20 +1100 Subject: [PATCH 060/129] Remove comment --- lib/ModelMixins/GeojsonMixin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ModelMixins/GeojsonMixin.ts b/lib/ModelMixins/GeojsonMixin.ts index 8a5ca3e7169..14684953008 100644 --- a/lib/ModelMixins/GeojsonMixin.ts +++ b/lib/ModelMixins/GeojsonMixin.ts @@ -775,7 +775,6 @@ function GeoJsonMixin>(Base: T) { let provider = new ProtomapsImageryProvider({ terria: this.terria, data: protomapsData, - // Note: this URL is only used for Terria feature picking (see PickedFeatures.ProviderCoordsMap) id: this.uniqueId, paintRules: [ // Polygon features From 33a90239b2adee4a87308d8bac74afc93ca49947 Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 14:19:53 +1100 Subject: [PATCH 061/129] Add attribution on screen and google logo for Google Photorealistic 3D Tiles --- lib/Models/Cesium.ts | 110 +++++++++++------- lib/ReactViews/BottomDock/MapDataCount.tsx | 5 +- .../Map/BottomLeftBar/BottomLeftBar.tsx | 73 ++++++++++-- .../TraitsClasses/Cesium3dTilesTraits.ts | 7 ++ wwwroot/images/google_on_non_white_hdpi.png | Bin 0 -> 11448 bytes 5 files changed, 136 insertions(+), 59 deletions(-) create mode 100644 wwwroot/images/google_on_non_white_hdpi.png diff --git a/lib/Models/Cesium.ts b/lib/Models/Cesium.ts index 4ddda5ab67a..f54115c838b 100644 --- a/lib/Models/Cesium.ts +++ b/lib/Models/Cesium.ts @@ -94,6 +94,11 @@ import { setViewerMode } from "./ViewerMode"; //import Cesium3DTilesInspector from "terriajs-cesium/Source/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector"; +type CreditDisplayElement = { + credit: Credit; + count: number; +}; + // Intermediary var cartesian3Scratch = new Cartesian3(); var enuToFixedScratch = new Matrix4(); @@ -129,7 +134,10 @@ export default class Cesium extends GlobeOrMap { | MappableMixin.Instance | /*TODO Cesium.Cesium3DTileset*/ any; + // Lightbox and on screen attributions from CreditDisplay private cesiumDataAttributions: IObservableArray = observable([]); + // Public because this is accessed from BottomLeftBar.tsx + cesiumScreenDataAttributions: IObservableArray = observable([]); // When true, feature picking is paused. This is useful for temporarily // disabling feature picking when some other interaction mode wants to take @@ -490,6 +498,7 @@ export default class Cesium extends GlobeOrMap { const creditDisplay: CreditDisplay & { _currentFrameCredits?: { lightboxCredits: AssociativeArray; + screenCredits: AssociativeArray; }; } = this.scene.frameState.creditDisplay; const creditDisplayOldDestroy = creditDisplay.destroy; @@ -505,48 +514,16 @@ export default class Cesium extends GlobeOrMap { creditDisplayOldEndFrame.bind(creditDisplay)(); runInAction(() => { - const creditDisplayElements: { - credit: Credit; - count: number; - }[] = creditDisplay._currentFrameCredits!.lightboxCredits.values; - - // sort credits by count (number of times they are added to map) - const credits = creditDisplayElements - .sort((credit1, credit2) => { - return credit2.count - credit1.count; - }) - .map(({ credit }) => credit.html); - - if (isEqual(credits, toJS(this.cesiumDataAttributions))) return; - - // first remove ones that are not on the map anymore - // Iterate backwards because we're removing items. - for (let i = this.cesiumDataAttributions.length - 1; i >= 0; i--) { - const attribution = this.cesiumDataAttributions[i]; - if (!credits.includes(attribution)) { - this.cesiumDataAttributions.remove(attribution); - } - } - - // then go through all credits and add them or update their position - for (const [index, credit] of credits.entries()) { - const attributionIndex = this.cesiumDataAttributions.indexOf(credit); - - if (attributionIndex === index) { - // it is already on correct position in the list - continue; - } else if (attributionIndex === -1) { - // it is not on the list yet so we add it to the list - this.cesiumDataAttributions.splice(index, 0, credit); - } else { - // it is on the list but not in the right place so we move it - this.cesiumDataAttributions.splice( - index, - 0, - this.cesiumDataAttributions.splice(attributionIndex, 1)[0] - ); - } - } + syncCesiumCreditsToAttributions( + creditDisplay._currentFrameCredits!.lightboxCredits + .values as CreditDisplayElement[], + this.cesiumDataAttributions + ); + syncCesiumCreditsToAttributions( + creditDisplay._currentFrameCredits!.screenCredits + .values as CreditDisplayElement[], + this.cesiumScreenDataAttributions + ); }); }; } @@ -1033,11 +1010,11 @@ export default class Cesium extends GlobeOrMap { : undefined; if (!center) { - /** In cases where the horizon is not visible, we cannot calculate a center using a pick ray, + /** In cases where the horizon is not visible, we cannot calculate a center using a pick ray, but we need to return a useful CameraView that works in 3D mode and 2D mode. - In this case we can return the correct definition for the cesium camera, with position, direction, and up, + In this case we can return the correct definition for the cesium camera, with position, direction, and up, but we need to calculate a bounding box on the ellipsoid too to be used in 2D mode. - + To do this we clone the camera, rotate it to point straight down, and project the camera view from that position onto the ellipsoid. **/ @@ -1851,3 +1828,46 @@ function flyToBoundingSpherePromise( }); }); } + +function syncCesiumCreditsToAttributions( + creditsElements: CreditDisplayElement[], + dataAttributionsObservable: IObservableArray +) { + // sort credits by count (number of times they are added to map) + const credits = creditsElements + .sort((credit1, credit2) => { + return credit2.count - credit1.count; + }) + .map(({ credit }) => credit.html); + + if (isEqual(credits, toJS(dataAttributionsObservable))) return; + + // first remove ones that are not on the map anymore + // Iterate backwards because we're removing items. + for (let i = dataAttributionsObservable.length - 1; i >= 0; i--) { + const attribution = dataAttributionsObservable[i]; + if (!credits.includes(attribution)) { + dataAttributionsObservable.remove(attribution); + } + } + + // then go through all credits and add them or update their position + for (const [index, credit] of credits.entries()) { + const attributionIndex = dataAttributionsObservable.indexOf(credit); + + if (attributionIndex === index) { + // it is already on correct position in the list + continue; + } else if (attributionIndex === -1) { + // it is not on the list yet so we add it to the list + dataAttributionsObservable.splice(index, 0, credit); + } else { + // it is on the list but not in the right place so we move it + dataAttributionsObservable.splice( + index, + 0, + dataAttributionsObservable.splice(attributionIndex, 1)[0] + ); + } + } +} diff --git a/lib/ReactViews/BottomDock/MapDataCount.tsx b/lib/ReactViews/BottomDock/MapDataCount.tsx index 0468faa962a..e10e54db3e5 100644 --- a/lib/ReactViews/BottomDock/MapDataCount.tsx +++ b/lib/ReactViews/BottomDock/MapDataCount.tsx @@ -39,10 +39,7 @@ const MapDataCount = observer(function (props: Props) { : t("countDatasets.noMapDataEnabled"); return ( - // Should we even provide a wrapper Box? makes sense not to, but most of the - // components as they stand come with their own "wrapper" via scss - // - + props.theme.mobile}px) { bottom: 35px; } + display: flex; +`; + +// Use padding to avoid other UI elements +const AttributionsContainer = styled(Text)` + text-shadow: 0 0 2px #000000; + padding-left: 8px; + padding-right: 56px; + @media (max-width: ${(props) => props.theme.mobile}px) { + padding-right: 8px; + padding-bottom: 32px; + } `; + const shouldShowPlayStoryButton = (viewState: ViewState) => viewState.terria.configParameters.storyEnabled && defined(viewState.terria.stories) && viewState.terria.stories.length > 0 && viewState.useSmallScreenInterface; -const BottomLeftBar: FC = () => { +const BottomLeftBar: FC = observer(() => { const { t } = useTranslation(); const theme = useTheme(); const viewState = useViewState(); + const screenDataAttributions = + viewState.terria.cesium?.cesiumScreenDataAttributions; + const isNotificationActive = viewState.terria.notificationState.currentNotification; + const isUsingGooglePhotorealistic3dTiles = + viewState.terria.mainViewer.viewerMode === ViewerMode.Cesium && + viewState.terria.workbench.items + .filter((i): i is Cesium3DTilesCatalogItem => i.type === "3d-tiles") + .some( + (i) => + i.url?.startsWith( + "https://tile.googleapis.com/v1/3dtiles/root.json" + ) && i.show + ); + return ( { ) : null} + {/* Google Logo. Needed for Google Photorealistic 3D Tiles + */} + {isUsingGooglePhotorealistic3dTiles && ( + + )} + {/* On screen data attributions. At the moment, this supports only Cesium viewer. + Needed for Google Photorealistic 3D Tiles + */} + {!!screenDataAttributions?.length && ( + + {screenDataAttributions + .flatMap((attributionHtml, i) => [ + + {parseCustomHtmlToReact(attributionHtml)} + , + + ]) + .slice(0, -1)} + + )} ); -}; +}); export default BottomLeftBar; diff --git a/lib/Traits/TraitsClasses/Cesium3dTilesTraits.ts b/lib/Traits/TraitsClasses/Cesium3dTilesTraits.ts index f98dd03086f..b3e88b8b9cb 100644 --- a/lib/Traits/TraitsClasses/Cesium3dTilesTraits.ts +++ b/lib/Traits/TraitsClasses/Cesium3dTilesTraits.ts @@ -101,6 +101,13 @@ export class OptionsTraits extends ModelTraits { description: "Point cloud shading parameters" }) pointCloudShading?: PointCloudShadingTraits; + + @primitiveTrait({ + type: "boolean", + name: "Show credits on screen", + description: "Whether to display the credits of this tileset on screen." + }) + showCreditsOnScreen: boolean = false; } export default class Cesium3DTilesTraits extends mixTraits( diff --git a/wwwroot/images/google_on_non_white_hdpi.png b/wwwroot/images/google_on_non_white_hdpi.png new file mode 100644 index 0000000000000000000000000000000000000000..393d03005d5bd544bd8655319844fca20fc10fc2 GIT binary patch literal 11448 zcmeHscTkht)_3SdX@Vd9lF%Xa7J63#1PCpZP^I@GO(}wabm>h51q4J?5TuJB zAWeEvse%Gu^qhO{x%0g<-`tt+{qJPvDSPj=etWIoT6<0Qvl9{e8WiMAsd$c*Z$m8 zn(xBYc6W()@g|(-5WE^u9Zxz838#W|k!XKAQ2)+m7uLGEy|FuDZLxmv{rrOW>d!BS zyeHivKQNb`a16unJ$pHRu91bzWoJCCON|^J1E%m(685Wd!*5$3lvhS~WH+|z$Pu_e!ZJf+$WpvRT_A*5{rNx-{;qiE(@y}=3 zU-e}>p9QoI=LRg6ihGG=^(bEWT^?RJslKif7~6TP*24S_xzr$H?Jy@TBg3m(Sy;43 zF)aMt@Fp#zP*Ss%BlYwVv`=7(ic3>;ERl+yO;P^F#|yRyBJZtUdle9Yqj1?Z*RR6r zu2D+<*C3X`x=;}?wRYkIwb%JZ+|u$}lOg4?Cu0^MQQ4a$L;djLZl-69=)1M~SB@rp z9>qCnapxGhqRVp(o%AF>7`b_v7Rgw<&O8}+b*pO|b25-W8F#}xKN(w3;Ka*AO^&Iv zI}8m#c{K_{Ztq7z&27-DrgPObbzQIX)K4v1?ceA5F-rx^p`+P5_F8|i-7fUvE5By| zJ!+X4kvTAOm45aB^X#Z~_RG@)=cz9A`Ro_Peyi_J!d<$X(Ywx5fk%;-vT*8ry45=| zYB>aq7;QWv)+~ty)nUV2%zwiT6YREwtSw|%=+ALuPMl!hoR=kZ>JXf z=BwcA>mGoVPuRjnFv;WAR@h05H?s3nc5GhgjuTY@^ zpZFy1G}j*$t8Xnx@_-fe8WdBW>3PrITl0I&UpJdQT~jqWw>r<-wSijmQiXS%H$=fI^y)1*(_s=d(<+I@wGd9;+?ZRWzS&%)D=sCmO$ldDMX* z-kelliuGj4&8y;l(yH%p5IJ(3Awu;0CH@y5JqHwd-Lsnf?wx9dz<0xFDVNf0_vf#g zQ_D?eDB$ieP_+>udh@nnV0S+(1tE)dB_yPb7a?_Sc!lt1&GhR2z6CQQ92|h=l2Ei#%tvrq%rW7kP zR|#3gG4=;*+tdX751St9K+fJ}!ZTui+>&qaevT)Cg*}MbnY}&Oy3$PG{qQ+ICeKaC zUg;>GHjry~a=sX-!AF;Sce{#4o_-ul75>E*at<_&{5T>3EN5s+S$y>zXki<2!$`wjg!8DP_B$@_`xTLh_Pjb_s+fsb@1y1)($0RZX)O{Y#% zR=QRLrti%LNKTG0ev#&zsjd<)mnL=Ynz zg!)lLY0+_|-xb*;)dwnhA13y5DLokRs~d{l_B|qE#A0EGUVKsv6gP-j19ddac1?x* z3h-!j^|h^q0#|n+#R505aMDV1pRlr&Dqr%me3=I%w~qG|*xzOdUn^WQFW7U zGTO{lPY2tUwWRo?DXZd$?zY2qik|f;ABv^V5iZm=s{*-Mncnt51U3Pp=rQf0cex#= z+Qsn-ff-!^z#E?1YudKac95O!GD-;HSYDBLJxM^+*S6t%EaR!3tXI{Y-$XXYQ(8%T zI|-HG`|C!L8g4$OfQ=l3zRH`s$0{MLsUl>X<6Om(8t_MBQ+b*v@o0cXS*i1Mn{`Hs zJTdxiNl9rO={yh7j?77U&I8K5R^Fc8mdfwaYoqLz=As%TL5oGw4BsEppUcaoP@8`U z*I?CN9`;++o;Kd2imjK9F$js3I#=@C%w60^F}zS#Ig8W1W*U&r#n&e+zdrS`%51+I z=4C^^L4CYI#%+uy*``HA?1}6KD7oQ5#YHzL1@E7TefX@oMsM$)9fm(m98Ia!w8MM% zBwe(+@6eVY)jv#-K?j1!wBoL)`!u5zzkTKcP3ALOk8n`wAhDahV5LE9PtrR^>=Clv zr9{I?LY)sYR*J{jQPt88WzdS$?lqV8A!!oj>RJxeTo7Z9Ma^50uA*n6@hQA`_ zBci1L+8xNz)y^ay6IwhqCNaR+lS1U1Z(~G_Ujf&62}uNtU8tX=6wd7nlk!lBCaT_? z>!AvsAuJ4e70JdP5lVFWHTpQV`(q3?<650@oUDF1*yJmKRV>oHv~o*DZe=%vTV(hS z8Ml~KjiVu94RK4nR6Fw$r~Al;?RRUJ!v=+wi$f09t(P>`ca&$Y(}DNOZY0{*n*u#m>@Gp`?KteQM3OG2Y1?$?df>Bohq> z4XaF#OZg^HIVTae$P~LC1dFBYeH1#XqW9M6w7nbNX&f=EB0UfxGflrb z$>)G2pf7QaWPXFqtDKmv=wds&;eFH~E@XB_ORiw%WNe-NMCtWBdQcB<#l*&BrmeT4 zc!Mk#q4?r>YKlbRi?==>&I`C1n-5q!dHQ7(@&=Y>B>USTtiO)1MvD;PM`vNnS+(R%p8o^kve$8Vl9;5!=l9kk52EV6~{4eFsG)vLfM`qA&6!>P#<*YWn%oZwN3^VjOD1Va!CPS-L~h@ z>%Vi>BE(G6?6vPu{vm!46LntH9Hq*BgyF9#KM2{q0yCWLpgvHl*#FdooS;q;Qa_@s z1}ZXjs*gk}#;Ix07|L-H!|`0AN!3irXwHJ8>WPXzIjT~Oygh@>_7u_)`qWXXz3v8Q zeF_a{$&~YF|K>Njzv*y~0v)5@8^u9EBn&T9b*E=3&pz?F--&P#Wy!h_FzvDFL=s!D zkmcX^01!y%A@NaI0eg}w9w$_~_0BLV(Cq2cCvBPQQ$T4J?soQAM%u{KjRuOBx-BDb zi3;;+2l4n9-633FEd%|B)l_&Tn)g7#dE4Q59aM&RPl_ela{~*O)e?NFDd6s0&lH=+K>~mh{ z`QBH*M-LCTy6c5k!N_jPjL=c)y3cwul<&pq?%Uq0Gh8O-;{ub*J+YPK8DBM#AjGCg zJoAs8%LAXHxzQFTes=PXL0t$@rMwyTdVTS!GxGeJA8a+C>FJ)l)0hbRI)oBTk9 z3HfIZ^{`V#AbFHp97d7tQ8SX~XH?9A+s@Z~7H~eeBgkRG-+RB^dg|&fx@RWBi)&XY z`FXj(_3tAQ>lu>4{Z~U94C$0eT%^cd)1O$)v>`fNdmdBfUFZ>d4)^VQ+`rf9(VH4U ztJ^~cr&daKU9=NxCAiVckddHjI8TdBA)48$O<=lzJ>YKRZ8sVjAgMG>C6>yay0so8 zPg&Ua$oz<&{m~vxGPj)*oMk8Ki~c(k5}xLRv)9zFq`=R?4r1`g_e-pS7LKY>3pX@x zkaYL7Z4+yK0ybRjazEAEx5}jAj>-3rnDH$|KKcG>en@jVRdXQv_+h!rF>%wXSMzHg zd>^jH=?F}X7C)KTrb&|k6J^g*6SOa^Se$0MW_=LP(3dZuC?gA$>^*>dLscDd7hZb{ zMz2WNt!i&B(Un~iO-o#4PfLdS&dDXTFF}hhR}%+gSzD-5aoNJl{L$9?b^T4)qD2S$ zQhVWD&ZA1Y$$J>4rqmN2iu19rIj*}6nv-P+0>o@&sI8O|jUyIoo8W8sDcfl4rrB`u z!_)pL$XxSuZr+AHK4)-0Gc{HI`r|`JAIQ6_VZopPAUAfgtmuV0xepgiC&mT_NW81) z=n4GpIOHYEI_O=gNFJ@iCCRKrBpT7K`{t>W&8`bSP|ZG%Jr&eVtLv$GgFyt?chP&(w@2FEA3oSxqyHYt+Yr1m(9pwd8R|6E-Sxpl+va|@`Sx3 zf{afk&&c*DlqK(l8p{qSqFtIw&eL^g5tDlNjZ-d7jo_~VZZnOjJP=H>R9+b+7o~@-#-qo<<6pv5!};)# zmy`{WKJd}T+uDO35fD@t82xzGK4C^+Bi#WYX^#oXmKxch8KRrsTE&imN9XUB^yQWE zrPcWMGuj_$1GypB$W5mBU35r|8;j--4aGC@FTQa|mV`br>{d$oLNZbln$fPYz;1L! z5nKLAG|`aSPLg&DGIHE>O%V0gmqqe*@TngKI0K)tx`Pc^`NYf-uVhSqXFK6^bjXMS+!wyLX{R1 z8^@MiS;lqcVe*@ctMY6&WFfS|Y?8D#eWWJuiKes%xBLkx>R+U|W4oRD94|kSHQ#zC zsK9;VQaXW9b#GaE%dKBptecba^|VRhdR$-2s=)PlA*ue7oBE@f%GvZG@7j%`iQI}e z+K1Xmk+%J-jR7X z_WfzxRyyldi{1{V4^G8&wo^NFq{|N^+kV&} z+$p)_!)%%-Y277Cd;rnw3@n8TdR;lqP$Ip&N^TLR7cdUsZx?VSEsQ-E2n45uLbslZ zd0e?}wQ_?6h$zgviFAG&O;RS}L?=X_8fXi6lawc7X;MZt@WGVRmb#eSenTR)RB}>C ziVj9^^Ky9J+*TKohSeSoQFYC|^8I^s8gpK!IYkF-FwE$Xy#B+s-V0tNkz;AZ=9K&dNrPQ z-?Z>DKCz0xtSy|AameDUMjNuIlj`{gzX5a#GsCjGL#|{$*fuad$MwRtrG0_ktARv; z72y}o-Vr5Z`8FesqYouCvC5Q9`uQc5qxX;6+dR}HgQGlw_VZS&B!DKru}y?52Y%#o z6Qt)BIcehu(=gk_+EGH+m(%pz;{CcnK^1`%iy%fMiRZd>49R3e|F_$Ci;`k*^`8ah z0Dx<)KRW=#E7Ipkb<>A zP!y(gjL%Zq$L>{f#*d_n%|sq0f|nBzft07>JF>0<#m}bSRzast#X9-r3C+XiD}s4O znfM9xpV=A5t-YvDIf~dTeL31|WN#1;)r%&3BtpKK3bcJ)dmoE;Pn1cX!W~A!^{Su# z4nd$w!8RLjE}tDyo!Volk`?FXOzcX8rM=sv`NDNP0H+?QfRRgo$f7bQvqO{Z`-|85 z3v95|(AI=0R{*Rwnv(13Q|+sgo&FTh3kI0SCK@#uWQyM|%#AEwjHOV8%Vn!Fy6M{} zU74?3!6IwwVOq$H6j?H5{+*>UU-Dlq6TV@Nn!pSe^uGa^7~C4rGJE}cy;&ps zni7jk;M6^0g)-T9YkSOmndMDOT`oqBg7ugxU&(fMzRju`(EhPN^!_^AgN_Ur(dOvS z6D^G`dlkwcAUW@$!WczgQ64P&ANS1J2~+E$cZZ?W~Z^IA9cs75b}8M zotv?op$J8_HCiFR7SCY5z1U4i59zqz$Do?S&zt{Zi7w4^GveoH571Gz`xDRpU}=Tp zr;bbF>KB2I$%_~U0055@t*VO9QdRxe*L~bK{rrOTCheC9y0~tWCN*rjHi>utqLr8U zJsnfhD3v?cYxU80?Zp#0wD`LsF(p(J%G4o*>`E_YF4aj$9tSRc4QClXy}T{jvcsi2 zxN58UxZ3Od#fA?S)wbX=Q94Lm02DfiTrU_6RJhf2r|U4q{9x{(Gk4M;q8))k-ZZh> zO)dz^Vm50El0Bq)MP)Cm{aEuS*Qdns{>GW32}k;iz`WVoS2M4kZPv7UL)Yl29*9d+ zaG4yI=fX5(cC>8C@%Q-2%E_97eczePC1^vRu_VNS<|{ZbHNyv);OsXhH+hy)O$vFa z4!RfI*qx6m-dlfGrkSkcHgy9>o@iLGE1xi$H83~Bx)-LzRwgAbLsl4`gAfMBZr^X= z4oQO1xWf<&Jzbas#zO?@h_OeB1bTSk4od(4ImJLPq=Op@3$#Z$qdnz8Upl%$K(wPg z$XrrSOwUUd<$~4>_CXm3>zg>K2XMFt9c`f~n)_yYru@^$b*dtuQSPv9?1 zq&>zDD-QzU#)1E`&%;Yk?=N^y-#=Nv@gW+B^b!>p5fkthW!fM zAJ!Q7pmAA=bpN|mzn~m(Q1%c>87KsT6qZ2BK!qV-F-Kt;aYr#>Cus=@2{AFSq$Je- zHa7Wpet zaR9$HaL&L~eNad&#>WJMahC`EvI+Q0^RHn&+vr* zNWmmzgmF!=zsX}9(N01CFX>;a2PpRk%bI9k-26emyZ%^G#whPUT7NX%(ZAOc5cqpl zz>p4qxZsQQNBt2k9M&I04lYPfXB4h{{F$%+>PP<%xnPfkIyge?C50gp5@6g-Mp9VD z-bqGS94zkO2!`5AIY=RYv+x(XFUAQQfb>BrJL7o7afQpz-&_It{)iO+e~JgVpnin` z#~6+x;lCJzi2cc!=${^o{;C@Pa#>FF{~|^1x4_@Z3{LNlK3w_2RYTFg%Hf}+;SB!Y z{QVh>|II0Yz<(zBulW6ku7BwIuNe5Rg#XE|f9U$J82GP*|H-cZZ*-CWdqait#QhBl zz-^ay9QkN)TP=Kh9Su0(GCpmn8`mQ9(zNsi0Ityfy6^yb`8RN#Bv>syHIg+FdRh^_ zZ7tY60D!1f3$AS9@@d}E+C5W^?!w9cS!=&(41EG|Qc|W}S)HAsCtk0fmQv!rT@k(l zwICW1m1nmc4bmnwOoaF7^d>7rh`P%d`)e2G~K|EkF^d1T*x?Ox@0TsclBW^1#GCMR_{&?H%wn#Y-O_;b|eo(5U zxrkPZyEM$9|7Q9R{J8#g=W(9bWlGYob5BtPR{$YudI(cI-Zpa6OXtfcs@K=;G7hk# zU9FDosHBcCdw=6Z_oR=GG0q)Q$^I>?YfbP*;?RpPxuEyQop(f_gah8W!U8Cpgq*(R z4QDpJED&hl!>e-H+C`76BOzf_P2}50Vk(J;+o9Pd-E<^BV|NCQO1^|jP7`}O9>;jk z96llOO}V&ZU`{;h%|81jko3Uf0R3>JOP-yJF9^}Mxe?*fnQ?5){bYJ%+o3>0JU^yV zF77k3+V?86QcedVWqzA0R8?a)sEZnY5?{7KZ%G;~43@kp#QIL8zSWRQLx?+R#VERC z7?nM})VYCAI96_2@gb0KD_aQNi zfrewPO1!dBZ#t(QTUrAG9Wqd*#=(OprZ2eoOy<|19cngfKa-!9J#p&nzaR49iSEZ3 z`lX+aUf|h9sb_26@wug zQ5Y*Aaf_ZO_V%Eh{Ab310I~Ug1)naLLS5@b58h7QnpKgUp>%_kj8|iU%;_J3ivR#b z=>q@&s-D7npj4Bx8>Y2He8c7mNr2FB%NY~%xn50AbHDNld zE-y%prDFmAU@f#wq{P*?a8HZ)O&ZA}F~Th$Avt&mdqc}zq0X5kOSC(8p5ennQC?{5Js7G#BxI-!=c=b44ON?xp%UXna4z2+@le-C87IGCEp=Hg_HeyYq z1j$8vPQUn(wJz^M(a#GC9&shFtncJ$S7k5~ zY$mrl#uaowcnbeW063|Toa!B8UcNPUR~&1&Eake)xW`Pnn@kg$b!ZHs82w1+h5U%@ z?$(aC(UwW@l>Nv!F96egPb0j<$>~76t!lm8Sl6h3UluwhvQTchuIrY#T;`VPmHBk_ zvv)nI!gDy>CAP1@Y@hTjok@QF_-lSqtq?LSdsuc##1s7)l9Yg8O)Gf`0<~~{ z$LB-dROyD+n!hD9NWJnjh5`BXP=h}2z1pN*L)Yl*g0=#=1keCp74396B~f`K+&e#3 z%P6AoY1wh5&wCZgGyRCR*a`vwLw^Whl$~FL@xH7b3pqE5a5bB(soK_R(q%JgZLJrr z&PyBdPhE^psjpZ~UJEH>&eY&Bo?c(B)-deL z$&Plui5*Wq_>S)Q+^w+kwzS02L20nL}!{5NCAl_DjyokQoG uwr>LlTjp2yz;yHPO(&N=zw3V(PLN2*yJS4b8-gq904+6rc)f~U)c*mQ%#mLJ literal 0 HcmV?d00001 From 9151a0013c5b2a15a2a26ea389c80aaf372a6b4c Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 14:20:17 +1100 Subject: [PATCH 062/129] Fix mobx warning --- lib/ReactViews/Map/ProgressBar.tsx | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/ReactViews/Map/ProgressBar.tsx b/lib/ReactViews/Map/ProgressBar.tsx index 9fcc5359d60..6c60dbb3a09 100644 --- a/lib/ReactViews/Map/ProgressBar.tsx +++ b/lib/ReactViews/Map/ProgressBar.tsx @@ -1,9 +1,10 @@ -import React, { VFC, useCallback, useEffect, useMemo, useState } from "react"; +import { observer } from "mobx-react"; +import { VFC, useCallback, useEffect, useState } from "react"; import styled, { css, keyframes, useTheme } from "styled-components"; import EventHelper from "terriajs-cesium/Source/Core/EventHelper"; import { useViewState } from "../Context"; -export const ProgressBar: VFC = () => { +export const ProgressBar: VFC = observer(() => { const [loadPercentage, setLoadPercentage] = useState(0); const [indeterminateLoading, setIndeterminateLoading] = useState(); @@ -32,13 +33,8 @@ export const ProgressBar: VFC = () => { }; }, []); - const backgroundColor = useMemo( - () => - terria.baseMapContrastColor === "#ffffff" - ? "#ffffff" - : theme.colorPrimary, - [terria.baseMapContrastColor] - ); + const backgroundColor = + terria.baseMapContrastColor === "#ffffff" ? "#ffffff" : theme.colorPrimary; const allComplete = loadPercentage === 100 && !indeterminateLoading; @@ -50,7 +46,7 @@ export const ProgressBar: VFC = () => { loadPercentage={`${loadPercentage}%`} /> ); -}; +}); interface IStyledProgressBarProps { loadPercentage: string; From 223970cf2894fec920c88553347b60ceb279e47a Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 14:23:59 +1100 Subject: [PATCH 063/129] Remove debugging console logs --- lib/ViewModels/TerriaViewer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ViewModels/TerriaViewer.ts b/lib/ViewModels/TerriaViewer.ts index 5e5c7568f26..f2ddec0e238 100644 --- a/lib/ViewModels/TerriaViewer.ts +++ b/lib/ViewModels/TerriaViewer.ts @@ -219,7 +219,6 @@ export default class TerriaViewer { newViewer = untracked(() => new NoViewer(this)); } - console.log(`Creating a viewer: ${newViewer.type}`); this._lastViewer = newViewer; newViewer.zoomTo(currentView || untracked(() => this.homeCamera), 0.0); @@ -248,7 +247,6 @@ export default class TerriaViewer { let currentView: CameraView | undefined; if (this._lastViewer !== undefined) { this.beforeViewerChanged.raiseEvent(); - console.log(`Destroying viewer: ${this._lastViewer.type}`); currentView = this._lastViewer.getCurrentCameraView(); this._lastViewer.destroy(); this._lastViewer = undefined; From f10f0c7269ae1d234727c29cda8e90c9ff2aca62 Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 15:33:34 +1100 Subject: [PATCH 064/129] Add changelog entry --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 1ee97136874..ead7503da41 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ - Fix bug in mismatched GeoJSON Feature `_id_` and TableMixin `rowId` - this was causing incorrect styling when using `filterByProperties` or features had `null` geometry - Fix splitter for `GeoJsonMixin` (lines and polygon features only) - Fix share links with picked features from `ProtomapsImageryProvider` +- Added on screen attribution and Google logo for Google Photorealistic 3D Tiles. - [The next improvement] #### 8.3.6 - 2023-10-03 From 7a0f2096fba9bf03fd10dcd5980d63fd73f8d9ce Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 15:37:17 +1100 Subject: [PATCH 065/129] Fix missing React imports, waiting on React 18 https://github.com/TerriaJS/terriajs/pull/6902 --- lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx | 2 +- lib/ReactViews/Map/ProgressBar.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx b/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx index 552cc842b9c..2fbc49aacdf 100644 --- a/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx +++ b/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react"; -import { FC } from "react"; +import React, { FC } from "react"; import { useTranslation } from "react-i18next"; import styled, { useTheme } from "styled-components"; import defined from "terriajs-cesium/Source/Core/defined"; diff --git a/lib/ReactViews/Map/ProgressBar.tsx b/lib/ReactViews/Map/ProgressBar.tsx index 6c60dbb3a09..eedfef26599 100644 --- a/lib/ReactViews/Map/ProgressBar.tsx +++ b/lib/ReactViews/Map/ProgressBar.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react"; -import { VFC, useCallback, useEffect, useState } from "react"; +import React, { VFC, useCallback, useEffect, useState } from "react"; import styled, { css, keyframes, useTheme } from "styled-components"; import EventHelper from "terriajs-cesium/Source/Core/EventHelper"; import { useViewState } from "../Context"; From 97ba9ffd8474412d78732a093e1a9871aec49a4b Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Fri, 20 Oct 2023 04:04:23 +1100 Subject: [PATCH 066/129] Add ASGS 2021 leftovers and add an automated duplicate alias check --- .../find-region-mapping-alias-duplicates.yml | 34 + .../find-region-mapping-alias-duplicates.js | 22 + wwwroot/data/regionMapping.json | 724 ++++++++++++++---- 3 files changed, 634 insertions(+), 146 deletions(-) create mode 100644 .github/workflows/find-region-mapping-alias-duplicates.yml create mode 100644 buildprocess/find-region-mapping-alias-duplicates.js diff --git a/.github/workflows/find-region-mapping-alias-duplicates.yml b/.github/workflows/find-region-mapping-alias-duplicates.yml new file mode 100644 index 00000000000..331c2ed47c3 --- /dev/null +++ b/.github/workflows/find-region-mapping-alias-duplicates.yml @@ -0,0 +1,34 @@ +name: Find region mapping alias duplicates + +# Run when regionMapping.json file or is updated +on: + push: + paths: + - "wwwroot/data/regionMapping.json" + - "buildprocess/find-region-mapping-alias-duplicates.js" + pull_request: + paths: + - "wwwroot/data/regionMapping.json" + - "buildprocess/find-region-mapping-alias-duplicates.js" + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job + find-alias-duplicates: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Check out only 2 files + # Use without cone mode as no git operations are executed past checkout + - uses: actions/checkout@v4 + with: + sparse-checkout: | + wwwroot/data/regionMapping.json + buildprocess/find-region-mapping-alias-duplicates.js + sparse-checkout-cone-mode: false + + - name: Check aliases + run: | + node buildprocess/find-region-mapping-alias-duplicates.js diff --git a/buildprocess/find-region-mapping-alias-duplicates.js b/buildprocess/find-region-mapping-alias-duplicates.js new file mode 100644 index 00000000000..e3d2c6e079c --- /dev/null +++ b/buildprocess/find-region-mapping-alias-duplicates.js @@ -0,0 +1,22 @@ +const fs = require("fs"); +const regions = JSON.parse( + fs.readFileSync("wwwroot/data/regionMapping.json") +).regionWmsMap; + +const aliasToType = new Map(); +for (const [regType, regDef] of Object.entries(regions)) { + for (const alias of regDef.aliases ?? []) { + aliasToType.set(alias, [...(aliasToType.get(alias) ?? []), regType]); + } +} + +let issues = 0; +for (const [alias, regTypes] of aliasToType.entries()) { + if (regTypes.length > 1) { + console.error( + `Alias "${alias}" used in multiple types: ${regTypes.join(", ")}` + ); + issues++; + } +} +process.exitCode = issues > 0 ? 1 : 0; diff --git a/wwwroot/data/regionMapping.json b/wwwroot/data/regionMapping.json index 987f18be348..a2f929b28d2 100644 --- a/wwwroot/data/regionMapping.json +++ b/wwwroot/data/regionMapping.json @@ -1,6 +1,447 @@ { "comments": "Matching takes place in the order defined in this file. Place code matches before name matches, and smaller regions before larger ones.", "regionWmsMap": { + "STE_2021": { + "layerName": "STE_2021", + "server": "https://tiles.terria.io/STE_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-STE_2021.json", + "uniqueIdProp": "FID", + "regionProp": "STATE_CODE_2021", + "nameProp": "STATE_NAME_2021", + "aliases": ["ste_code_2021", "ste_code", "ste"], + "description": "States and Territories 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "ILOC_2021": { + "layerName": "ILOC_2021", + "server": "https://tiles.terria.io/ILOC_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ILOC_2021.json", + "uniqueIdProp": "FID", + "regionProp": "ILO_CODE21", + "nameProp": "ILO_NAME21", + "aliases": ["iloc_code_2021", "iloc_code", "iloc"], + "description": "Indigenous Locations 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "ILOC_NAME_2021": { + "layerName": "ILOC_2021", + "server": "https://tiles.terria.io/ILOC_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ILOC_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "ILO_NAME21", + "nameProp": "ILO_NAME21", + "aliases": ["iloc_name_2021", "iloc_name"], + "description": "Indigenous Locations 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "IARE_2021": { + "layerName": "IARE_2021", + "server": "https://tiles.terria.io/IARE_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-IARE_2021.json", + "uniqueIdProp": "FID", + "regionProp": "IAR_CODE21", + "nameProp": "IAR_NAME21", + "aliases": ["iare_code_2021", "iare_code", "iare"], + "description": "Indigenous Areas 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "IARE_NAME_2021": { + "layerName": "IARE_2021", + "server": "https://tiles.terria.io/IARE_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-IARE_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "IAR_NAME21", + "nameProp": "IAR_NAME21", + "aliases": ["iare_name_2021", "iare_name"], + "description": "Indigenous Areas 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "IREG_2021": { + "layerName": "IREG_2021", + "server": "https://tiles.terria.io/IREG_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-IREG_2021.json", + "uniqueIdProp": "FID", + "regionProp": "IRE_CODE21", + "nameProp": "IRE_NAME21", + "aliases": ["ireg_code_2021", "ireg_code", "ireg"], + "description": "Indigenous Regions 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "IREG_NAME_2021": { + "layerName": "IREG_2021", + "server": "https://tiles.terria.io/IREG_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-IREG_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "IRE_NAME21", + "nameProp": "IRE_NAME21", + "aliases": ["ireg_name_2021", "ireg_name"], + "description": "Indigenous Regions 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "RA_2021": { + "layerName": "RA_2021", + "server": "https://tiles.terria.io/RA_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-RA_2021.json", + "uniqueIdProp": "FID", + "regionProp": "RA_CODE21", + "nameProp": "RA_NAME21", + "aliases": ["ra_code_2021", "ra_code", "ra"], + "description": "Remoteness Areas 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SAL_2021": { + "layerName": "SAL_2021", + "server": "https://tiles.terria.io/SAL_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SAL_2021.json", + "uniqueIdProp": "FID", + "regionProp": "SAL_CODE_2021", + "nameProp": "SAL_NAME_2021", + "aliases": ["sal_code_2021", "sal_code", "sal"], + "description": "Suburbs and Localities 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "ADD_2021": { + "layerName": "ADD_2021", + "server": "https://tiles.terria.io/ADD_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ADD_2021.json", + "uniqueIdProp": "FID", + "regionProp": "ADD_CODE_2021", + "nameProp": "ADD_NAME_2021", + "aliases": ["add_code_2021", "add_code", "add"], + "description": "Australian Drainage Divisions 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "ADD_NAME_2021": { + "layerName": "ADD_2021", + "server": "https://tiles.terria.io/ADD_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ADD_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "ADD_NAME_2021", + "nameProp": "ADD_NAME_2021", + "aliases": ["add_name_2021", "add_name"], + "description": "Australian Drainage Divisions 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "DZN_2021": { + "layerName": "DZN_2021", + "server": "https://tiles.terria.io/DZN_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-DZN_2021.json", + "uniqueIdProp": "FID", + "regionProp": "DZN_CODE_2021", + "nameProp": "DZN_CODE_2021", + "aliases": ["dzn_code_2021", "dzn_code", "dzn"], + "description": "Destination Zones 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "LGA_2022": { + "layerName": "LGA_2022", + "server": "https://tiles.terria.io/LGA_2022/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-LGA_2022.json", + "uniqueIdProp": "FID", + "regionProp": "LGA_CODE_2022", + "nameProp": "LGA_NAME_2022", + "aliases": ["lga_code_2022"], + "description": "Local Government Areas 2022", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "LGA_2023": { + "layerName": "LGA_2023", + "server": "https://tiles.terria.io/LGA_2023/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-LGA_2023.json", + "uniqueIdProp": "FID", + "regionProp": "LGA_CODE_2023", + "nameProp": "LGA_NAME_2023", + "aliases": ["lga_code_2023", "lga_code", "lga"], + "description": "Local Government Areas 2023", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SED_2021": { + "layerName": "SED_2021", + "server": "https://tiles.terria.io/SED_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_2021.json", + "uniqueIdProp": "FID", + "regionProp": "SED_CODE_2021", + "nameProp": "SED_NAME_2021", + "aliases": ["sed_code_2021"], + "description": "State Electoral Divisions 2021 (ABS)", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SED_NAME_2021": { + "layerName": "SED_2021", + "server": "https://tiles.terria.io/SED_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "SED_NAME_2021", + "nameProp": "SED_NAME_2021", + "aliases": ["sed_name_2021"], + "description": "State Electoral Divisions 2021 (ABS)", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SED_2022": { + "layerName": "SED_2022", + "server": "https://tiles.terria.io/SED_2022/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_2022.json", + "uniqueIdProp": "FID", + "regionProp": "SED_CODE_2022", + "nameProp": "SED_NAME_2022", + "aliases": ["sed_code_2022", "sed_code", "sed"], + "description": "State Electoral Divisions 2022 (ABS)", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SED_NAME_2022": { + "layerName": "SED_2022", + "server": "https://tiles.terria.io/SED_2022/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_NAME_2022.json", + "uniqueIdProp": "FID", + "regionProp": "SED_NAME_2022", + "nameProp": "SED_NAME_2022", + "aliases": ["sed_name_2022", "sed_name"], + "description": "State Electoral Divisions 2022 (ABS)", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "CED_2021": { + "layerName": "CED_2021", + "server": "https://tiles.terria.io/CED_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CED_2021.json", + "uniqueIdProp": "FID", + "regionProp": "CED_CODE_2021", + "nameProp": "CED_NAME_2021", + "aliases": ["ced_code_2021", "ced_code", "ced"], + "description": "Commonwealth Electoral Divisions 2021 (ABS)", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "CED_NAME_2021": { + "layerName": "CED_2021", + "server": "https://tiles.terria.io/CED_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CED_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "CED_NAME_2021", + "nameProp": "CED_NAME_2021", + "aliases": ["ced_name_2021", "ced_name"], + "description": "Commonwealth Electoral Divisions 2021 (ABS)", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "POA_2021": { + "layerName": "POA_2021", + "server": "https://tiles.terria.io/POA_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-POA_2021.json", + "uniqueIdProp": "FID", + "regionProp": "POA_CODE_2021", + "nameProp": "POA_CODE_2021", + "aliases": [ + "poa_code_2021", + "poa_code", + "poa", + "postcode_2021", + "postcode" + ], + "description": "Postal Areas 2021 (ABS)", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "TR_2021": { + "layerName": "TR_2021", + "server": "https://tiles.terria.io/TR_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-TR_2021.json", + "uniqueIdProp": "FID", + "regionProp": "TR_CODE_2021", + "nameProp": "TR_NAME_2021", + "aliases": ["tr_code_2021", "tr_code", "tr"], + "description": "Tourism Regions 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "TR_NAME_2021": { + "layerName": "TR_2021", + "server": "https://tiles.terria.io/TR_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-TR_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "TR_NAME_2021", + "nameProp": "TR_NAME_2021", + "aliases": ["tr_name_2021", "tr_name"], + "description": "Tourism Regions 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SUA_2021": { + "layerName": "SUA_2021", + "server": "https://tiles.terria.io/SUA_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SUA_2021.json", + "uniqueIdProp": "FID", + "regionProp": "SUA_CODE_2021", + "nameProp": "SUA_NAME_2021", + "aliases": ["sua_code_2021", "sua_code", "sua"], + "description": "Significant Urban Areas 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SUA_NAME_2021": { + "layerName": "SUA_2021", + "server": "https://tiles.terria.io/SUA_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SUA_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "SUA_NAME_2021", + "nameProp": "SUA_NAME_2021", + "aliases": ["sua_name_2022", "sua_name"], + "description": "Significant Urban Areas 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "UCL_2021": { + "layerName": "UCL_2021", + "server": "https://tiles.terria.io/UCL_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-UCL_2021.json", + "uniqueIdProp": "FID", + "regionProp": "UCL_CODE_2021", + "nameProp": "UCL_NAME_2021", + "aliases": ["ucl_code_2021", "ucl_code", "ucl"], + "description": "Urban Centres and Localities 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "UCL_NAME_2021": { + "layerName": "UCL_2021", + "server": "https://tiles.terria.io/UCL_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-UCL_NAME_2021.json", + "uniqueIdProp": "FID", + "regionProp": "UCL_NAME_2021", + "nameProp": "UCL_NAME_2021", + "aliases": ["ucl_name_2021", "ucl_name"], + "description": "Urban Centres and Localities 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SOS_2021": { + "layerName": "SOS_2021", + "server": "https://tiles.terria.io/SOS_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SOS_2021.json", + "uniqueIdProp": "FID", + "regionProp": "SOS_CODE_2021", + "nameProp": "SOS_NAME_2021", + "aliases": ["sos_code_2021", "sos_code", "sos"], + "description": "Section of State 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, + "SOSR_2021": { + "layerName": "SOSR_2021", + "server": "https://tiles.terria.io/SOSR_2021/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 12, + "serverMinZoom": 0, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SOSR_2021.json", + "uniqueIdProp": "FID", + "regionProp": "SOSR_CODE_2021", + "nameProp": "SOSR_NAME_2021", + "aliases": ["sosr_code_2021", "sosr_code", "sosr"], + "description": "Section of State Range 2021", + "bbox": [96.81, -43.74, 168, -9.14] + }, "SA1_2011": { "layerName": "FID_SA1_2011_AUST", "server": "https://vector-tiles.terria.io/FID_SA1_2011_AUST/{z}/{x}/{y}.pbf", @@ -10,7 +451,7 @@ "aliases": ["sa1_code_2011", "sa1_maincode_2011"], "digits": 11, "description": "Statistical Area Level 1 2011 (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA1_2011_AUST_SA1_MAIN11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA1_2011_AUST_SA1_MAIN11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -31,7 +472,7 @@ "aliases": ["sa1_7digitcode_2011"], "digits": 7, "description": "Statistical Area Level 1 2011 by 7-dig code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA1_2011_AUST_SA1_7DIG11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA1_2011_AUST_SA1_7DIG11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -59,7 +500,7 @@ "aliases": ["sa1_code_2016", "sa1_maincode_2016"], "nameProp": "SA1_7DIG16", "description": "Statistical Area Level 1 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA1_2016_AUST_SA1_MAIN16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA1_2016_AUST_SA1_MAIN16.json" }, "SA1_7DIGIT_2016": { "layerName": "SA1_2016_AUST", @@ -77,7 +518,7 @@ "aliases": ["sa1_7digitcode", "sa1_7digitcode_2016"], "nameProp": "SA1_7DIG16", "description": "Statistical Area Level 1 2016 by 7-dig code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA1_2016_AUST_SA1_7DIG16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA1_2016_AUST_SA1_7DIG16.json" }, "SA1_2021": { "layerName": "SA1_2021", @@ -86,7 +527,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA1_2021_SA1_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA1_2021_SA1_2021.json", "regionProp": "SA1_CODE21", "nameProp": "SA1_CODE21", "aliases": ["sa1_code_2021", "sa1_maincode_2021", "sa1", "sa1_code"], @@ -102,7 +543,7 @@ "aliases": ["sa4_code_2011", "sa4_maincode_2011"], "digits": 3, "description": "Statistical Area Level 4 2011 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA4_2011_AUST_SA4_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA4_2011_AUST_SA4_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -122,7 +563,7 @@ "regionProp": "SA4_NAME11", "aliases": ["sa4_name_2011"], "description": "Statistical Area Level 4 2011 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA4_2011_AUST_SA4_NAME11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA4_2011_AUST_SA4_NAME11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -150,7 +591,7 @@ "aliases": ["sa4_maincode_2016", "sa4_code_2016"], "nameProp": "SA4_NAME16", "description": "Statistical Area Level 4 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" }, "SA4_NAME_2016": { "layerName": "SA4_2016_AUST", @@ -168,7 +609,7 @@ "aliases": ["sa4_name_2016"], "nameProp": "SA4_NAME16", "description": "Statistical Area Level 4 2016 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA4_2016_AUST_SA4_NAME16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_NAME16.json" }, "SA4_2021": { "layerName": "SA4_2021", @@ -177,7 +618,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA4_2021_SA4_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2021_SA4_2021.json", "regionProp": "SA4_CODE21", "nameProp": "SA4_NAME21", "aliases": ["sa4_code_2021", "sa4_maincode_2021", "sa4", "sa4_code"], @@ -191,7 +632,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA4_NAME_2021_SA4_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_NAME_2021_SA4_2021.json", "regionProp": "SA4_NAME21", "nameProp": "SA4_NAME21", "aliases": ["sa4_name_2021", "sa4_name"], @@ -207,7 +648,7 @@ "aliases": ["sa3_code_2011", "sa3_maincode_2011"], "digits": 5, "description": "Statistical Area Level 3 2011 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA3_2011_AUST_SA3_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA3_2011_AUST_SA3_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -228,7 +669,7 @@ "aliases": ["sa3_name_2011"], "digits": 5, "description": "Statistical Area Level 3 2011 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA3_2011_AUST_SA3_NAME11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA3_2011_AUST_SA3_NAME11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -256,7 +697,7 @@ "aliases": ["sa3_code_2016", "sa3_maincode_2016"], "nameProp": "SA3_NAME16", "description": "Statistical Area Level 3 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA3_2016_AUST_SA3_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA3_2016_AUST_SA3_CODE16.json" }, "SA3_NAME_2016": { "layerName": "SA3_2016_AUST", @@ -274,7 +715,7 @@ "aliases": ["sa3_name_2016"], "nameProp": "SA3_NAME16", "description": "Statistical Area Level 3 2016 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA3_2016_AUST_SA3_NAME16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA3_2016_AUST_SA3_NAME16.json" }, "SA3_2021": { "layerName": "SA3_2021", @@ -283,7 +724,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA3_2021_SA3_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA3_2021_SA3_2021.json", "regionProp": "SA3_CODE21", "nameProp": "SA3_NAME21", "aliases": ["sa3_code_2021", "sa3_maincode_2021", "sa3", "sa3_code"], @@ -297,7 +738,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA3_NAME_2021_SA3_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA3_NAME_2021_SA3_2021.json", "regionProp": "SA3_NAME21", "nameProp": "SA3_NAME21", "aliases": ["sa3_name_2021", "sa3_name"], @@ -313,7 +754,7 @@ "aliases": ["sa2_code_2011", "sa2_maincode_2011"], "digits": 9, "description": "Statistical Area Level 2 2011 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA2_2011_AUST_SA2_MAIN11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA2_2011_AUST_SA2_MAIN11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -334,7 +775,7 @@ "aliases": ["sa2_5digitcode_2011"], "digits": 5, "description": "Statistical Area Level 2 2011 by 5-dig code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA2_2011_AUST_SA2_5DIG11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA2_2011_AUST_SA2_5DIG11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -354,7 +795,7 @@ "regionProp": "SA2_NAME11", "aliases": ["sa2_name_2011"], "description": "Statistical Area Level 2 2011 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SA2_2011_AUST_SA2_NAME11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SA2_2011_AUST_SA2_NAME11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -382,7 +823,7 @@ "aliases": ["sa2_code_2016", "sa2_maincode_2016"], "nameProp": "SA2_NAME16", "description": "Statistical Area Level 2 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA2_2016_AUST_SA2_MAIN16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA2_2016_AUST_SA2_MAIN16.json" }, "SA2_5DIG_2016": { "layerName": "SA2_2016_AUST", @@ -400,7 +841,7 @@ "aliases": ["sa2_5digitcode", "sa2_5digitcode_2016"], "nameProp": "SA2_NAME16", "description": "Statistical Area Level 2 2016 by 5-dig code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA2_2016_AUST_SA2_5DIG16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA2_2016_AUST_SA2_5DIG16.json" }, "SA2_NAME_2016": { "layerName": "SA2_2016_AUST", @@ -418,7 +859,7 @@ "aliases": ["sa2_name_2016"], "nameProp": "SA2_NAME16", "description": "Statistical Area Level 2 2016 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA2_2016_AUST_SA2_NAME16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA2_2016_AUST_SA2_NAME16.json" }, "SA2_2021": { "layerName": "SA2_2021", @@ -427,7 +868,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA2_2021_SA2_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA2_2021_SA2_2021.json", "regionProp": "SA2_CODE21", "nameProp": "SA2_NAME21", "aliases": ["sa2_code_2021", "sa2_maincode_2021", "sa2", "sa2_code"], @@ -441,7 +882,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SA2_NAME_2021_SA2_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SA2_NAME_2021_SA2_2021.json", "regionProp": "SA2_NAME21", "nameProp": "SA2_NAME21", "aliases": ["sa2_name_2021", "sa2_name"], @@ -457,7 +898,7 @@ "aliases": ["ssc_code_2011"], "digits": 5, "description": "ABS approximations of suburbs by code (2011)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SSC_2011_AUST_SSC_CODE.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SSC_2011_AUST_SSC_CODE.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -477,7 +918,7 @@ "regionProp": "SSC_NAME", "aliases": ["ssc_name_2011"], "description": "ABS approximations of suburbs by name (2011)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SSC_2011_AUST_SSC_NAME.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SSC_2011_AUST_SSC_NAME.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -505,7 +946,7 @@ "aliases": ["ssc_code_2016", "ssc_code", "ssc"], "nameProp": "SSC_NAME16", "description": "ABS approximations of suburbs by code (2016)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SSC_2016_AUST_SSC_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SSC_2016_AUST_SSC_CODE16.json" }, "SSC_NAME_2016": { "layerName": "SSC_2016_AUST", @@ -523,7 +964,7 @@ "aliases": ["ssc_name_2016", "ssc_name", "suburb"], "nameProp": "SSC_NAME16", "description": "ABS approximations of suburbs by name (2016)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SSC_2016_AUST_SSC_NAME16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SSC_2016_AUST_SSC_NAME16.json" }, "LGA_2021": { "layerName": "LGA_2021", @@ -532,10 +973,10 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-LGA_2021_LGA_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-LGA_2021_LGA_2021.json", "regionProp": "LGA_CODE21", "nameProp": "LGA_NAME21", - "aliases": ["lga_code_2021", "lga_code_2020", "lga_code", "lga"], + "aliases": ["lga_code_2021", "lga_code_2020"], "description": "Local Government Areas 2021 by code (ABS)", "bbox": [96.81, -43.75, 168, -9.14] }, @@ -546,7 +987,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-LGA_2019_LGA_2019.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-LGA_2019_LGA_2019.json", "regionProp": "LGA_CODE19", "nameProp": "LGA_NAME19", "aliases": ["lga_code_2019"], @@ -568,7 +1009,7 @@ "nameProp": "LGA_NAME18", "aliases": ["lga_code_2018"], "description": "Local Government Areas 2018 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-LGA_2018_AUST_LGA_CODE18.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-LGA_2018_AUST_LGA_CODE18.json" }, "LGA_2017": { "layerName": "LGA_2017_AUST", @@ -585,7 +1026,7 @@ "nameProp": "LGA_NAME17", "aliases": ["lga_code_2017"], "description": "Local Government Areas 2017 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-LGA_2017_AUST_LGA_CODE17.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-LGA_2017_AUST_LGA_CODE17.json" }, "LGA_2016": { "layerName": "LGA_2016_AUST", @@ -603,7 +1044,7 @@ "aliases": ["lga_code_2016"], "nameProp": "LGA_NAME16", "description": "Local Government Areas 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-LGA_2016_AUST_LGA_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-LGA_2016_AUST_LGA_CODE16.json" }, "LGA_2015": { "layerName": "FID_LGA_2015_AUST", @@ -614,7 +1055,7 @@ "aliases": ["lga_code_2015", "lga_code_2014"], "digits": 5, "description": "Local Government Areas 2015 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -635,7 +1076,7 @@ "aliases": ["lga_code_2013", "lga_code_2012"], "digits": 5, "description": "Local Government Areas 2013 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_LGA_2013_AUST_LGA_CODE13.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2013_AUST_LGA_CODE13.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -656,7 +1097,7 @@ "aliases": ["lga_code_2011", "lga_code_2010"], "digits": 5, "description": "Local Government Areas 2011 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -703,8 +1144,8 @@ "disambigProp": "STE_NAME16", "disambigRegionId": "STE_NAME_2016", "description": "Local Government Areas 2011 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_LGA_2011_AUST_LGA_NAME11.json", - "regionDisambigIdsFile": "build/TerriaJS/data/regionids/region_map-FID_LGA_2011_AUST_STE_NAME11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_NAME11.json", + "regionDisambigIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_STE_NAME11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -729,18 +1170,12 @@ -9.142175976999962 ], "regionProp": "POA_CODE16", - "aliases": [ - "poa_code_2016", - "poa_code", - "poa", - "postcode_2016", - "postcode" - ], + "aliases": ["poa_code_2016", "postcode_2016"], "digits": 4, "dataReplacements": [["^(?=\\d\\d\\d$)", "0"]], "nameProp": "POA_NAME16", "description": "Postal areas 2016 (ABS approximation)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-POA_2016_AUST_POA_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-POA_2016_AUST_POA_CODE16.json" }, "POA_2011": { "layerName": "FID_POA_2011_AUST", @@ -757,7 +1192,7 @@ "digits": 4, "dataReplacements": [["^(?=\\d\\d\\d$)", "0"]], "description": "Postal areas 2011 (ABS approximation)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_POA_2011_AUST_POA_CODE.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_POA_2011_AUST_POA_CODE.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -776,10 +1211,10 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CED_CODE18_CED_2018.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CED_CODE18_CED_2018.json", "regionProp": "CED_CODE18", "nameProp": "CED_NAME18", - "aliases": ["ced", "ced_code", "ced_2018", "ced_code_2018"], + "aliases": ["ced_2018", "ced_code_2018"], "description": "Commonwealth electoral divisions 2018 by code (ABS)", "bbox": [96.82, -43.74, 159.11, -9.14] }, @@ -791,10 +1226,10 @@ "serverMinZoom": 0, "serverMaxZoom": 28, "digits": 3, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CED_NAME18_CED_2018.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CED_NAME18_CED_2018.json", "regionProp": "CED_NAME18", "nameProp": "CED_NAME18", - "aliases": ["ced_name", "ced_name_2018"], + "aliases": ["ced_name_2018"], "description": "Commonwealth electoral divisions 2018 by name (ABS)", "bbox": [96.82, -43.74, 159.11, -9.14] }, @@ -807,7 +1242,7 @@ "aliases": ["ced_code_2016"], "digits": 3, "description": "Commonwealth electoral divisions 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_CED_2016_AUST_CED_CODE16.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2016_AUST_CED_CODE16.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -827,7 +1262,7 @@ "regionProp": "CED_NAME16", "aliases": ["ced_name_2016"], "description": "Commonwealth electoral divisions 2016 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_CED_2016_AUST_CED_NAME16.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2016_AUST_CED_NAME16.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -848,7 +1283,7 @@ "aliases": ["ced_code_2013"], "digits": 3, "description": "Commonwealth electoral divisions 2013 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_CED_2013_AUST_CED_CODE13.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2013_AUST_CED_CODE13.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -868,7 +1303,7 @@ "regionProp": "CED_NAME13", "aliases": ["ced_name_2013"], "description": "Commonwealth electoral divisions 2013 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_CED_2013_AUST_CED_NAME13.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2013_AUST_CED_NAME13.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -889,7 +1324,7 @@ "aliases": ["ced_code_2011"], "digits": 3, "description": "Commonwealth electoral divisions 2011 by code (ABS)", - "disabledregionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_CED_2011_AUST_CED_CODE.json", + "disabledregionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2011_AUST_CED_CODE.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -900,7 +1335,7 @@ -9.142175976999999 ], "nameProp": "CED_NAME", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_CED_2011_AUST_CED_CODE.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2011_AUST_CED_CODE.json" }, "CED_NAME_2011": { "layerName": "FID_CED_2011_AUST", @@ -910,7 +1345,7 @@ "regionProp": "CED_NAME", "aliases": ["ced_name_2011"], "description": "Commonwealth electoral divisions 2011 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_CED_2011_AUST_CED_NAME.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2011_AUST_CED_NAME.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -931,7 +1366,7 @@ "aliases": ["divisionid", "com_elb_id_2016", "com_elb_id", "com_elb"], "digits": 3, "description": "Commonwealth electoral districts 2016 by code (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_COM20160509_ELB_DIV_ID.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_COM20160509_ELB_DIV_ID.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -950,7 +1385,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ELB_NAME_2021_ELB_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ELB_NAME_2021_ELB_2021.json", "regionProp": "Elect_div", "nameProp": "Elect_div", "aliases": ["com_elb_name_2021", "com_elb_name", "divisionnm"], @@ -963,7 +1398,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMinZoom": 0, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ELB_NAME_2019_ELB_2019.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ELB_NAME_2019_ELB_2019.json", "regionProp": "Sortname", "nameProp": "Sortname", "aliases": ["com_elb_name_2019"], @@ -982,7 +1417,7 @@ "textCodes": true, "aliases": ["com_elb_name_2016"], "description": "Commonwealth electoral districts 2016 by name (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_COM20160509_ELB_ELECT_DIV.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_COM20160509_ELB_ELECT_DIV.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1003,7 +1438,7 @@ "textCodes": true, "aliases": ["com_elb_name_2011"], "description": "Commonwealth electoral districts 2011 by name (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_COM20111216_ELB_region_ELECT_DIV.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_COM20111216_ELB_region_ELECT_DIV.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1027,7 +1462,7 @@ "nameProp": "ced_name_2013", "aliases": ["com_elb_id_2013"], "description": "Commonwealth electoral districts 2013 by id (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CommonwealthElectoralDivision_2013_ced_code_2013.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CommonwealthElectoralDivision_2013_ced_code_2013.json" }, "COM_ELB_NAME_2013": { "layerName": "CommonwealthElectoralDivision_2013", @@ -1041,7 +1476,7 @@ "nameProp": "ced_name_2013", "aliases": ["com_elb_name_2013"], "description": "Commonwealth electoral districts 2013 by name (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CommonwealthElectoralDivision_2013_ced_name_2013.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CommonwealthElectoralDivision_2013_ced_name_2013.json" }, "COM_ELB_ID_2010": { "layerName": "CommonwealthElectoralDivision_2010", @@ -1055,7 +1490,7 @@ "nameProp": "ced_name_2010", "aliases": ["com_elb_id_2010"], "description": "Commonwealth electoral districts 2010 by code (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CommonwealthElectoralDivision_2010_ced_code_2010.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CommonwealthElectoralDivision_2010_ced_code_2010.json" }, "COM_ELB_NAME_2010": { "layerName": "CommonwealthElectoralDivision_2010", @@ -1069,7 +1504,7 @@ "nameProp": "ced_name_2010", "aliases": ["com_elb_name_2010"], "description": "Commonwealth electoral districts 2010 by name (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CommonwealthElectoralDivision_2010_ced_name_2010.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CommonwealthElectoralDivision_2010_ced_name_2010.json" }, "COM_ELB_ID_2007": { "layerName": "CommonwealthElectoralDivision_2007", @@ -1083,7 +1518,7 @@ "nameProp": "ced_name_2007", "aliases": ["com_elb_id_2007"], "description": "Commonwealth electoral districts 2007 by code (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CommonwealthElectoralDivision_2007_ced_code_2007.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CommonwealthElectoralDivision_2007_ced_code_2007.json" }, "COM_ELB_NAME_2007": { "layerName": "CommonwealthElectoralDivision_2007", @@ -1097,7 +1532,7 @@ "nameProp": "ced_name_2007", "aliases": ["com_elb_name_2007"], "description": "Commonwealth electoral districts 2007 by name (AEC)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-CommonwealthElectoralDivision_2007_ced_name_2007.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-CommonwealthElectoralDivision_2007_ced_name_2007.json" }, "SED_CODE18": { "layerName": "SED_2018", @@ -1106,10 +1541,10 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SED_CODE18_SED_2018.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_CODE18_SED_2018.json", "regionProp": "SED_CODE18", "nameProp": "SED_NAME18", - "aliases": ["sed", "sed_code", "sed_2018", "sed_code_2018"], + "aliases": ["sed_2018", "sed_code_2018"], "description": "State electoral divisions 2018 by code (ABS)", "bbox": [96.82, -43.74, 159.11, -9.14] }, @@ -1120,10 +1555,10 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SED_NAME18_SED_2018.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_NAME18_SED_2018.json", "regionProp": "SED_NAME18", "nameProp": "SED_NAME18", - "aliases": ["sed_name", "sed_name_2018"], + "aliases": ["sed_name_2018"], "description": "State electoral divisions 2018 by name (ABS)", "bbox": [96.82, -43.74, 159.11, -9.14] }, @@ -1134,7 +1569,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SED_CODE18_SED_2016.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_CODE18_SED_2016.json", "regionProp": "SED_CODE16", "nameProp": "SED_NAME16", "aliases": ["sed_2016", "sed_code_2016", "sed_code16"], @@ -1148,7 +1583,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-SED_NAME16_SED_2016.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-SED_NAME16_SED_2016.json", "regionProp": "SED_NAME16", "nameProp": "SED_NAME16", "aliases": ["sed_name_2016", "sed_name16"], @@ -1164,7 +1599,7 @@ "aliases": ["sed_code_2011"], "digits": 5, "description": "State electoral divisions 2011 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SED_2011_AUST_SED_CODE.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SED_2011_AUST_SED_CODE.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1184,7 +1619,7 @@ "regionProp": "SED_NAME", "aliases": ["sed_name_2011"], "description": "State electoral divisions 2011 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SED_2011_AUST_SED_NAME.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SED_2011_AUST_SED_NAME.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1205,7 +1640,7 @@ "aliases": ["gccsa_code_2011"], "digits": 5, "description": "Greater capital city statistical areas 2011 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_GCCSA_2011_AUST_GCC_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_GCCSA_2011_AUST_GCC_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1225,7 +1660,7 @@ "regionProp": "GCC_NAME11", "aliases": ["gccsa_name_2011"], "description": "Greater capital city statistical areas 2011 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_GCCSA_2011_AUST_GCC_NAME11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_GCCSA_2011_AUST_GCC_NAME11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1253,7 +1688,7 @@ "aliases": ["gccsa_code_2016"], "nameProp": "GCC_NAME16", "description": "Greater capital city statistical areas 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-GCCSA_2016_AUST_GCC_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-GCCSA_2016_AUST_GCC_CODE16.json" }, "GCCSA_NAME_2016": { "layerName": "GCCSA_2016_AUST", @@ -1271,7 +1706,7 @@ "aliases": ["gccsa_name_2016"], "nameProp": "GCC_NAME16", "description": "Greater capital city statistical areas 2016 by name (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-GCCSA_2016_AUST_GCC_NAME16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-GCCSA_2016_AUST_GCC_NAME16.json" }, "GCCSA_2021": { "layerName": "GCCSA_2021", @@ -1280,7 +1715,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-GCCSA_2021_GCCSA_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-GCCSA_2021_GCCSA_2021.json", "regionProp": "GCC_CODE21", "nameProp": "GCC_NAME21", "aliases": ["gccsa_code_2021", "gccsa_code", "gccsa"], @@ -1294,7 +1729,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-GCCSA_NAME_2021_GCCSA_2021.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-GCCSA_NAME_2021_GCCSA_2021.json", "regionProp": "GCC_NAME21", "nameProp": "GCC_NAME21", "aliases": ["gccsa_name_2021", "gccsa_name"], @@ -1307,10 +1742,10 @@ "analyticsWmsLayerName": "region_map:FID_SUA_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "SUA_CODE11", - "aliases": ["sua_code_2011", "sua_code", "sua"], + "aliases": ["sua_code_2011"], "digits": 4, "description": "Significant urban areas 2011 by code", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SUA_2011_AUST_SUA_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SUA_2011_AUST_SUA_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1328,11 +1763,11 @@ "analyticsWmsLayerName": "region_map:FID_SUA_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "SUA_NAME11", - "aliases": ["sua_name_2011", "sua_name"], + "aliases": ["sua_name_2011"], "description": "Significant urban areas 2011 by name", "serverReplacements": [["[^A-Za-z]", ""]], "dataReplacements": [["[^A-Za-z]", ""]], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SUA_2011_AUST_SUA_NAME11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SUA_2011_AUST_SUA_NAME11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1353,7 +1788,7 @@ "aliases": ["ste_code_2011"], "description": "States and Territories 2011 by code (ABS)", "digits": 1, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_STE_2011_AUST_STE_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_STE_2011_AUST_STE_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1379,18 +1814,15 @@ ], "regionProp": "STE_CODE16", "aliases": [ - "ste", - "ste_code", "ste_code_2016", "ste_code_2017", "ste_code_2018", "ste_code_2019", - "ste_code_2020", - "ste_code_2021" + "ste_code_2020" ], "nameProp": "STE_NAME16", "description": "States and Territories 2016 by code (ABS)", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-STE_2016_AUST_STE_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-STE_2016_AUST_STE_CODE16.json" }, "SOS": { "layerName": "FID_SOS_2011_AUST", @@ -1398,10 +1830,10 @@ "analyticsWmsLayerName": "region_map:FID_SOS_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "SOS_CODE11", - "aliases": ["sos_code_2011", "sos_code", "sos"], + "aliases": ["sos_code_2011"], "digits": 2, "description": "Section of state 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SOS_2011_AUST_SOS_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SOS_2011_AUST_SOS_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1419,10 +1851,10 @@ "analyticsWmsLayerName": "region_map:FID_SOSR_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "SSR_CODE11", - "aliases": ["sosr_code_2011", "sosr_code", "sosr"], + "aliases": ["sosr_code_2011"], "digits": 3, "description": "Section of state range 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_SOSR_2011_AUST_SSR_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_SOSR_2011_AUST_SSR_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1440,10 +1872,10 @@ "analyticsWmsLayerName": "region_map:FID_UCL_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "UCL_CODE11", - "aliases": ["ucl_code_2011", "ucl_code", "ucl"], + "aliases": ["ucl_code_2011"], "digits": 6, "description": "Urban centres and localities 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_UCL_2011_AUST_UCL_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_UCL_2011_AUST_UCL_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1461,10 +1893,10 @@ "analyticsWmsLayerName": "region_map:FID_IREG_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "IR_CODE11", - "aliases": ["ireg_code_2011", "ireg_code", "ireg"], + "aliases": ["ireg_code_2011"], "digits": 3, "description": "Indigenous regions 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_IREG_2011_AUST_IR_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_IREG_2011_AUST_IR_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1482,10 +1914,10 @@ "analyticsWmsLayerName": "region_map:FID_ILOC_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "IL_CODE11", - "aliases": ["iloc_code_2011", "iloc_code", "iloc"], + "aliases": ["iloc_code_2011"], "digits": 8, "description": "Indigenous locations 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_ILOC_2011_AUST_IL_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_ILOC_2011_AUST_IL_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1503,10 +1935,10 @@ "analyticsWmsLayerName": "region_map:FID_IARE_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "IA_CODE11", - "aliases": ["iare_code_2011", "iare_code", "iare"], + "aliases": ["iare_code_2011"], "digits": 6, "description": "Indigenous areas 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_IARE_2011_AUST_IA_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_IARE_2011_AUST_IA_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1524,10 +1956,10 @@ "analyticsWmsLayerName": "region_map:FID_RA_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "RA_CODE11", - "aliases": ["ra_code_2011", "ra_code", "ra"], + "aliases": ["ra_code_2011"], "digits": 2, "description": "Remoteness areas 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_RA_2011_AUST_RA_CODE11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_RA_2011_AUST_RA_CODE11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1545,10 +1977,10 @@ "analyticsWmsLayerName": "region_map:FID_TR_2015_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "TR_CODE15", - "aliases": ["tr_code_2015", "tr_code", "tr"], + "aliases": ["tr_code_2015"], "digits": 5, "description": "Tourism regions 2015", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_TR_2015_AUST_TR_CODE15.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_TR_2015_AUST_TR_CODE15.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1569,7 +2001,7 @@ "aliases": ["tr_code_2013", "tr_2013"], "digits": 5, "description": "Tourism regions 2013", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_TR_2013_AUST_TR_CODE13.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_TR_2013_AUST_TR_CODE13.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1590,7 +2022,7 @@ "aliases": ["nrmr", "nrmr_code", "nrmr_code_2011"], "digits": 3, "description": "Natural resource management regions 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_NRMR_2011_AUST_NRMR_CODE.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_NRMR_2011_AUST_NRMR_CODE.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1610,7 +2042,7 @@ "regionProp": "NRMR_NAME", "aliases": ["nrmr_name"], "description": "Natural resource management regions 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_NRMR_2011_AUST_NRMR_NAME.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_NRMR_2011_AUST_NRMR_NAME.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1628,10 +2060,10 @@ "analyticsWmsLayerName": "region_map:FID_ADD_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "ADD_CODE", - "aliases": ["add", "add_code", "add_code_2011"], + "aliases": ["add_code_2011"], "digits": 3, "description": "Australian drainage divisions 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_ADD_2011_AUST_ADD_CODE.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_ADD_2011_AUST_ADD_CODE.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1649,9 +2081,9 @@ "analyticsWmsLayerName": "region_map:FID_ADD_2011_AUST", "analyticsWmsServer": "http://geoserver.nationalmap.nicta.com.au/region_map/ows", "regionProp": "ADD_NAME", - "aliases": ["add_name"], + "aliases": ["add_name_2011"], "description": "Australian drainage divisions by name 2011", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_ADD_2011_AUST_ADD_NAME.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_ADD_2011_AUST_ADD_NAME.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1676,10 +2108,10 @@ -9.142175976999962 ], "regionProp": "ADD_CODE16", - "aliases": ["add_code_2016", "add_code", "add"], + "aliases": ["add_code_2016"], "nameProp": "ADD_NAME16", "description": "Australian drainage divisions 2016", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ADD_2016_AUST_ADD_CODE16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ADD_2016_AUST_ADD_CODE16.json" }, "ADD_NAME_2016": { "layerName": "ADD_2016_AUST", @@ -1694,10 +2126,10 @@ -9.142175976999962 ], "regionProp": "ADD_NAME16", - "aliases": ["add_name_2016", "add_name"], + "aliases": ["add_name_2016"], "nameProp": "ADD_NAME16", "description": "Australian drainage divisions by name 2016", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ADD_2016_AUST_ADD_NAME16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ADD_2016_AUST_ADD_NAME16.json" }, "PHN": { "layerName": "FID_PHN_boundaries_AUS_Sep2015_V5", @@ -1708,7 +2140,7 @@ "aliases": ["phn_code_2015", "phn_code", "phn"], "digits": 6, "description": "Primary health networks", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_PHN_boundaries_AUS_Sep2015_V5_PHN_Code.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_PHN_boundaries_AUS_Sep2015_V5_PHN_Code.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1747,7 +2179,7 @@ ["^8$", "Australian Capital Territory"], ["^9$", "Other Territories"] ], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_STE_2011_AUST_STE_NAME11.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_STE_2011_AUST_STE_NAME11.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1794,7 +2226,7 @@ ["^8$", "Australian Capital Territory"], ["^9$", "Other Territories"] ], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-STE_2016_AUST_STE_NAME16.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-STE_2016_AUST_STE_NAME16.json" }, "SLA": { "layerName": "fid_asgc06_sla", @@ -1821,7 +2253,7 @@ 96.81676569599999, -43.740509602999985, 159.10921900799997, -9.142175976999999 ], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-fid_asgc06_sla_SLA_CODE06.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_sla_SLA_CODE06.json" }, "SLA_5DIGITCODE": { "layerName": "fid_asgc06_sla", @@ -1842,7 +2274,7 @@ 96.81676569599999, -43.740509602999985, 159.10921900799997, -9.142175976999999 ], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-fid_asgc06_sla_SLA_5DIGIT.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_sla_SLA_5DIGIT.json" }, "SLA_NAME": { "layerName": "fid_asgc06_sla", @@ -1862,7 +2294,7 @@ 96.81676569599999, -43.740509602999985, 159.10921900799997, -9.142175976999999 ], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-fid_asgc06_sla_SLA_NAME06.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_sla_SLA_NAME06.json" }, "CD": { "layerName": "fid_asgc06_cd", @@ -1883,7 +2315,7 @@ -9.142175976999999 ], "nameProp": "CD_CODE06", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-fid_asgc06_cd_CD_CODE06.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_cd_CD_CODE06.json" }, "CNT2": { "layerName": "FID_TM_WORLD_BORDERS", @@ -1894,7 +2326,7 @@ "aliases": ["cnt2", "iso2"], "digits": 2, "textCodes": true, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_TM_WORLD_BORDERS_ISO2.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_TM_WORLD_BORDERS_ISO2.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1915,7 +2347,7 @@ "aliases": ["cnt3", "iso3"], "digits": 3, "textCodes": true, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_TM_WORLD_BORDERS_ISO3.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_TM_WORLD_BORDERS_ISO3.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1935,7 +2367,7 @@ "regionProp": "NAME", "textCodes": true, "aliases": ["country"], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_TM_WORLD_BORDERS_NAME.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_TM_WORLD_BORDERS_NAME.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1965,7 +2397,7 @@ "aus_code_2021", "aus_code_2022" ], - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_AUS_2011_AUST_AUS_CODE.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_AUS_2011_AUST_AUS_CODE.json", "serverType": "MVT", "serverSubdomains": [], "serverMinZoom": 0, @@ -1993,7 +2425,7 @@ "aliases": ["esa", "esa_code", "esa_code_2009"], "nameProp": "ESA_NAME", "description": "Employment Service Areas 2009-2015+", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_AUS_ESA_09_ESA_CODE.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_AUS_ESA_09_ESA_CODE.json" }, "ESA_NAME_09": { "layerName": "FID_AUS_ESA_09", @@ -2011,7 +2443,7 @@ "aliases": ["esa_name", "esa_name_2009"], "nameProp": "ESA_NAME", "description": "Employment Service Areas 2009-2015+", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-FID_AUS_ESA_09_ESA_NAME.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_AUS_ESA_09_ESA_NAME.json" }, "IBRA7_REG": { "layerName": "ibra7_regions", @@ -2029,7 +2461,7 @@ "aliases": ["ibra7_reg", "ibra7_reg_code"], "nameProp": "REG_NAME_7", "description": "IBRA Regions v7", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ibra7_regions_REG_CODE_7.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ibra7_regions_REG_CODE_7.json" }, "IBRA7_REG_NAME": { "layerName": "ibra7_regions", @@ -2047,7 +2479,7 @@ "aliases": ["ibra7_reg_name"], "nameProp": "REG_NAME_7", "description": "IBRA Regions v7", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ibra7_regions_REG_NAME_7.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ibra7_regions_REG_NAME_7.json" }, "IBRA7_SUB": { "layerName": "ibra7_subregions", @@ -2065,7 +2497,7 @@ "aliases": ["ibra7_sub", "ibra7_sub_code"], "nameProp": "SUB_NAME_7", "description": "IBRA Subregions v7", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ibra7_subregions_SUB_CODE_7.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ibra7_subregions_SUB_CODE_7.json" }, "IBRA7_SUB_NAME": { "layerName": "ibra7_subregions", @@ -2083,7 +2515,7 @@ "aliases": ["ibra7_sub_name"], "nameProp": "SUB_NAME_7", "description": "IBRA Subregions v7", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ibra7_subregions_SUB_NAME_7.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ibra7_subregions_SUB_NAME_7.json" }, "NZ_AU_2017": { "layerName": "NZ_AU2017_HD_Clipped", @@ -2100,7 +2532,7 @@ "nameProp": "AU2017_NAM", "aliases": ["nz_au_code_2017", "nz_au"], "description": "Stats New Zealand Area Units 2017", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-NZ_AU2017_HD_Clipped_AU2017.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-NZ_AU2017_HD_Clipped_AU2017.json" }, "NZ_AU_2017_NAME": { "layerName": "NZ_AU2017_HD_Clipped", @@ -2117,7 +2549,7 @@ "nameProp": "AU2017_NAM", "aliases": ["nz_au_name_2017"], "description": "Stats New Zealand Area Units 2017", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-NZ_AU2017_HD_Clipped_AU2017_NAM.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-NZ_AU2017_HD_Clipped_AU2017_NAM.json" }, "NZ_MB_2017": { "layerName": "NZ_MB2017_HD_Clipped", @@ -2134,7 +2566,7 @@ "nameProp": "MB2017", "aliases": ["nz_mb_code_2017", "nz_mb"], "description": "Stats New Zealand Mesh Blocks 2017", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-NZ_MB2017_HD_Clipped_MB2017.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-NZ_MB2017_HD_Clipped_MB2017.json" }, "AEC_FED_2017_AMLS": { "layerName": "AEC_FED_2017_AMLS", @@ -2148,7 +2580,7 @@ "nameProp": "FED_DIV", "aliases": ["fed_code_2017"], "description": "Australian Electoral Commission Commonwealth Electoral Boundaries for ABS Australian Marriage Law Survey", - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-AEC_FED_2017_AMLS_FED_ABB.json" + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-AEC_FED_2017_AMLS_FED_ABB.json" }, "RRA_Name": { "layerName": "Regional_Recovery_Areas", @@ -2157,7 +2589,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-RRA_Name_Regional_Recovery_Areas.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-RRA_Name_Regional_Recovery_Areas.json", "regionProp": "RRA_Name", "nameProp": "RRA_Name", "aliases": ["RRA_NAME", "RRA"], @@ -2174,7 +2606,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ABARES_CODE_ABARES_Ag_Regions.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ABARES_CODE_ABARES_Ag_Regions.json", "regionProp": "AbaresCode", "nameProp": "AbaresName", "aliases": ["abares_code", "abares_region_code"], @@ -2191,7 +2623,7 @@ "serverMaxNativeZoom": 12, "serverMinZoom": 0, "serverMaxZoom": 28, - "regionIdsFile": "build/TerriaJS/data/regionids/region_map-ABARES_NAME_ABARES_Ag_Regions.json", + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-ABARES_NAME_ABARES_Ag_Regions.json", "regionProp": "AbaresName", "nameProp": "AbaresName", "description": "ABARES regions, farm survey statistical aggregation areas", From e75d4bee6c74f02511462c15f863a28410fa12be Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Mon, 23 Oct 2023 11:36:14 +1100 Subject: [PATCH 067/129] Fix tests, and improve some tests by checking for errors on loading --- test/ModelMixins/TableMixinSpec.ts | 92 ++++++++++--------- .../YDYRCatalogFunctionJobSpec.ts | 4 +- .../YDYRCatalogFunctionSpec.ts | 4 +- .../SdmxJson/SdmxJsonCatalogItemSpec.ts | 4 +- .../DimensionSelectorSectionSpec.tsx | 2 +- test/Table/TableStyleSpec.ts | 9 +- 6 files changed, 60 insertions(+), 55 deletions(-) diff --git a/test/ModelMixins/TableMixinSpec.ts b/test/ModelMixins/TableMixinSpec.ts index 57d6200e654..f467749db1e 100644 --- a/test/ModelMixins/TableMixinSpec.ts +++ b/test/ModelMixins/TableMixinSpec.ts @@ -74,24 +74,26 @@ describe("TableMixin", function () { item = new CsvCatalogItem("test", terria, undefined); jasmine.Ajax.install(); + jasmine.Ajax.stubRequest(/.*/).andError({}); + jasmine.Ajax.stubRequest( "build/TerriaJS/data/regionMapping.json" ).andReturn({ responseText: regionMapping }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-STE_2016_AUST_STE_NAME16.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-STE_2016_AUST_STE_NAME16.json" ).andReturn({ responseText: regionIdsSte }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-FID_LGA_2011_AUST_LGA_NAME11.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_NAME11.json" ).andReturn({ responseText: regionIdsLgaName }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json" ).andReturn({ responseText: regionIdsLgaCode }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-FID_LGA_2011_AUST_STE_NAME11.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_STE_NAME11.json" ).andReturn({ responseText: regionIdsLgaNameStates }); }); @@ -103,7 +105,7 @@ describe("TableMixin", function () { let dataSource: CustomDataSource; beforeEach(async function () { item.setTrait(CommonStrata.user, "csvString", LatLonEnumDateIdCsv); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); dataSource = item.mapItems[0]; expect(dataSource instanceof CustomDataSource).toBe(true); }); @@ -170,7 +172,7 @@ describe("TableMixin", function () { it("creates entities for all times", async function () { item.defaultStyle.time.setTrait(CommonStrata.user, "timeColumn", null); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0]; expect(mapItem instanceof CustomDataSource).toBe(true); if (mapItem instanceof CustomDataSource) { @@ -191,7 +193,7 @@ describe("TableMixin", function () { "csvString", LatLonEnumDateIdWithRegionCsv ); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); dataSource = item.mapItems[0]; expect(dataSource instanceof CustomDataSource).toBe(true); }); @@ -265,7 +267,7 @@ describe("TableMixin", function () { it("creates entities for all times", async function () { item.defaultStyle.time.setTrait(CommonStrata.user, "timeColumn", null); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0]; expect(mapItem instanceof CustomDataSource).toBe(true); if (mapItem instanceof CustomDataSource) { @@ -281,7 +283,7 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "csvString", LatLonValCsv) ); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0]; expect(mapItem instanceof CustomDataSource).toBe(true); if (mapItem instanceof CustomDataSource) { @@ -295,7 +297,7 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "removeDuplicateRows", true); }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0]; expect(mapItem instanceof CustomDataSource).toBe(true); if (mapItem instanceof CustomDataSource) { @@ -317,7 +319,7 @@ describe("TableMixin", function () { runInAction(() => item.setTrait(CommonStrata.user, "csvString", LatLonValCsv) ); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const dataSource = item.mapItems[0] as CustomDataSource; const propertyNames = dataSource.entities.values[0].properties?.propertyNames; @@ -331,7 +333,7 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "csvString", BadDatesCsv) ); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0]; expect(mapItem instanceof CustomDataSource).toBe(true); if (mapItem instanceof CustomDataSource) { @@ -344,7 +346,7 @@ describe("TableMixin", function () { let dataSource: CustomDataSource; beforeEach(async function () { item.setTrait(CommonStrata.user, "csvString", ParkingSensorDataCsv); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); dataSource = item.mapItems[0]; expect(dataSource instanceof CustomDataSource).toBe(true); }); @@ -508,7 +510,7 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "csvString", LatLonEnumDateIdCsv); }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.timeDisableDimension).toBeUndefined(); }); @@ -518,7 +520,7 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "showDisableTimeOption", true); }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.timeDisableDimension).toBeDefined(); }); }); @@ -529,7 +531,7 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "csvString", LatLonEnumDateIdCsv); }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.styleDimensions?.options?.length).toBe(4); expect(item.styleDimensions?.options?.[2].id).toBe("value"); @@ -542,7 +544,7 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "showDisableStyleOption", true); }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.styleDimensions?.options?.length).toBe(4); expect(item.styleDimensions?.allowUndefined).toBeTruthy(); @@ -559,7 +561,7 @@ describe("TableMixin", function () { }); }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.styleDimensions?.options?.[2].id).toBe("value"); expect(item.styleDimensions?.options?.[2].name).toBe("Some Title"); @@ -574,7 +576,7 @@ describe("TableMixin", function () { }); }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.styleDimensions?.options?.[2].id).toBe("value"); expect(item.styleDimensions?.options?.[2].name).toBe("Some Style Title"); @@ -583,13 +585,13 @@ describe("TableMixin", function () { it("loads regionProviderLists on loadMapItems", async function () { item.setTrait(CommonStrata.user, "csvString", LatLonEnumDateIdCsv); - await item.loadMetadata(); + (await item.loadMetadata()).throwIfError(); expect(item.regionProviderLists).toBeUndefined(); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); - expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(114); + expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(143); }); it("loads regionProviderLists on loadMapItems - with multiple regionMappingDefinitionsUrl", async function () { @@ -609,16 +611,16 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "csvString", LgaWithDisambigCsv); - await item.loadMetadata(); + (await item.loadMetadata()).throwIfError(); expect(item.regionProviderLists).toBeUndefined(); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.regionProviderLists?.length).toBe(2); expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(2); - expect(item.regionProviderLists?.[1]?.regionProviders.length).toBe(114); + expect(item.regionProviderLists?.[1]?.regionProviders.length).toBe(143); // Item region provider should match from "additionalRegion.json" (as it comes before "build/TerriaJS/data/regionMapping.json") expect(item.activeTableStyle.regionColumn?.regionType?.description).toBe( @@ -644,15 +646,15 @@ describe("TableMixin", function () { item.setTrait(CommonStrata.user, "csvString", LgaWithDisambigCsv); - await item.loadMetadata(); + (await item.loadMetadata()).throwIfError(); expect(item.regionProviderLists).toBeUndefined(); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.regionProviderLists?.length).toBe(1); - expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(114); + expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(143); // Item region provider should match from "build/TerriaJS/data/regionMapping.json" expect(item.activeTableStyle.regionColumn?.regionType?.description).toBe( @@ -667,7 +669,7 @@ describe("TableMixin", function () { item.setTrait("definition", "activeStyle", "0dp"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.legends[0].items.length).toBe(7); expect(item.legends[0].items.map((i) => i.title)).toEqual([ @@ -686,7 +688,7 @@ describe("TableMixin", function () { item.setTrait("definition", "activeStyle", "1dp"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.legends[0].items.length).toBe(7); expect(item.legends[0].items.map((i) => i.title)).toEqual([ @@ -705,7 +707,7 @@ describe("TableMixin", function () { item.setTrait("definition", "activeStyle", "2dp"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.legends[0].items.length).toBe(7); expect(item.legends[0].items.map((i) => i.title)).toEqual([ @@ -724,7 +726,7 @@ describe("TableMixin", function () { item.setTrait("definition", "activeStyle", "3dp"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.legends[0].items.length).toBe(7); expect(item.legends[0].items.map((i) => i.title)).toEqual([ @@ -746,7 +748,7 @@ describe("TableMixin", function () { styles: [{ name: "0dp", title: "Some title" }] }); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.legends[0].title).toBe("0dp"); }); @@ -755,7 +757,7 @@ describe("TableMixin", function () { describe("region mapping - LGA with disambig", function () { beforeEach(async function () { item.setTrait(CommonStrata.user, "csvString", LgaWithDisambigCsv); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); await item.regionProviderLists?.[0] ?.getRegionProvider("LGA_NAME_2011") @@ -833,7 +835,7 @@ describe("TableMixin", function () { ` ); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.activeTableStyle.regionColumn?.name).toBe("lga code-_-2015"); expect(item.activeTableStyle.regionColumn?.regionType?.regionType).toBe( @@ -884,7 +886,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -958,7 +960,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -1067,7 +1069,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -1242,7 +1244,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -1396,7 +1398,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -1571,7 +1573,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -1773,7 +1775,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -1911,7 +1913,7 @@ describe("TableMixin", function () { ]); item.setTrait(CommonStrata.user, "activeStyle", "test-style"); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); const mapItem = item.mapItems[0] as CustomDataSource; @@ -2017,7 +2019,7 @@ describe("TableMixin", function () { it("doesn't pick hidden style as default activeStyle", async function () { item.setTrait(CommonStrata.user, "csvString", ParkingSensorDataCsv); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.activeStyle).toBe("eventid"); @@ -2028,7 +2030,7 @@ describe("TableMixin", function () { }) ]); - await item.loadMapItems(); + (await item.loadMapItems()).throwIfError(); expect(item.activeStyle).toBe("parkflag"); }); diff --git a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts index cb243ee2cff..7fabfa43206 100644 --- a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts +++ b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts @@ -60,11 +60,11 @@ describe("YDYRCatalogFunctionJob", function () { ).andReturn({ responseText: regionMapping }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" ).andReturn({ responseText: sa4regionCodes }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json" ).andReturn({ responseText: lga2011RegionCodes }); terria = new Terria(); diff --git a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts index 2b062b557d6..d36b51abe64 100644 --- a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts +++ b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts @@ -62,11 +62,11 @@ describe("YDYRCatalogFunction", function () { }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" ).andReturn({ responseText: sa4regionCodes }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json" ).andReturn({ responseText: lga2011RegionCodes }); jasmine.Ajax.stubRequest( diff --git a/test/Models/Catalog/SdmxJson/SdmxJsonCatalogItemSpec.ts b/test/Models/Catalog/SdmxJson/SdmxJsonCatalogItemSpec.ts index d27b47aea4a..538fc939f9e 100644 --- a/test/Models/Catalog/SdmxJson/SdmxJsonCatalogItemSpec.ts +++ b/test/Models/Catalog/SdmxJson/SdmxJsonCatalogItemSpec.ts @@ -46,11 +46,11 @@ describe("SdmxJsonCatalogItem", function () { ).andReturn({ responseText: regionMapping }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-STE_2016_AUST_STE_CODE16.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-STE_2016_AUST_STE_CODE16.json" ).andReturn({ responseText: steCodes }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-FID_TM_WORLD_BORDERS_ISO2.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-FID_TM_WORLD_BORDERS_ISO2.json" ).andReturn({ responseText: isoCodes }); jasmine.Ajax.stubRequest( diff --git a/test/ReactViews/DimensionSelectorSectionSpec.tsx b/test/ReactViews/DimensionSelectorSectionSpec.tsx index 073a2fc0500..ccc2f9e185a 100644 --- a/test/ReactViews/DimensionSelectorSectionSpec.tsx +++ b/test/ReactViews/DimensionSelectorSectionSpec.tsx @@ -170,7 +170,7 @@ describe("DimensionSelectorSection", function () { }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json" ).andReturn({ responseText: JSON.stringify( require("../../wwwroot/data/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json") diff --git a/test/Table/TableStyleSpec.ts b/test/Table/TableStyleSpec.ts index 49d303dc467..350b31770f4 100644 --- a/test/Table/TableStyleSpec.ts +++ b/test/Table/TableStyleSpec.ts @@ -44,21 +44,24 @@ describe("TableStyle", function () { "build/TerriaJS/data/regionMapping.json"; jasmine.Ajax.install(); + jasmine.Ajax.stubRequest(/.*/).andError({ + statusText: "Unexpected request, not stubbed" + }); jasmine.Ajax.stubRequest( "build/TerriaJS/data/regionMapping.json" ).andReturn({ responseText: regionMapping }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-SED_CODE18_SED_2018.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-SED_CODE18_SED_2018.json" ).andReturn({ responseText: SedCods }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json" ).andReturn({ responseText: Sa4Codes }); jasmine.Ajax.stubRequest( - "build/TerriaJS/data/regionids/region_map-SA4_2016_AUST_SA4_NAME16.json" + "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_NAME16.json" ).andReturn({ responseText: Sa4Names }); }); From e949c4d1dd1dde9dd98cc5c762446e47ab4889db Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 19:01:58 +1100 Subject: [PATCH 068/129] Add CLUE blocks --- wwwroot/data/regionMapping.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/wwwroot/data/regionMapping.json b/wwwroot/data/regionMapping.json index a2f929b28d2..7d456cd82e5 100644 --- a/wwwroot/data/regionMapping.json +++ b/wwwroot/data/regionMapping.json @@ -2632,6 +2632,20 @@ 96.81694140799998, -43.74050960300003, 159.10921900799997, -9.142175976999999 ] + }, + "City_of_Melbourne_CLUE": { + "layerName": "City_of_Melbourne_CLUE", + "server": "https://tiles.terria.io/City_of_Melbourne_CLUE/{z}/{x}/{y}.pbf", + "serverType": "MVT", + "serverMaxNativeZoom": 16, + "serverMinZoom": 10, + "serverMaxZoom": 28, + "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-City_of_Melbourne_CLUE.json", + "uniqueIdProp": "FID", + "regionProp": "block_id", + "nameProp": "clue_area", + "description": "City of Melbourne Census of Land Use and Employment", + "bbox": [144.88, -37.86, 145, -37.77] } } } From 7b493681ace33769f754d178472ea2d50d2f3b96 Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 19:39:53 +1100 Subject: [PATCH 069/129] Remove atom tracking database https://xkcd.com/2170/ --- wwwroot/data/regionMapping.json | 555 +++++++++----------------------- 1 file changed, 144 insertions(+), 411 deletions(-) diff --git a/wwwroot/data/regionMapping.json b/wwwroot/data/regionMapping.json index 7d456cd82e5..7ffeece0aad 100644 --- a/wwwroot/data/regionMapping.json +++ b/wwwroot/data/regionMapping.json @@ -14,7 +14,7 @@ "nameProp": "STATE_NAME_2021", "aliases": ["ste_code_2021", "ste_code", "ste"], "description": "States and Territories 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "ILOC_2021": { "layerName": "ILOC_2021", @@ -29,7 +29,7 @@ "nameProp": "ILO_NAME21", "aliases": ["iloc_code_2021", "iloc_code", "iloc"], "description": "Indigenous Locations 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "ILOC_NAME_2021": { "layerName": "ILOC_2021", @@ -44,7 +44,7 @@ "nameProp": "ILO_NAME21", "aliases": ["iloc_name_2021", "iloc_name"], "description": "Indigenous Locations 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "IARE_2021": { "layerName": "IARE_2021", @@ -59,7 +59,7 @@ "nameProp": "IAR_NAME21", "aliases": ["iare_code_2021", "iare_code", "iare"], "description": "Indigenous Areas 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "IARE_NAME_2021": { "layerName": "IARE_2021", @@ -74,7 +74,7 @@ "nameProp": "IAR_NAME21", "aliases": ["iare_name_2021", "iare_name"], "description": "Indigenous Areas 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "IREG_2021": { "layerName": "IREG_2021", @@ -89,7 +89,7 @@ "nameProp": "IRE_NAME21", "aliases": ["ireg_code_2021", "ireg_code", "ireg"], "description": "Indigenous Regions 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "IREG_NAME_2021": { "layerName": "IREG_2021", @@ -104,7 +104,7 @@ "nameProp": "IRE_NAME21", "aliases": ["ireg_name_2021", "ireg_name"], "description": "Indigenous Regions 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "RA_2021": { "layerName": "RA_2021", @@ -119,7 +119,7 @@ "nameProp": "RA_NAME21", "aliases": ["ra_code_2021", "ra_code", "ra"], "description": "Remoteness Areas 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SAL_2021": { "layerName": "SAL_2021", @@ -134,7 +134,7 @@ "nameProp": "SAL_NAME_2021", "aliases": ["sal_code_2021", "sal_code", "sal"], "description": "Suburbs and Localities 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "ADD_2021": { "layerName": "ADD_2021", @@ -149,7 +149,7 @@ "nameProp": "ADD_NAME_2021", "aliases": ["add_code_2021", "add_code", "add"], "description": "Australian Drainage Divisions 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "ADD_NAME_2021": { "layerName": "ADD_2021", @@ -164,7 +164,7 @@ "nameProp": "ADD_NAME_2021", "aliases": ["add_name_2021", "add_name"], "description": "Australian Drainage Divisions 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "DZN_2021": { "layerName": "DZN_2021", @@ -179,7 +179,7 @@ "nameProp": "DZN_CODE_2021", "aliases": ["dzn_code_2021", "dzn_code", "dzn"], "description": "Destination Zones 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "LGA_2022": { "layerName": "LGA_2022", @@ -194,7 +194,7 @@ "nameProp": "LGA_NAME_2022", "aliases": ["lga_code_2022"], "description": "Local Government Areas 2022", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "LGA_2023": { "layerName": "LGA_2023", @@ -209,7 +209,7 @@ "nameProp": "LGA_NAME_2023", "aliases": ["lga_code_2023", "lga_code", "lga"], "description": "Local Government Areas 2023", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SED_2021": { "layerName": "SED_2021", @@ -224,7 +224,7 @@ "nameProp": "SED_NAME_2021", "aliases": ["sed_code_2021"], "description": "State Electoral Divisions 2021 (ABS)", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SED_NAME_2021": { "layerName": "SED_2021", @@ -239,7 +239,7 @@ "nameProp": "SED_NAME_2021", "aliases": ["sed_name_2021"], "description": "State Electoral Divisions 2021 (ABS)", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SED_2022": { "layerName": "SED_2022", @@ -254,7 +254,7 @@ "nameProp": "SED_NAME_2022", "aliases": ["sed_code_2022", "sed_code", "sed"], "description": "State Electoral Divisions 2022 (ABS)", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SED_NAME_2022": { "layerName": "SED_2022", @@ -269,7 +269,7 @@ "nameProp": "SED_NAME_2022", "aliases": ["sed_name_2022", "sed_name"], "description": "State Electoral Divisions 2022 (ABS)", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "CED_2021": { "layerName": "CED_2021", @@ -284,7 +284,7 @@ "nameProp": "CED_NAME_2021", "aliases": ["ced_code_2021", "ced_code", "ced"], "description": "Commonwealth Electoral Divisions 2021 (ABS)", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "CED_NAME_2021": { "layerName": "CED_2021", @@ -299,7 +299,7 @@ "nameProp": "CED_NAME_2021", "aliases": ["ced_name_2021", "ced_name"], "description": "Commonwealth Electoral Divisions 2021 (ABS)", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "POA_2021": { "layerName": "POA_2021", @@ -320,7 +320,7 @@ "postcode" ], "description": "Postal Areas 2021 (ABS)", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "TR_2021": { "layerName": "TR_2021", @@ -335,7 +335,7 @@ "nameProp": "TR_NAME_2021", "aliases": ["tr_code_2021", "tr_code", "tr"], "description": "Tourism Regions 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "TR_NAME_2021": { "layerName": "TR_2021", @@ -350,7 +350,7 @@ "nameProp": "TR_NAME_2021", "aliases": ["tr_name_2021", "tr_name"], "description": "Tourism Regions 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SUA_2021": { "layerName": "SUA_2021", @@ -365,7 +365,7 @@ "nameProp": "SUA_NAME_2021", "aliases": ["sua_code_2021", "sua_code", "sua"], "description": "Significant Urban Areas 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SUA_NAME_2021": { "layerName": "SUA_2021", @@ -380,7 +380,7 @@ "nameProp": "SUA_NAME_2021", "aliases": ["sua_name_2022", "sua_name"], "description": "Significant Urban Areas 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "UCL_2021": { "layerName": "UCL_2021", @@ -395,7 +395,7 @@ "nameProp": "UCL_NAME_2021", "aliases": ["ucl_code_2021", "ucl_code", "ucl"], "description": "Urban Centres and Localities 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "UCL_NAME_2021": { "layerName": "UCL_2021", @@ -410,7 +410,7 @@ "nameProp": "UCL_NAME_2021", "aliases": ["ucl_name_2021", "ucl_name"], "description": "Urban Centres and Localities 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SOS_2021": { "layerName": "SOS_2021", @@ -425,7 +425,7 @@ "nameProp": "SOS_NAME_2021", "aliases": ["sos_code_2021", "sos_code", "sos"], "description": "Section of State 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SOSR_2021": { "layerName": "SOSR_2021", @@ -440,7 +440,7 @@ "nameProp": "SOSR_NAME_2021", "aliases": ["sosr_code_2021", "sosr_code", "sosr"], "description": "Section of State Range 2021", - "bbox": [96.81, -43.74, 168, -9.14] + "bbox": [96.81, -43.74, 168.0, -9.14] }, "SA1_2011": { "layerName": "FID_SA1_2011_AUST", @@ -457,10 +457,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA1_7DIG11" }, "SA1_7DIGIT_2011": { @@ -478,10 +475,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA1_7DIG11" }, "SA1_2016": { @@ -492,10 +486,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA1_MAIN16", "aliases": ["sa1_code_2016", "sa1_maincode_2016"], "nameProp": "SA1_7DIG16", @@ -510,10 +501,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA1_7DIG16", "aliases": ["sa1_7digitcode", "sa1_7digitcode_2016"], "nameProp": "SA1_7DIG16", @@ -532,7 +520,7 @@ "nameProp": "SA1_CODE21", "aliases": ["sa1_code_2021", "sa1_maincode_2021", "sa1", "sa1_code"], "description": "Statistical Area Level 1 2021 (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SA4_2011": { "layerName": "FID_SA4_2011_AUST", @@ -549,10 +537,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA4_NAME11" }, "SA4_NAME_2011": { @@ -569,10 +554,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA4_NAME11" }, "SA4_2016": { @@ -583,10 +565,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA4_CODE16", "aliases": ["sa4_maincode_2016", "sa4_code_2016"], "nameProp": "SA4_NAME16", @@ -601,10 +580,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA4_NAME16", "aliases": ["sa4_name_2016"], "nameProp": "SA4_NAME16", @@ -623,7 +599,7 @@ "nameProp": "SA4_NAME21", "aliases": ["sa4_code_2021", "sa4_maincode_2021", "sa4", "sa4_code"], "description": "Statistical Area Level 4 2021 by code (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SA4_NAME_2021": { "layerName": "SA4_2021", @@ -637,7 +613,7 @@ "nameProp": "SA4_NAME21", "aliases": ["sa4_name_2021", "sa4_name"], "description": "Statistical Area Level 4 2021 by name (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SA3_2011": { "layerName": "FID_SA3_2011_AUST", @@ -654,10 +630,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA3_NAME11" }, "SA3_NAME_2011": { @@ -675,10 +648,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA3_NAME11" }, "SA3_2016": { @@ -689,10 +659,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA3_CODE16", "aliases": ["sa3_code_2016", "sa3_maincode_2016"], "nameProp": "SA3_NAME16", @@ -707,10 +674,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA3_NAME16", "aliases": ["sa3_name_2016"], "nameProp": "SA3_NAME16", @@ -729,7 +693,7 @@ "nameProp": "SA3_NAME21", "aliases": ["sa3_code_2021", "sa3_maincode_2021", "sa3", "sa3_code"], "description": "Statistical Area Level 3 2021 by code (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SA3_NAME_2021": { "layerName": "SA3_2021", @@ -743,7 +707,7 @@ "nameProp": "SA3_NAME21", "aliases": ["sa3_name_2021", "sa3_name"], "description": "Statistical Area Level 3 2021 by name (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SA2_2011": { "layerName": "FID_SA2_2011_AUST", @@ -760,10 +724,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA2_NAME11" }, "SA2_5DIG_2011": { @@ -781,10 +742,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA2_NAME11" }, "SA2_NAME_2011": { @@ -801,10 +759,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SA2_NAME11" }, "SA2_2016": { @@ -815,10 +770,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA2_MAIN16", "aliases": ["sa2_code_2016", "sa2_maincode_2016"], "nameProp": "SA2_NAME16", @@ -833,10 +785,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA2_5DIG16", "aliases": ["sa2_5digitcode", "sa2_5digitcode_2016"], "nameProp": "SA2_NAME16", @@ -851,10 +800,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SA2_NAME16", "aliases": ["sa2_name_2016"], "nameProp": "SA2_NAME16", @@ -873,7 +819,7 @@ "nameProp": "SA2_NAME21", "aliases": ["sa2_code_2021", "sa2_maincode_2021", "sa2", "sa2_code"], "description": "Statistical Area Level 2 2021 by code (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SA2_NAME_2021": { "layerName": "SA2_2021", @@ -887,7 +833,7 @@ "nameProp": "SA2_NAME21", "aliases": ["sa2_name_2021", "sa2_name"], "description": "Statistical Area Level 2 2021 by name (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SSC": { "layerName": "FID_SSC_2011_AUST", @@ -904,10 +850,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SSC_NAME" }, "SSC_NAME": { @@ -924,10 +867,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SSC_NAME" }, "SSC_2016": { @@ -938,10 +878,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SSC_CODE16", "aliases": ["ssc_code_2016", "ssc_code", "ssc"], "nameProp": "SSC_NAME16", @@ -956,10 +893,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "SSC_NAME16", "aliases": ["ssc_name_2016", "ssc_name", "suburb"], "nameProp": "SSC_NAME16", @@ -978,7 +912,7 @@ "nameProp": "LGA_NAME21", "aliases": ["lga_code_2021", "lga_code_2020"], "description": "Local Government Areas 2021 by code (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "LGA_2019": { "layerName": "LGA_2019", @@ -992,7 +926,7 @@ "nameProp": "LGA_NAME19", "aliases": ["lga_code_2019"], "description": "Local Government Areas 2019 by code (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "LGA_2018": { "layerName": "LGA_2018_AUST", @@ -1000,10 +934,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400008, -43.74050960205758, 167.99803499600011, - -9.142175976703571 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "uniqueIdProp": "FID", "regionProp": "LGA_CODE18", "nameProp": "LGA_NAME18", @@ -1017,10 +948,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400008, -43.74050960205758, 167.99803499600011, - -9.142175976703571 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "uniqueIdProp": "FID", "regionProp": "LGA_CODE17", "nameProp": "LGA_NAME17", @@ -1036,10 +964,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "LGA_CODE16", "aliases": ["lga_code_2016"], "nameProp": "LGA_NAME16", @@ -1061,10 +986,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "LGA_NAME15" }, "LGA_2013": { @@ -1082,10 +1004,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "LGA_NAME13" }, "LGA_2011": { @@ -1103,10 +1022,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395999996, -43.74050957999999, 153.63872711999997, - -9.142175969999997 - ], + "bbox": [112.92, -43.75, 153.64, -9.14], "nameProp": "LGA_NAME11" }, "LGA_NAME_2011": { @@ -1151,10 +1067,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395999996, -43.74050957999999, 153.63872711999997, - -9.142175969999997 - ], + "bbox": [112.92, -43.75, 153.64, -9.14], "nameProp": "LGA_NAME11" }, "POA": { @@ -1165,10 +1078,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "POA_CODE16", "aliases": ["poa_code_2016", "postcode_2016"], "digits": 4, @@ -1198,10 +1108,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.59821500299999, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.6, 159.11, -9.14], "nameProp": "POA_NAME" }, "CED_CODE18": { @@ -1216,7 +1123,7 @@ "nameProp": "CED_NAME18", "aliases": ["ced_2018", "ced_code_2018"], "description": "Commonwealth electoral divisions 2018 by code (ABS)", - "bbox": [96.82, -43.74, 159.11, -9.14] + "bbox": [96.82, -43.74, 159.12, -9.14] }, "CED_NAME18": { "layerName": "CED_2018", @@ -1231,7 +1138,7 @@ "nameProp": "CED_NAME18", "aliases": ["ced_name_2018"], "description": "Commonwealth electoral divisions 2018 by name (ABS)", - "bbox": [96.82, -43.74, 159.11, -9.14] + "bbox": [96.82, -43.74, 159.12, -9.14] }, "CED_CODE_2016": { "layerName": "FID_CED_2016_AUST", @@ -1248,10 +1155,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "CED_NAME16" }, "CED_NAME_2016": { @@ -1268,10 +1172,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "CED_NAME16" }, "CED_CODE_2013": { @@ -1289,10 +1190,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "CED_NAME13" }, "CED_NAME_2013": { @@ -1309,10 +1207,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "CED_NAME13" }, "CED_CODE_2011": { @@ -1330,10 +1225,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "CED_NAME", "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-FID_CED_2011_AUST_CED_CODE.json" }, @@ -1351,10 +1243,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "CED_NAME" }, "COM_ELB_ID_2016": { @@ -1372,10 +1261,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676599999997, -43.740509999999986, 159.1092189999999, - -9.142175999999996 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SORTNAME" }, "ELB_2021": { @@ -1390,7 +1276,7 @@ "nameProp": "Elect_div", "aliases": ["com_elb_name_2021", "com_elb_name", "divisionnm"], "description": "Commonwealth electoral districts 2021 by name (AEC)", - "bbox": [96.81, -43.73, 168, -9.1] + "bbox": [96.81, -43.73, 168.0, -9.1] }, "ELB_2019": { "layerName": "ELB_2019", @@ -1403,10 +1289,7 @@ "nameProp": "Sortname", "aliases": ["com_elb_name_2019"], "description": "Commonwealth electoral districts 2019 by name (AEC)", - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ] + "bbox": [96.81, -43.75, 159.11, -9.14] }, "COM_ELB_NAME_2016": { "layerName": "FID_COM20160509_ELB", @@ -1423,10 +1306,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676599999997, -43.740509999999986, 159.1092189999999, - -9.142175999999996 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SORTNAME" }, "COM_ELB_NAME_2011": { @@ -1444,10 +1324,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676599999999, -43.74050999999999, 159.10921899999994, - -9.142175999999997 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SORTNAME" }, "COM_ELB_ID_2013": { @@ -1456,7 +1333,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [96.816766, -43.74051, 159.109219, -9.142176], + "bbox": [96.81, -43.75, 159.11, -9.14], "uniqueIdProp": "FID", "regionProp": "ced_code_2013", "nameProp": "ced_name_2013", @@ -1470,7 +1347,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [96.816766, -43.74051, 159.109219, -9.142176], + "bbox": [96.81, -43.75, 159.11, -9.14], "uniqueIdProp": "FID", "regionProp": "ced_name_2013", "nameProp": "ced_name_2013", @@ -1484,7 +1361,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [96.816766, -43.74051, 159.109219, -9.142176], + "bbox": [96.81, -43.75, 159.11, -9.14], "uniqueIdProp": "FID", "regionProp": "ced_code_2010", "nameProp": "ced_name_2010", @@ -1498,7 +1375,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [96.816766, -43.74051, 159.109219, -9.142176], + "bbox": [96.81, -43.75, 159.11, -9.14], "uniqueIdProp": "FID", "regionProp": "ced_name_2010", "nameProp": "ced_name_2010", @@ -1512,7 +1389,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [96.817997, -43.74051, 159.105442, -9.142186], + "bbox": [96.81, -43.75, 159.11, -9.14], "uniqueIdProp": "FID", "regionProp": "ced_code_2007", "nameProp": "ced_name_2007", @@ -1526,7 +1403,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [96.817997, -43.74051, 159.105442, -9.142186], + "bbox": [96.81, -43.75, 159.11, -9.14], "uniqueIdProp": "FID", "regionProp": "ced_name_2007", "nameProp": "ced_name_2007", @@ -1546,7 +1423,7 @@ "nameProp": "SED_NAME18", "aliases": ["sed_2018", "sed_code_2018"], "description": "State electoral divisions 2018 by code (ABS)", - "bbox": [96.82, -43.74, 159.11, -9.14] + "bbox": [96.82, -43.74, 159.12, -9.14] }, "SED_NAME18": { "layerName": "SED_2018", @@ -1560,7 +1437,7 @@ "nameProp": "SED_NAME18", "aliases": ["sed_name_2018"], "description": "State electoral divisions 2018 by name (ABS)", - "bbox": [96.82, -43.74, 159.11, -9.14] + "bbox": [96.82, -43.74, 159.12, -9.14] }, "SED_CODE16": { "layerName": "SED_2016", @@ -1574,7 +1451,7 @@ "nameProp": "SED_NAME16", "aliases": ["sed_2016", "sed_code_2016", "sed_code16"], "description": "State electoral divisions 2016 by code (ABS)", - "bbox": [96.82, -43.74, 159.11, -9.14] + "bbox": [96.82, -43.74, 159.12, -9.14] }, "SED_NAME16": { "layerName": "SED_2016", @@ -1588,7 +1465,7 @@ "nameProp": "SED_NAME16", "aliases": ["sed_name_2016", "sed_name16"], "description": "State electoral divisions 2016 by code (ABS)", - "bbox": [96.82, -43.74, 159.11, -9.14] + "bbox": [96.82, -43.74, 159.12, -9.14] }, "SED_CODE11": { "layerName": "FID_SED_2011_AUST", @@ -1605,10 +1482,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395199997, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [112.92, -43.75, 159.11, -9.14], "nameProp": "SED_NAME" }, "SED_NAME11": { @@ -1625,10 +1499,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395199997, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [112.92, -43.75, 159.11, -9.14], "nameProp": "SED_NAME" }, "GCCSA_2011": { @@ -1646,10 +1517,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "GCC_NAME11" }, "GCCSA_NAME_2011": { @@ -1666,10 +1534,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "GCC_NAME11" }, "GCCSA_2016": { @@ -1680,10 +1545,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "GCC_CODE16", "aliases": ["gccsa_code_2016"], "nameProp": "GCC_NAME16", @@ -1698,10 +1560,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "GCC_NAME16", "aliases": ["gccsa_name_2016"], "nameProp": "GCC_NAME16", @@ -1720,7 +1579,7 @@ "nameProp": "GCC_NAME21", "aliases": ["gccsa_code_2021", "gccsa_code", "gccsa"], "description": "Greater capital city statistical areas 2021 by code (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "GCCSA_NAME_2021": { "layerName": "GCCSA_2021", @@ -1734,7 +1593,7 @@ "nameProp": "GCC_NAME21", "aliases": ["gccsa_name_2021", "gccsa_name"], "description": "Greater capital city statistical areas 2021 by name (ABS)", - "bbox": [96.81, -43.75, 168, -9.14] + "bbox": [96.81, -43.75, 168.0, -9.14] }, "SUA": { "layerName": "FID_SUA_2011_AUST", @@ -1751,10 +1610,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SUA_NAME11" }, "SUA_NAME": { @@ -1773,10 +1629,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SUA_NAME11" }, "STE_2011": { @@ -1794,10 +1647,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "STE_NAME11" }, "STE_2016": { @@ -1808,10 +1658,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "STE_CODE16", "aliases": [ "ste_code_2016", @@ -1839,10 +1686,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SOS_NAME11" }, "SOSR": { @@ -1860,10 +1704,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "SSR_NAME11" }, "UCL": { @@ -1881,10 +1722,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "UCL_NAME11" }, "IREG": { @@ -1902,10 +1740,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "IR_NAME11" }, "ILOC": { @@ -1923,10 +1758,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "IL_NAME11" }, "IARE": { @@ -1944,10 +1776,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "IA_NAME11" }, "RA": { @@ -1965,10 +1794,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "RA_NAME11" }, "TR": { @@ -1986,10 +1812,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395199997, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [112.92, -43.75, 159.11, -9.14], "nameProp": "TR_NAME15" }, "TR_2013": { @@ -2007,10 +1830,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395199997, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [112.92, -43.75, 159.11, -9.14], "nameProp": "TR_NAME13" }, "NRMR": { @@ -2028,10 +1848,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799995, -43.74050960299998, 159.10921900799994, - -9.142175976999997 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "NRMR_NAME" }, "NRMR_NAME": { @@ -2048,10 +1865,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799995, -43.74050960299998, 159.10921900799994, - -9.142175976999997 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "NRMR_NAME" }, "ADD": { @@ -2069,10 +1883,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395199994, -43.74050960299998, 159.10921900799994, - -9.142175976999997 - ], + "bbox": [112.92, -43.75, 159.11, -9.14], "nameProp": "ADD_NAME" }, "ADD_NAME": { @@ -2089,10 +1900,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 112.92111395199994, -43.74050960299998, 159.10921900799994, - -9.142175976999997 - ], + "bbox": [112.92, -43.75, 159.11, -9.14], "nameProp": "ADD_NAME" }, "ADD_2016": { @@ -2103,10 +1911,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "ADD_CODE16", "aliases": ["add_code_2016"], "nameProp": "ADD_NAME16", @@ -2121,10 +1926,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "ADD_NAME16", "aliases": ["add_name_2016"], "nameProp": "ADD_NAME16", @@ -2146,10 +1948,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140800003, -43.74050960299996, 159.10921900800005, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "PHN_Name" }, "STE_NAME_2011": { @@ -2185,10 +1984,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "STE_NAME11" }, "STE_NAME_2016": { @@ -2199,10 +1995,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694139400007, -43.74050960299996, 167.99803499600006, - -9.142175976999962 - ], + "bbox": [96.81, -43.75, 168.0, -9.14], "regionProp": "STE_NAME16", "aliases": ["state", "ste_name", "ste_name_2016", "ste_name_2021"], "nameProp": "STE_NAME16", @@ -2249,10 +2042,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676569599999, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_sla_SLA_CODE06.json" }, "SLA_5DIGITCODE": { @@ -2270,10 +2060,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676569599999, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_sla_SLA_5DIGIT.json" }, "SLA_NAME": { @@ -2290,10 +2077,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676569599999, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_sla_SLA_NAME06.json" }, "CD": { @@ -2310,10 +2094,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.76945846399997, -43.740509602999985, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.76, -43.75, 159.11, -9.14], "nameProp": "CD_CODE06", "regionIdsFile": "https://tiles.terria.io/region-mapping/regionids/region_map-fid_asgc06_cd_CD_CODE06.json" }, @@ -2332,10 +2113,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 10, "serverMaxZoom": 28, - "bbox": [ - -179.99999999999994, -85.05109999999998, 179.99999999999994, - 83.62359600000005 - ], + "bbox": [-180.0, -85.06, 180.0, 83.63], "nameProp": "NAME" }, "CNT3": { @@ -2353,10 +2131,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 10, "serverMaxZoom": 28, - "bbox": [ - -179.99999999999994, -85.05109999999998, 179.99999999999994, - 83.62359600000005 - ], + "bbox": [-180.0, -85.06, 180.0, 83.63], "nameProp": "NAME" }, "COUNTRY": { @@ -2373,10 +2148,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 10, "serverMaxZoom": 28, - "bbox": [ - -179.99999999999994, -85.05109999999998, 179.99999999999994, - 83.62359600000005 - ], + "bbox": [-180.0, -85.06, 180.0, 83.63], "nameProp": "NAME" }, "AUS": { @@ -2403,10 +2175,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "nameProp": "AUS_NAME" }, "ESA_09": { @@ -2417,10 +2186,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676599999999, -43.74050999999999, 159.10921899999994, - -9.142175999999997 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "regionProp": "ESA_CODE", "aliases": ["esa", "esa_code", "esa_code_2009"], "nameProp": "ESA_NAME", @@ -2435,10 +2201,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 96.81676599999999, -43.74050999999999, 159.10921899999994, - -9.142175999999997 - ], + "bbox": [96.81, -43.75, 159.11, -9.14], "regionProp": "ESA_NAME", "aliases": ["esa_name", "esa_name_2009"], "nameProp": "ESA_NAME", @@ -2453,10 +2216,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 72.57737629065888, -54.776992953536805, 167.9981399159851, - -9.141289999999968 - ], + "bbox": [72.57, -54.78, 168.0, -9.14], "regionProp": "REG_CODE_7", "aliases": ["ibra7_reg", "ibra7_reg_code"], "nameProp": "REG_NAME_7", @@ -2471,10 +2231,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 72.57737629065888, -54.776992953536805, 167.9981399159851, - -9.141289999999968 - ], + "bbox": [72.57, -54.78, 168.0, -9.14], "regionProp": "REG_NAME_7", "aliases": ["ibra7_reg_name"], "nameProp": "REG_NAME_7", @@ -2489,10 +2246,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 72.57737629065888, -54.77699295353692, 167.9981399159851, - -9.141289999999968 - ], + "bbox": [72.57, -54.78, 168.0, -9.14], "regionProp": "SUB_CODE_7", "aliases": ["ibra7_sub", "ibra7_sub_code"], "nameProp": "SUB_NAME_7", @@ -2507,10 +2261,7 @@ "serverMinZoom": 0, "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - 72.57737629065888, -54.77699295353692, 167.9981399159851, - -9.141289999999968 - ], + "bbox": [72.57, -54.78, 168.0, -9.14], "regionProp": "SUB_NAME_7", "aliases": ["ibra7_sub_name"], "nameProp": "SUB_NAME_7", @@ -2523,10 +2274,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - -176.89314232298426, -47.28999251282331, 178.57724348546415, - -34.392630183116 - ], + "bbox": [-176.9, -47.29, 178.58, -34.39], "uniqueIdProp": "FID", "regionProp": "AU2017", "nameProp": "AU2017_NAM", @@ -2540,10 +2288,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - -176.89314232298426, -47.28999251282331, 178.57724348546415, - -34.392630183116 - ], + "bbox": [-176.9, -47.29, 178.58, -34.39], "uniqueIdProp": "FID", "regionProp": "AU2017_NAM", "nameProp": "AU2017_NAM", @@ -2557,10 +2302,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [ - -176.89314232298426, -47.28999251282331, 178.57724348546415, - -34.392630183116 - ], + "bbox": [-176.9, -47.29, 178.58, -34.39], "uniqueIdProp": "FID", "regionProp": "MB2017", "nameProp": "MB2017", @@ -2574,7 +2316,7 @@ "serverType": "MVT", "serverMaxNativeZoom": 12, "serverMaxZoom": 28, - "bbox": [96.816941, -43.74051, 167.99803499600011, -9.142176], + "bbox": [96.81, -43.75, 168.0, -9.14], "uniqueIdProp": "FID", "regionProp": "FED_ABB", "nameProp": "FED_DIV", @@ -2594,10 +2336,7 @@ "nameProp": "RRA_Name", "aliases": ["RRA_NAME", "RRA"], "description": "Bushfire Regional Recovery Areas", - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ] + "bbox": [96.81, -43.75, 159.11, -9.14] }, "ABARES_CODE": { "layerName": "ABARES_Ag_Regions", @@ -2611,10 +2350,7 @@ "nameProp": "AbaresName", "aliases": ["abares_code", "abares_region_code"], "description": "ABARES regions, farm survey statistical aggregation areas", - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ] + "bbox": [96.81, -43.75, 159.11, -9.14] }, "ABARES_NAME": { "layerName": "ABARES_Ag_Regions", @@ -2628,10 +2364,7 @@ "nameProp": "AbaresName", "description": "ABARES regions, farm survey statistical aggregation areas", "aliases": ["abares_name", "abares_region_name"], - "bbox": [ - 96.81694140799998, -43.74050960300003, 159.10921900799997, - -9.142175976999999 - ] + "bbox": [96.81, -43.75, 159.11, -9.14] }, "City_of_Melbourne_CLUE": { "layerName": "City_of_Melbourne_CLUE", @@ -2645,7 +2378,7 @@ "regionProp": "block_id", "nameProp": "clue_area", "description": "City of Melbourne Census of Land Use and Employment", - "bbox": [144.88, -37.86, 145, -37.77] + "bbox": [144.88, -37.86, 145.0, -37.77] } } } From 1aaeffb3d18580ecfb801ad1cb4a97830e3a7964 Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Wed, 25 Oct 2023 19:48:34 +1100 Subject: [PATCH 070/129] Add changelog entry --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1ee97136874..eab10dc1711 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,10 @@ - Fix bug in mismatched GeoJSON Feature `_id_` and TableMixin `rowId` - this was causing incorrect styling when using `filterByProperties` or features had `null` geometry - Fix splitter for `GeoJsonMixin` (lines and polygon features only) - Fix share links with picked features from `ProtomapsImageryProvider` +- Added many remaining ASGS 2021 region types to region mapping (STE_2021,ILOC_2021,IARE_2021,IREG_2021,RA_2021,SAL_2021,ADD_2021,DZN_2021,LGA_2022,LGA_2023,SED_2021,SED_2022, + CED_2021,POA_2021,TR_2021,SUA_2021,UCL_2021,SOS_2021,SOSR_2021). + - 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. - [The next improvement] #### 8.3.6 - 2023-10-03 From beabdbea819578d41892ae340ee65bc1966250b1 Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Thu, 26 Oct 2023 00:44:57 +1100 Subject: [PATCH 071/129] Update number of region mapping types --- test/ModelMixins/TableMixinSpec.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/ModelMixins/TableMixinSpec.ts b/test/ModelMixins/TableMixinSpec.ts index f467749db1e..f74c8d54f18 100644 --- a/test/ModelMixins/TableMixinSpec.ts +++ b/test/ModelMixins/TableMixinSpec.ts @@ -63,6 +63,8 @@ const regionIdsLgaNameStates = JSON.stringify( require("../../wwwroot/data/regionids/region_map-FID_LGA_2011_AUST_STE_NAME11.json") ); +const NUMBER_OF_REGION_MAPPING_TYPES = 144; + describe("TableMixin", function () { let item: CsvCatalogItem; let terria: Terria; @@ -591,7 +593,9 @@ describe("TableMixin", function () { (await item.loadMapItems()).throwIfError(); - expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(143); + expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe( + NUMBER_OF_REGION_MAPPING_TYPES + ); }); it("loads regionProviderLists on loadMapItems - with multiple regionMappingDefinitionsUrl", async function () { @@ -620,7 +624,9 @@ describe("TableMixin", function () { expect(item.regionProviderLists?.length).toBe(2); expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(2); - expect(item.regionProviderLists?.[1]?.regionProviders.length).toBe(143); + expect(item.regionProviderLists?.[1]?.regionProviders.length).toBe( + NUMBER_OF_REGION_MAPPING_TYPES + ); // Item region provider should match from "additionalRegion.json" (as it comes before "build/TerriaJS/data/regionMapping.json") expect(item.activeTableStyle.regionColumn?.regionType?.description).toBe( @@ -654,7 +660,9 @@ describe("TableMixin", function () { expect(item.regionProviderLists?.length).toBe(1); - expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe(143); + expect(item.regionProviderLists?.[0]?.regionProviders.length).toBe( + NUMBER_OF_REGION_MAPPING_TYPES + ); // Item region provider should match from "build/TerriaJS/data/regionMapping.json" expect(item.activeTableStyle.regionColumn?.regionType?.description).toBe( From 2f0cec5a4c864cabb2bcdbc51cd34cea2ca2cbfb Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Thu, 26 Oct 2023 11:16:01 +1000 Subject: [PATCH 072/129] Add `hideDefaultDescription` to `CatalogMemberMixin` (#6951) --- CHANGES.md | 1 + lib/ReactViews/Preview/Description.jsx | 6 +- .../TraitsClasses/CatalogMemberTraits.ts | 8 +++ test/ReactViews/Preview/DescriptionSpec.tsx | 55 ++++++++++++++++++- 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1ee97136874..1745a1601b3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ - Fix bug in mismatched GeoJSON Feature `_id_` and TableMixin `rowId` - this was causing incorrect styling when using `filterByProperties` or features had `null` geometry - Fix splitter for `GeoJsonMixin` (lines and polygon features only) - Fix share links with picked features from `ProtomapsImageryProvider` +- Add `hideDefaultDescription` to `CatalogMemberTraits` - if true, then no generic default description will be shown when `description` is empty. - [The next improvement] #### 8.3.6 - 2023-10-03 diff --git a/lib/ReactViews/Preview/Description.jsx b/lib/ReactViews/Preview/Description.jsx index cfb32fdd38c..904abfbd82f 100644 --- a/lib/ReactViews/Preview/Description.jsx +++ b/lib/ReactViews/Preview/Description.jsx @@ -64,7 +64,11 @@ class Description extends React.Component {

    {t("description.dataNotLocal")}

    diff --git a/lib/Traits/TraitsClasses/CatalogMemberTraits.ts b/lib/Traits/TraitsClasses/CatalogMemberTraits.ts index c073799b775..9ddf821411d 100644 --- a/lib/Traits/TraitsClasses/CatalogMemberTraits.ts +++ b/lib/Traits/TraitsClasses/CatalogMemberTraits.ts @@ -116,6 +116,14 @@ class CatalogMemberTraits extends ModelTraits { }) description?: string; + @primitiveTrait({ + type: "boolean", + name: "Hide default description", + description: + "If true, then no generic default description will be displayed if `description` is undefined." + }) + hideDefaultDescription: boolean = false; + @primitiveTrait({ type: "string", name: "Name in catalog", diff --git a/test/ReactViews/Preview/DescriptionSpec.tsx b/test/ReactViews/Preview/DescriptionSpec.tsx index fa1ec7c33d6..c9bfe4b42bf 100644 --- a/test/ReactViews/Preview/DescriptionSpec.tsx +++ b/test/ReactViews/Preview/DescriptionSpec.tsx @@ -3,9 +3,11 @@ import React from "react"; import { act } from "react-dom/test-utils"; import { create, ReactTestRenderer } from "react-test-renderer"; import { ThemeProvider } from "styled-components"; -import Terria from "../../../lib/Models/Terria"; -import updateModelFromJson from "../../../lib/Models/Definition/updateModelFromJson"; +import GeoJsonCatalogItem from "../../../lib/Models/Catalog/CatalogItems/GeoJsonCatalogItem"; import WebMapServiceCatalogItem from "../../../lib/Models/Catalog/Ows/WebMapServiceCatalogItem"; +import CommonStrata from "../../../lib/Models/Definition/CommonStrata"; +import updateModelFromJson from "../../../lib/Models/Definition/updateModelFromJson"; +import Terria from "../../../lib/Models/Terria"; import Description from "../../../lib/ReactViews/Preview/Description"; import { terriaTheme } from "../../../lib/ReactViews/StandardUserInterface"; @@ -142,4 +144,53 @@ describe("DescriptionSpec", function () { expect(child.props.children).toBe("some link"); }); + + it("respects hideDefaultDescription", function () { + const geoJsonItem = new GeoJsonCatalogItem("test-geojson", terria); + runInAction(() => { + geoJsonItem.setTrait(CommonStrata.definition, "description", "test"); + }); + + act(() => { + testRenderer = create( + + + + ); + }); + + const showDescription = testRenderer.root.findAll( + (node) => node.type === "p" + ); + + expect(showDescription.length).toEqual(1); + expect(showDescription[0].children[0]).toBe("test"); + + runInAction(() => { + geoJsonItem.setTrait(CommonStrata.definition, "description", ""); + }); + + const showDefaultDescription = testRenderer.root.findAll( + (node) => node.type === "p" + ); + + expect(showDefaultDescription.length).toEqual(1); + expect(showDefaultDescription[0].children[0]).toBe( + "description.dataNotLocal" + ); + + runInAction(() => { + geoJsonItem.setTrait( + CommonStrata.definition, + "hideDefaultDescription", + true + ); + }); + + const showNoDescription = testRenderer.root.findAll( + (node) => node.type === "p" + ); + + expect(showNoDescription.length).toEqual(0); + }); }); From 49b4c8a451595ded793773f23f4bfbddad2ddbfe Mon Sep 17 00:00:00 2001 From: Mike Wu <41275384+mwu2018@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:41:02 +1100 Subject: [PATCH 073/129] v8.3.7 (#6953) --- CHANGES.md | 7 +++++-- package.json | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8d119cecab2..2244eaed456 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ # Change Log -#### next release (8.3.7) +#### next release (8.3.8) + +- [The next improvement] + +#### 8.3.7 - 2023-10-26 - Fix `WebMapServiceCatalogItem` `allowFeaturePicking` - Allow translation of TableStylingWorkflow. @@ -13,7 +17,6 @@ - Fix share links with picked features from `ProtomapsImageryProvider` - Added on screen attribution and Google logo for Google Photorealistic 3D Tiles. - Add `hideDefaultDescription` to `CatalogMemberTraits` - if true, then no generic default description will be shown when `description` is empty. -- [The next improvement] #### 8.3.6 - 2023-10-03 diff --git a/package.json b/package.json index 35fc2b61343..c1baed979f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "terriajs", - "version": "8.3.6", + "version": "8.3.7", "description": "Geospatial data visualization platform.", "license": "Apache-2.0", "engines": { From 66c4dd09d912ef8b18527c1b9764cbe4c6fb7398 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 27 Oct 2023 22:43:14 +1100 Subject: [PATCH 074/129] Re-enable tests --- test/ReactViews/DataCatalog/CatalogGroupSpec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx b/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx index 8aa83fb516c..731d8363357 100644 --- a/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx +++ b/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx @@ -6,7 +6,7 @@ import { terriaTheme } from "../../../lib/ReactViews/StandardUserInterface"; import { create } from "react-test-renderer"; import { act } from "react-dom/test-utils"; -fdescribe("CatalogGroup", () => { +describe("CatalogGroup", () => { let testRenderer: ReturnType; describe("Loading", () => { From 51c39b31191b8791ed9b1d8ea18426ab8af88914 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 27 Oct 2023 23:19:24 +1100 Subject: [PATCH 075/129] Fix WMS nested group IDs --- CHANGES.md | 3 +- .../Catalog/Ows/WebMapServiceCatalogGroup.ts | 18 +++--- .../Ows/WebMapServiceCatalogGroupSpec.ts | 61 ++++++++++++++----- wwwroot/test/WMS/wms_nested_groups.xml | 47 ++++++++++++++ 4 files changed, 105 insertions(+), 24 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3bb3ea568c1..bbb6d82efb8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,8 +2,9 @@ #### next release (8.3.8) -- [The next improvement] +- Fix WMS nested group IDs - nested groups with the same name were not being created - Remove `jsx-control-statements` dependency +- [The next improvement] #### 8.3.7 - 2023-10-26 diff --git a/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts b/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts index bbaecbcee64..bdf48aff1ce 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts @@ -168,8 +168,8 @@ class GetCapabilitiesStratum extends LoadableStratum( } @action - createMemberFromLayer(layer: CapabilitiesLayer) { - const layerId = this.getLayerId(layer); + createMemberFromLayer(layer: CapabilitiesLayer, parentLayerId?: string) { + const layerId = this.getLayerId(layer, parentLayerId); if (!layerId) { return; @@ -185,7 +185,7 @@ class GetCapabilitiesStratum extends LoadableStratum( members = [layer.Layer as CapabilitiesLayer]; } - members.forEach((member) => this.createMemberFromLayer(member)); + members.forEach((member) => this.createMemberFromLayer(member, layerId)); // Create group const existingModel = this.catalogGroup.terria.getModelById( @@ -215,7 +215,9 @@ class GetCapabilitiesStratum extends LoadableStratum( model.setTrait( CommonStrata.definition, "members", - filterOutUndefined(members.map((member) => this.getLayerId(member))) + filterOutUndefined( + members.map((member) => this.getLayerId(member, layerId)) + ) ); // Set group `info` trait if applicable @@ -318,11 +320,13 @@ class GetCapabilitiesStratum extends LoadableStratum( model.createGetCapabilitiesStratumFromParent(this.capabilities); } - getLayerId(layer: CapabilitiesLayer) { - if (!isDefined(this.catalogGroup.uniqueId)) { + getLayerId(layer: CapabilitiesLayer, parentLayerId?: string) { + if (!isDefined(this.catalogGroup.uniqueId) && !isDefined(parentLayerId)) { return; } - return `${this.catalogGroup.uniqueId}/${layer.Name ?? layer.Title}`; + return `${parentLayerId ?? this.catalogGroup.uniqueId}/${ + layer.Name ?? layer.Title + }`; } /** For backward-compatibility. diff --git a/test/Models/Catalog/Ows/WebMapServiceCatalogGroupSpec.ts b/test/Models/Catalog/Ows/WebMapServiceCatalogGroupSpec.ts index 2c3b3ebb7be..c1b16bbd00d 100644 --- a/test/Models/Catalog/Ows/WebMapServiceCatalogGroupSpec.ts +++ b/test/Models/Catalog/Ows/WebMapServiceCatalogGroupSpec.ts @@ -133,26 +133,53 @@ describe("WebMapServiceCatalogGroup", function () { }); it("loads", async function () { - expect(wms.members.length).toEqual(3); - expect(wms.memberModels.length).toEqual(3); + expect(wms.members.length).toEqual(2); + expect(wms.memberModels.length).toEqual(2); - const firstGroup = wms.memberModels[0]; + const firstGroup = wms.memberModels[0] as WebMapServiceCatalogGroup; + expect(firstGroup.uniqueId).toEqual( + "test/Digital Earth Australia - OGC Web Services" + ); expect( GroupMixin.isMixedInto(firstGroup) && firstGroup.members.length ).toEqual(3); - const firstGroupFirstModel = - GroupMixin.isMixedInto(firstGroup) && firstGroup.memberModels[0]; - expect( - firstGroupFirstModel && - CatalogMemberMixin.isMixedInto(firstGroupFirstModel) && - firstGroupFirstModel.name - ).toEqual("Surface Reflectance 25m Annual Geomedian (Landsat 8)"); + const firstSubGroup = firstGroup + .memberModels[0] as WebMapServiceCatalogGroup; + expect(firstSubGroup.uniqueId).toEqual( + "test/Digital Earth Australia - OGC Web Services/Surface Reflectance" + ); + expect(firstSubGroup.name).toEqual("Surface Reflectance"); + expect(firstSubGroup.members.length).toEqual(3); - const thirdGroup = wms.memberModels[2]; - expect( - GroupMixin.isMixedInto(thirdGroup) && thirdGroup.members.length - ).toEqual(1); + const firstSubGroupModel = firstSubGroup + .memberModels[0] as WebMapServiceCatalogItem; + expect(firstSubGroupModel.uniqueId).toEqual( + "test/Digital Earth Australia - OGC Web Services/Surface Reflectance/ls8_nbart_geomedian_annual" + ); + expect(firstSubGroupModel.name).toEqual( + "Surface Reflectance 25m Annual Geomedian (Landsat 8)" + ); + + const secondGroup = wms.memberModels[1] as WebMapServiceCatalogGroup; + expect(secondGroup.uniqueId).toEqual("test/Some other catalog"); + expect(secondGroup.name).toEqual("Some other catalog"); + expect(secondGroup.memberModels.length).toEqual(1); + + const secondSubGroup = secondGroup + .memberModels[0] as WebMapServiceCatalogGroup; + expect(secondSubGroup.uniqueId).toEqual( + "test/Some other catalog/Surface Reflectance" + ); + expect(secondSubGroup.name).toEqual("Surface Reflectance"); + expect(secondSubGroup.members.length).toEqual(1); + + const secondSubGroupModel = secondSubGroup + .memberModels[0] as WebMapServiceCatalogItem; + expect(secondSubGroupModel.uniqueId).toEqual( + "test/Some other catalog/Surface Reflectance/some_layer" + ); + expect(secondSubGroupModel.name).toEqual("Some layer"); }); }); @@ -176,8 +203,10 @@ describe("WebMapServiceCatalogGroup", function () { }); it("sets traits correctly", async function () { - const wmsItem = (wms.memberModels[0] as WebMapServiceCatalogGroup) - .memberModels[0] as WebMapServiceCatalogItem; + const wmsItem = ( + (wms.memberModels[0] as WebMapServiceCatalogGroup) + .memberModels[0] as WebMapServiceCatalogGroup + ).memberModels[0] as WebMapServiceCatalogItem; expect(wmsItem.linkedWcsUrl).toEqual("some-url"); expect(wmsItem.linkedWcsCoverage).toEqual("ls8_nbart_geomedian_annual"); diff --git a/wwwroot/test/WMS/wms_nested_groups.xml b/wwwroot/test/WMS/wms_nested_groups.xml index 62a58e44d16..fc5852e1f94 100644 --- a/wwwroot/test/WMS/wms_nested_groups.xml +++ b/wwwroot/test/WMS/wms_nested_groups.xml @@ -601,5 +601,52 @@ NOTE this layer has no EX_GeographicBoundingBox + + Some other catalog + + Some other catalog + + EPSG:3857 + EPSG:4326 + EPSG:3577 + EPSG:3111 + + 100 + 160 + -50 + -10 + + + Surface Reflectance + This is another layer called Surface Reflectance + + some_layer + Some layer + Some layer + + + WOfS + + + 109.989859933428 + 156.101505058599 + -45.2413329418709 + -9.02727104242042 + + + + + + + 2013-01-01,2014-01-01,2015-01-01,2016-01-01,2017-01-01,2018-01-01 + + + + + From 1e1e66c38aa46392f7799f76dfb14671e7870625 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Thu, 2 Nov 2023 21:08:54 +1100 Subject: [PATCH 076/129] Fix maximum call stack size exceeded on Math.min/max when creating Charts --- CHANGES.md | 1 + lib/Core/math.ts | 23 +++++++++++++++++++++++ lib/ModelMixins/ChartableMixin.ts | 3 ++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 lib/Core/math.ts diff --git a/CHANGES.md b/CHANGES.md index 3bb3ea568c1..625749b8db6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ #### next release (8.3.8) - [The next improvement] +- Fix maximum call stack size exceeded on Math.min/max when creating Charts - Remove `jsx-control-statements` dependency #### 8.3.7 - 2023-10-26 diff --git a/lib/Core/math.ts b/lib/Core/math.ts new file mode 100644 index 00000000000..0659572674f --- /dev/null +++ b/lib/Core/math.ts @@ -0,0 +1,23 @@ +export function getMax(nums: number[]) { + let len = nums.length; + if (len === 0) return undefined; + + let max = -Infinity; + + while (len--) { + max = nums[len] > max ? nums[len] : max; + } + return max; +} + +export function getMin(nums: number[]) { + let len = nums.length; + if (len === 0) return undefined; + + let min = Infinity; + + while (len--) { + min = nums[len] < min ? nums[len] : min; + } + return min; +} diff --git a/lib/ModelMixins/ChartableMixin.ts b/lib/ModelMixins/ChartableMixin.ts index 9da5b576d1c..e6806b34d57 100644 --- a/lib/ModelMixins/ChartableMixin.ts +++ b/lib/ModelMixins/ChartableMixin.ts @@ -1,6 +1,7 @@ import { maxBy, minBy } from "lodash-es"; import AbstractConstructor from "../Core/AbstractConstructor"; import LatLonHeight from "../Core/LatLonHeight"; +import { getMax, getMin } from "../Core/math"; import Model from "../Models/Definition/Model"; import { GlyphStyle } from "../ReactViews/Custom/Chart/Glyphs"; import ModelTraits from "../Traits/ModelTraits"; @@ -23,7 +24,7 @@ export function calculateDomain(points: ChartPoint[]): ChartDomain { const asNum = (x: Date | number) => (x instanceof Date ? x.getTime() : x); return { x: [minBy(xs, asNum) || 0, maxBy(xs, asNum) || 0], - y: [Math.min(...ys), Math.max(...ys)] + y: [getMin(ys) ?? 0, getMax(ys) ?? 0] }; } From 7a7ea6706eb3a0ddf597909b375784cde067e6e7 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Thu, 2 Nov 2023 21:09:20 +1100 Subject: [PATCH 077/129] Fix boolean flag in `MyDataTab` displaying number --- CHANGES.md | 1 + lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 625749b8db6..4a74e1041b5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ - [The next improvement] - Fix maximum call stack size exceeded on Math.min/max when creating Charts +- Fix boolean flag in `MyDataTab` displaying number - Remove `jsx-control-statements` dependency #### 8.3.7 - 2023-10-26 diff --git a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx index 7fbb75a9057..bfec39c7c72 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx +++ b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx @@ -123,7 +123,7 @@ class MyDataTab extends React.Component { } render() { - const showTwoColumn = this.hasUserAddedData() & !this.state.activeTab; + const showTwoColumn = !!(this.hasUserAddedData() & !this.state.activeTab); const { t, className } = this.props; return ( Date: Thu, 2 Nov 2023 21:15:31 +1100 Subject: [PATCH 078/129] make operator consistent --- lib/ModelMixins/ChartableMixin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ModelMixins/ChartableMixin.ts b/lib/ModelMixins/ChartableMixin.ts index e6806b34d57..b3884db2a77 100644 --- a/lib/ModelMixins/ChartableMixin.ts +++ b/lib/ModelMixins/ChartableMixin.ts @@ -23,7 +23,7 @@ export function calculateDomain(points: ChartPoint[]): ChartDomain { const ys = points.map((p) => p.y); const asNum = (x: Date | number) => (x instanceof Date ? x.getTime() : x); return { - x: [minBy(xs, asNum) || 0, maxBy(xs, asNum) || 0], + x: [minBy(xs, asNum) ?? 0, maxBy(xs, asNum) ?? 0], y: [getMin(ys) ?? 0, getMax(ys) ?? 0] }; } From 7214e69f91b4c745672f53a24214ad6fa236a963 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Fri, 3 Nov 2023 02:56:22 +0100 Subject: [PATCH 079/129] fix: white screen --- lib/Models/SearchProviders/CatalogSearchProvider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index e1b113b4fb4..920aa16101d 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -3,7 +3,8 @@ import { computed, observable, runInAction, - makeObservable + makeObservable, + override } from "mobx"; import { fromPromise } from "mobx-utils"; import { @@ -137,7 +138,7 @@ export default class CatalogSearchProvider extends SearchProviderMixin( return CatalogSearchProvider.type; } - @computed get resultsAreReferences() { + @override get resultsAreReferences() { return ( isDefined(this.terria.catalogIndex?.loadPromise) && fromPromise(this.terria.catalogIndex!.loadPromise).state === "fulfilled" From 44236d0e97b78821196f80214479329e790b3877 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Fri, 3 Nov 2023 03:00:09 +0100 Subject: [PATCH 080/129] fix: remove unused property --- lib/Models/Terria.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index a352d8f050c..d02935554e8 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -417,7 +417,7 @@ interface HomeCameraInit { export default class Terria { private readonly models = observable.map(); - private searchProviders: any[] = []; + /** Map from share key -> id */ readonly shareKeysMap = observable.map(); /** Map from id -> share keys */ From c2d5cf13ce2ba1939ed24a1c3eeee1aff7c6b6ac Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 3 Nov 2023 14:15:39 +1100 Subject: [PATCH 081/129] Fix WMS sharekeys --- .../Catalog/Ows/WebMapServiceCatalogGroup.ts | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts b/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts index bdf48aff1ce..58517a153ae 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts @@ -201,7 +201,7 @@ class GetCapabilitiesStratum extends LoadableStratum( // At the moment we ignore duplicate layers this.catalogGroup.terria.addModel( model, - this.getLayerShareKeys(layer) + this.getLayerShareKeys(layer, layerId) ); } catch (e) { TerriaError.from(e, "Failed to add CatalogGroup").log(); @@ -253,7 +253,10 @@ class GetCapabilitiesStratum extends LoadableStratum( try { // Sometimes WMS Layers have duplicate names // At the moment we ignore duplicate layers - this.catalogGroup.terria.addModel(model, this.getLayerShareKeys(layer)); + this.catalogGroup.terria.addModel( + model, + this.getLayerShareKeys(layer, layerId) + ); } catch (e) { TerriaError.from(e, "Failed to add WebMapServiceCatalogItem").log(); return; @@ -330,14 +333,20 @@ class GetCapabilitiesStratum extends LoadableStratum( } /** For backward-compatibility. - * If layer.Name is defined, we will use it to create layer autoID (see `this.getLayerId`). - * Previously we used layer.Title, so we now add it as a shareKey + * Previously we have used the following IDs + * - `WMS Group Catalog ID/WMS Layer Name` - regardless of nesting + * - `WMS Group Catalog ID/WMS Layer Title` */ - getLayerShareKeys(layer: CapabilitiesLayer) { + getLayerShareKeys(layer: CapabilitiesLayer, layerId: string) { + const shareKeys: string[] = []; + + if (layerId !== `${this.catalogGroup.uniqueId}/${layer.Name}`) + shareKeys.push(`${this.catalogGroup.uniqueId}/${layer.Name}`); + if (isDefined(layer.Name) && layer.Title !== layer.Name) - return [`${this.catalogGroup.uniqueId}/${layer.Title}`]; + shareKeys.push(`${this.catalogGroup.uniqueId}/${layer.Title}`); - return []; + return shareKeys; } } From 9dd9a144c599b12442ed61d98a7b5085b9dbafde Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 3 Nov 2023 14:57:37 +1100 Subject: [PATCH 082/129] Add comment + update CHANGES --- CHANGES.md | 11 +++++------ .../Catalog/Ows/WebMapServiceCapabilitiesStratum.ts | 4 +++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2b14c2a1222..e3b49c67e38 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,19 +2,18 @@ #### next release (8.3.8) -- [The next improvement] - Remove `jsx-control-statements` dependency +- WMS `isEsri` default value will now check for case-insensitive `mapserver/wmsserver` (instead of `MapServer/WMSServer`) +- Tweak ArcGis MapServer WMS `GetFeatureInfo` default behaviour + - Add `application/geo+json` and `application/vnd.geo+json` default `GetFeatureInfo` (after `application/json` in priority list) + - Add `application/xml` default `GetFeatureInfo`. (if `isEsri` is true, then this will be used before `text/html`) +- [The next improvement] #### 8.3.7 - 2023-10-26 - Fix `WebMapServiceCatalogItem` `allowFeaturePicking` - Allow translation of TableStylingWorkflow. - Fix "Remove all" not removing selected/picked features -- WMS `isEsri` default value will now check for case-insensitive `mapserver/wmsserver` (instead of `MapServer/WMSServer`) -- Tweak ArcGis MapServer WMS `GetFeatureInfo` default behaviour - - Add `application/geo+json` and `application/vnd.geo+json` default `GetFeatureInfo` (after `application/json` in priority list) - - Add `application/xml` default `GetFeatureInfo`. (if `isEsri` is true, then this will be used before `text/html`) -- [The next improvement] - Fix crash on empty GeoJSON features - Add `tableFeatureInfoContext` support to `GeoJsonMixin.createProtomapsImageryProvider` - Fix `GeoJsonMixin` timeline animation for lines/polygons diff --git a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts index 32b8737cc18..67a83fb5f11 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts @@ -861,7 +861,7 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( if (formatsArray.includes("application/vnd.geo+json")) return { format: "application/vnd.geo+json", type: "json" }; - // Special case for Esri WMS, use XML before HTML + // Special case for Esri WMS, use XML before HTML/GML // as HTML includes with rowbg that is hard to read if (this.isEsri && formatsArray.includes("text/xml")) { return { format: "text/xml", type: "xml" }; @@ -870,6 +870,8 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( return { format: "text/html", type: "html" }; if (formatsArray.includes("application/vnd.ogc.gml")) return { format: "application/vnd.ogc.gml", type: "xml" }; + + // For non-Esri services, we use XML after HTML/GML if (formatsArray.includes("text/xml")) { return { format: "text/xml", type: "xml" }; } From a4fb9dd6b5a03413355d904e5eff22847fc60b5a Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 3 Nov 2023 18:22:23 +1100 Subject: [PATCH 083/129] Fix changes --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index b560d68191f..bf3f525cd4d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ #### next release (8.3.8) +- Remove `jsx-control-statements` dependency - Fix WMS nested group IDs - nested groups with the same name were not being created - WMS `isEsri` default value will now check for case-insensitive `mapserver/wmsserver` (instead of `MapServer/WMSServer`) - Tweak ArcGis MapServer WMS `GetFeatureInfo` default behaviour From 5ded654c251c0a9582864537c5b38b8414883697 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Fri, 3 Nov 2023 10:06:35 +0100 Subject: [PATCH 084/129] fix: rename ADR --- ...-search-providers.md => 0011-configurable-search-providers.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename architecture/{0007-configurable-search-providers.md => 0011-configurable-search-providers.md} (100%) diff --git a/architecture/0007-configurable-search-providers.md b/architecture/0011-configurable-search-providers.md similarity index 100% rename from architecture/0007-configurable-search-providers.md rename to architecture/0011-configurable-search-providers.md From 97920ceac3f264d9ce6212e3a6874c78108875a8 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 14:18:52 +0100 Subject: [PATCH 085/129] tests: reorganize tests and test search provider mixin --- .../SearchProviderMixinSpec.ts | 76 +++++++++++++++++++ .../BingMapsSearchProviderSpec.ts} | 0 .../LocationSearchProviderSpec.ts} | 0 3 files changed, 76 insertions(+) create mode 100644 test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts rename test/{Traits/SearchProviders/BingMapsSearchProviderTraitsSpec.ts => Models/SearchProviders/BingMapsSearchProviderSpec.ts} (100%) rename test/{Traits/SearchProviders/LocationSearchProviderTraitsSpec.ts => Models/SearchProviders/LocationSearchProviderSpec.ts} (100%) diff --git a/test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts b/test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts new file mode 100644 index 00000000000..54c07c34b38 --- /dev/null +++ b/test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts @@ -0,0 +1,76 @@ +import { CommonStrata } from "terriajs-plugin-api"; +import SearchProviderMixin from "../../../lib/ModelMixins/SearchProviders/SearchProviderMixin"; +import CreateModel from "../../../lib/Models/Definition/CreateModel"; +import SearchProviderResults from "../../../lib/Models/SearchProviders/SearchProviderResults"; +import Terria from "../../../lib/Models/Terria"; +import BingMapsSearchProviderTraits from "../../../lib/Traits/SearchProviders/BingMapsSearchProviderTraits"; + +class TestSearchProvider extends SearchProviderMixin( + CreateModel(BingMapsSearchProviderTraits) +) { + type = "test"; + + constructor(uniqueId: string | undefined, terria: Terria) { + super(uniqueId, terria); + } + + public override logEvent = jasmine.createSpy(); + public override doSearch = jasmine + .createSpy() + .and.returnValue(Promise.resolve()); +} + +describe("SearchProviderMixin", () => { + let terria: Terria; + let searchProvider: TestSearchProvider; + + beforeEach(() => { + terria = new Terria({ + baseUrl: "./" + }); + searchProvider = new TestSearchProvider("test", terria); + searchProvider.setTrait(CommonStrata.definition, "minCharacters", 3); + searchProvider.logEvent.calls.reset(); + searchProvider.doSearch.calls.reset(); + }); + + it(" - properly mixed", () => { + expect(SearchProviderMixin.isMixedInto(searchProvider)).toBeTruthy(); + }); + + it(" - should not run search if searchText is undefined", () => { + const result = searchProvider.search(undefined as never); + expect(result.resultsCompletePromise).toBeDefined(); + expect(result.message).toBeDefined(); + + expect(searchProvider.logEvent).not.toHaveBeenCalled(); + expect(searchProvider.doSearch).not.toHaveBeenCalled(); + }); + + it(" - should not run search if only spaces", () => { + const result = searchProvider.search(" "); + expect(result.resultsCompletePromise).toBeDefined(); + expect(result.message).toBeDefined(); + + expect(searchProvider.logEvent).not.toHaveBeenCalled(); + expect(searchProvider.doSearch).not.toHaveBeenCalled(); + }); + + it(" - should not run search if searchText less than minCharacters", () => { + const result = searchProvider.search("12"); + expect(result.resultsCompletePromise).toBeDefined(); + expect(result.message).toBeDefined(); + + expect(searchProvider.logEvent).not.toHaveBeenCalled(); + expect(searchProvider.doSearch).not.toHaveBeenCalled(); + }); + + it(" - should run search if searchText is valid", () => { + const result = searchProvider.search("1234"); + expect(result.resultsCompletePromise).toBeDefined(); + expect(result.message).not.toBeDefined(); + + expect(searchProvider.logEvent).toHaveBeenCalled(); + expect(searchProvider.doSearch).toHaveBeenCalled(); + }); +}); diff --git a/test/Traits/SearchProviders/BingMapsSearchProviderTraitsSpec.ts b/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts similarity index 100% rename from test/Traits/SearchProviders/BingMapsSearchProviderTraitsSpec.ts rename to test/Models/SearchProviders/BingMapsSearchProviderSpec.ts diff --git a/test/Traits/SearchProviders/LocationSearchProviderTraitsSpec.ts b/test/Models/SearchProviders/LocationSearchProviderSpec.ts similarity index 100% rename from test/Traits/SearchProviders/LocationSearchProviderTraitsSpec.ts rename to test/Models/SearchProviders/LocationSearchProviderSpec.ts From 6263bb5d5a51c5507e2a12dc4090285bc0e89b33 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 14:19:19 +0100 Subject: [PATCH 086/129] fix: ADR title number --- architecture/0011-configurable-search-providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/architecture/0011-configurable-search-providers.md b/architecture/0011-configurable-search-providers.md index a80f24246fb..2779eaf5a97 100644 --- a/architecture/0011-configurable-search-providers.md +++ b/architecture/0011-configurable-search-providers.md @@ -1,4 +1,4 @@ -# 7. Configuration of search providers +# 11. Configuration of search providers Date: 2021-01-19 From 07057ab7aa82f96c982bd670acd7751ac2afcacd Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 15:13:46 +0100 Subject: [PATCH 087/129] feat: add CatalogSearchProviderMixin and small types refactor --- .../CatalogSearchProviderMixin.ts | 44 +++++++++++++++++++ .../LocationSearchProviderMixin.ts | 6 +-- .../SearchProviders/SearchProviderMixin.ts | 8 +--- .../WebFeatureServiceSearchProviderMixin.ts | 8 ++-- .../SearchProviders/CatalogSearchProvider.ts | 10 +---- .../SearchProviders/SearchProviderResults.ts | 4 +- lib/ReactViewModels/SearchState.ts | 11 ++--- lib/ReactViewModels/ViewState.ts | 6 ++- .../Search/LocationSearchResults.tsx | 9 ++-- test/Map/StyledHtmlSpec.tsx | 2 +- .../MapNavigation/MapNavigationModelSpec.ts | 2 +- test/Models/TerriaSpec.ts | 6 +-- test/ReactViews/BottomDock/BottomDockSpec.tsx | 2 +- .../BottomDock/MapDataCountSpec.tsx | 2 +- .../DataCatalog/DataCatalogItemSpec.tsx | 2 +- test/ReactViews/DisclaimerSpec.tsx | 2 +- test/ReactViews/FeatureInfoPanelSpec.tsx | 2 +- test/ReactViews/Generic/PromptSpec.tsx | 2 +- .../Map/Navigation/Compass/CompassSpec.tsx | 2 +- .../Compass/GyroscopeGuidanceSpec.tsx | 2 +- .../Map/Panels/HelpPanel/HelpPanelSpec.tsx | 2 +- .../Map/Panels/HelpPanel/VideoGuideSpec.tsx | 2 +- .../Map/Panels/LangPanel/LangPanelSpec.tsx | 2 +- .../Panels/SharePanel/BuildShareLinkSpec.ts | 2 +- test/ReactViews/Search/BreadcrumbsSpec.tsx | 2 +- .../Search/SearchBoxAndResultsSpec.tsx | 2 +- test/ReactViews/Search/SearchBoxSpec.tsx | 2 +- .../TrainerBar/TrainerBarSpec.tsx | 2 +- .../ItemSearchTool/ItemSearchToolSpec.tsx | 2 +- test/ReactViews/Tour/TourPortalSpec.tsx | 2 +- test/ReactViews/WelcomeMessageSpec.tsx | 2 +- 31 files changed, 93 insertions(+), 61 deletions(-) create mode 100644 lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts diff --git a/lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts new file mode 100644 index 00000000000..a0a62d02afd --- /dev/null +++ b/lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts @@ -0,0 +1,44 @@ +import { computed, makeObservable } from "mobx"; +import AbstractConstructor from "../../Core/AbstractConstructor"; +import Model from "../../Models/Definition/Model"; +import SearchProviderTraits from "../../Traits/SearchProviders/SearchProviderTraits"; +import SearchProviderMixin from "./SearchProviderMixin"; +import isDefined from "../../Core/isDefined"; +import { fromPromise } from "mobx-utils"; + +type CatalogSearchProviderModel = Model; + +function CatalogSearchProviderMixin< + T extends AbstractConstructor +>(Base: T) { + abstract class CatalogSearchProviderMixin extends SearchProviderMixin(Base) { + constructor(...args: any[]) { + super(...args); + makeObservable(this); + } + + @computed get resultsAreReferences() { + return ( + isDefined(this.terria.catalogIndex?.loadPromise) && + fromPromise(this.terria.catalogIndex!.loadPromise).state === "fulfilled" + ); + } + + get hasCatalogSearchProviderMixin() { + return true; + } + } + + return CatalogSearchProviderMixin; +} + +namespace CatalogSearchProviderMixin { + export interface Instance + extends InstanceType> {} + + export function isMixedInto(model: any): model is Instance { + return model && model.hasCatalogSearchProviderMixin; + } +} + +export default CatalogSearchProviderMixin; diff --git a/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts index 31c1784d414..33a73f8372c 100644 --- a/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts @@ -59,12 +59,10 @@ export function getMapCenter(terria: Terria): MapCenter { } namespace LocationSearchProviderMixin { - export interface LocationSearchProviderMixin + export interface Instance extends InstanceType> {} - export function isMixedInto( - model: any - ): model is LocationSearchProviderMixin { + export function isMixedInto(model: any): model is Instance { return model && model.hasLocationSearchProviderMixin; } } diff --git a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts index badb379b831..8825b2102bb 100644 --- a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts @@ -64,20 +64,16 @@ function SearchProviderMixin< get hasSearchProviderMixin() { return true; } - - @computed get resultsAreReferences() { - return isDefined(this.terria.catalogIndex); - } } return SearchProviderMixin; } namespace SearchProviderMixin { - export interface SearchProviderMixin + export interface Instance extends InstanceType> {} - export function isMixedInto(model: any): model is SearchProviderMixin { + export function isMixedInto(model: any): model is Instance { return model && model.hasSearchProviderMixin; } } diff --git a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index c80a3d1abfa..ef81a3e1160 100644 --- a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -199,21 +199,19 @@ function WebFeatureServiceSearchProviderMixin< } namespace WebFeatureServiceSearchProviderMixin { - export interface WebFeatureServiceSearchProviderMixin + export interface Instance extends InstanceType< ReturnType > {} - export function isMixedInto( - model: any - ): model is WebFeatureServiceSearchProviderMixin { + export function isMixedInto(model: any): model is Instance { return model && model.isWebFeatureServiceSearchProviderMixin; } } export default WebFeatureServiceSearchProviderMixin; function createZoomToFunction( - model: WebFeatureServiceSearchProviderMixin.WebFeatureServiceSearchProviderMixin, + model: WebFeatureServiceSearchProviderMixin.Instance, location: any ) { // Server does not return information of a bounding box, just a location. diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index 920aa16101d..175a4600c1a 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -23,6 +23,7 @@ import Terria from "../Terria"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; import isDefined from "../../Core/isDefined"; +import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; type UniqueIdString = string; type ResultMap = Map; @@ -115,7 +116,7 @@ export function loadAndSearchCatalogRecursively( }); } -export default class CatalogSearchProvider extends SearchProviderMixin( +export default class CatalogSearchProvider extends CatalogSearchProviderMixin( CreateModel(CatalogSearchProviderTraits) ) { static readonly type = "catalog-search-provider"; @@ -138,13 +139,6 @@ export default class CatalogSearchProvider extends SearchProviderMixin( return CatalogSearchProvider.type; } - @override get resultsAreReferences() { - return ( - isDefined(this.terria.catalogIndex?.loadPromise) && - fromPromise(this.terria.catalogIndex!.loadPromise).state === "fulfilled" - ); - } - protected logEvent(searchText: string) { this.terria.analytics?.logEvent( Category.search, diff --git a/lib/Models/SearchProviders/SearchProviderResults.ts b/lib/Models/SearchProviders/SearchProviderResults.ts index 6ddfb1cc9bb..1b2ce0cf6d0 100644 --- a/lib/Models/SearchProviders/SearchProviderResults.ts +++ b/lib/Models/SearchProviders/SearchProviderResults.ts @@ -16,9 +16,7 @@ export default class SearchProviderResults { Promise.resolve() ); - constructor( - readonly searchProvider: SearchProviderMixin.SearchProviderMixin - ) { + constructor(readonly searchProvider: SearchProviderMixin.Instance) { makeObservable(this); } diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index 1a64d95d20f..df12571996d 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -12,19 +12,20 @@ import SearchProviderMixin from "../ModelMixins/SearchProviders/SearchProviderMi import CatalogSearchProvider from "../Models/SearchProviders/CatalogSearchProvider"; import SearchProviderResults from "../Models/SearchProviders/SearchProviderResults"; import Terria from "../Models/Terria"; +import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; interface SearchStateOptions { terria: Terria; - catalogSearchProvider?: CatalogSearchProvider; - locationSearchProviders?: LocationSearchProviderMixin.LocationSearchProviderMixin[]; + catalogSearchProvider?: CatalogSearchProviderMixin.Instance; + locationSearchProviders?: LocationSearchProviderMixin.Instance[]; } export default class SearchState { @observable - catalogSearchProvider: SearchProviderMixin.SearchProviderMixin | undefined; + catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; @observable - locationSearchProviders: LocationSearchProviderMixin.LocationSearchProviderMixin[]; + locationSearchProviders: LocationSearchProviderMixin.Instance[]; @observable catalogSearchText: string = ""; @observable isWaitingToStartCatalogSearch: boolean = false; @@ -98,7 +99,7 @@ export default class SearchState { } @computed - get unifiedSearchProviders(): SearchProviderMixin.SearchProviderMixin[] { + get unifiedSearchProviders(): SearchProviderMixin.Instance[] { return filterOutUndefined([ this.catalogSearchProvider, ...this.locationSearchProviders diff --git a/lib/ReactViewModels/ViewState.ts b/lib/ReactViewModels/ViewState.ts index caaa9449bcf..360fa325e00 100644 --- a/lib/ReactViewModels/ViewState.ts +++ b/lib/ReactViewModels/ViewState.ts @@ -38,6 +38,8 @@ import { } from "./defaultTourPoints"; import DisclaimerHandler from "./DisclaimerHandler"; import SearchState from "./SearchState"; +import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; +import LocationSearchProviderMixin from "../ModelMixins/SearchProviders/LocationSearchProviderMixin"; export const DATA_CATALOG_NAME = "data-catalog"; export const USER_DATA_NAME = "my-data"; @@ -48,8 +50,8 @@ export const WORKBENCH_RESIZE_ANIMATION_DURATION = 500; interface ViewStateOptions { terria: Terria; - catalogSearchProvider: any; - locationSearchProviders: any[]; + catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; + locationSearchProviders: LocationSearchProviderMixin.Instance[]; errorHandlingProvider?: any; } diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index e3157571b87..1984b20a5b0 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -19,6 +19,7 @@ import { applyTranslationIfExists } from "../../Language/languageHelpers"; import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; import Terria from "../../Models/Terria"; +import SearchResultModel from "../../Models/SearchProviders/SearchResult"; import ViewState from "../../ReactViewModels/ViewState"; import Box, { BoxSpan } from "../../Styled/Box"; import { RawButton } from "../../Styled/Button"; @@ -44,7 +45,7 @@ interface PropsType extends WithTranslation { isWaitingForSearchToStart: boolean; terria: Terria; search: SearchProviderResults; - onLocationClick: () => void; + onLocationClick: (result: SearchResultModel) => void; theme: DefaultTheme; locationSearchText: string; } @@ -98,8 +99,8 @@ class LocationSearchResults extends React.Component { render() { const { search } = this.props; - const searchProvider: LocationSearchProviderMixin.LocationSearchProviderMixin = - search.searchProvider as any; + const searchProvider: LocationSearchProviderMixin.Instance = + search.searchProvider as unknown as LocationSearchProviderMixin.Instance; const maxResults = searchProvider.recommendedListLength || 5; const validResults = this.validResults; @@ -144,7 +145,7 @@ class LocationSearchResults extends React.Component { isWaitingForSearchToStart={this.props.isWaitingForSearchToStart} />
      - {results.map((result: any, i: number) => ( + {results.map((result: SearchResultModel, i: number) => ( { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); wmsItem = new WebMapServiceCatalogItem("test", terria); diff --git a/test/ReactViews/DisclaimerSpec.tsx b/test/ReactViews/DisclaimerSpec.tsx index b9672dd256b..f23132f2f1d 100644 --- a/test/ReactViews/DisclaimerSpec.tsx +++ b/test/ReactViews/DisclaimerSpec.tsx @@ -19,7 +19,7 @@ describe("Disclaimer", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/FeatureInfoPanelSpec.tsx b/test/ReactViews/FeatureInfoPanelSpec.tsx index 8abc2b9d572..5d498d89298 100644 --- a/test/ReactViews/FeatureInfoPanelSpec.tsx +++ b/test/ReactViews/FeatureInfoPanelSpec.tsx @@ -37,7 +37,7 @@ describe("FeatureInfoPanel", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Generic/PromptSpec.tsx b/test/ReactViews/Generic/PromptSpec.tsx index 31b7fa6f65b..5bd08e5153e 100644 --- a/test/ReactViews/Generic/PromptSpec.tsx +++ b/test/ReactViews/Generic/PromptSpec.tsx @@ -19,7 +19,7 @@ describe("HelpPrompt", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx b/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx index d88e6da7f4f..426a6456deb 100644 --- a/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx +++ b/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx @@ -21,7 +21,7 @@ describe("Compass", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx b/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx index f7883198777..40797f13eae 100644 --- a/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx +++ b/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx @@ -18,7 +18,7 @@ describe("GyroscopeGuidance", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx b/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx index 46f29ea5b71..9d1aa56fc21 100644 --- a/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx +++ b/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx @@ -23,7 +23,7 @@ describe("HelpPanel", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx b/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx index 884d9ffeaf5..30a95b5e0a8 100644 --- a/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx +++ b/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx @@ -23,7 +23,7 @@ describe("VideoGuide", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx b/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx index ba81a1c5ed5..37c04b4be7b 100644 --- a/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx +++ b/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx @@ -20,7 +20,7 @@ describe("LangPanel", function () { viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts b/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts index fef5d94cac1..f1ed6bb81ad 100644 --- a/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts +++ b/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts @@ -36,7 +36,7 @@ beforeEach(function () { viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Search/BreadcrumbsSpec.tsx b/test/ReactViews/Search/BreadcrumbsSpec.tsx index f85ce9a5278..c15c314be8f 100644 --- a/test/ReactViews/Search/BreadcrumbsSpec.tsx +++ b/test/ReactViews/Search/BreadcrumbsSpec.tsx @@ -25,7 +25,7 @@ describe("Breadcrumbs", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); catalogGroup = new CatalogGroup("group-of-geospatial-cats", terria); diff --git a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx index f62f0fd3a6d..11b936b9f57 100644 --- a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx +++ b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx @@ -22,7 +22,7 @@ describe("SearchBoxAndResults", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); diff --git a/test/ReactViews/Search/SearchBoxSpec.tsx b/test/ReactViews/Search/SearchBoxSpec.tsx index bb60465a479..7c06ec6c542 100644 --- a/test/ReactViews/Search/SearchBoxSpec.tsx +++ b/test/ReactViews/Search/SearchBoxSpec.tsx @@ -18,7 +18,7 @@ describe("SearchBox", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx b/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx index 7bba0db2c75..86cb68eccff 100644 --- a/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx +++ b/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx @@ -20,7 +20,7 @@ describe("TrainerBar", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx b/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx index d02a6666e75..7c810247bb1 100644 --- a/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx +++ b/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx @@ -58,7 +58,7 @@ describe("ItemSearchTool", function () { const terria: Terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); item = new MockSearchableItem("test", terria); diff --git a/test/ReactViews/Tour/TourPortalSpec.tsx b/test/ReactViews/Tour/TourPortalSpec.tsx index 4d0d1108d72..5c16fe79011 100644 --- a/test/ReactViews/Tour/TourPortalSpec.tsx +++ b/test/ReactViews/Tour/TourPortalSpec.tsx @@ -23,7 +23,7 @@ describe("TourPortal", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); diff --git a/test/ReactViews/WelcomeMessageSpec.tsx b/test/ReactViews/WelcomeMessageSpec.tsx index ce7fa35d34d..6d4a5be70dd 100644 --- a/test/ReactViews/WelcomeMessageSpec.tsx +++ b/test/ReactViews/WelcomeMessageSpec.tsx @@ -20,7 +20,7 @@ describe("WelcomeMessage", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: null, + catalogSearchProvider: undefined, locationSearchProviders: [] }); }); From 1e841bc8530ab5e8ceeb2d0f5f3720b2df77a9ae Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 17:32:16 +0100 Subject: [PATCH 088/129] fix: organise imports --- .../SearchProviders/CatalogSearchProviderMixin.ts | 4 ++-- .../SearchProviders/LocationSearchProviderMixin.ts | 3 +-- .../SearchProviders/SearchProviderMixin.ts | 6 ++---- .../WebFeatureServiceSearchProviderMixin.ts | 3 +-- .../AustralianGazetteerSearchProvider.ts | 2 +- .../SearchProviders/BingMapsSearchProvider.ts | 6 +++--- lib/Models/SearchProviders/CatalogIndex.ts | 2 +- .../SearchProviders/CatalogSearchProvider.ts | 14 ++------------ .../SearchProviders/SearchProviderResults.ts | 4 ++-- lib/Models/SearchProviders/SearchResult.ts | 2 +- lib/Models/SearchProviders/StubSearchProvider.ts | 2 +- .../SearchProviders/registerSearchProviders.ts | 2 +- .../upsertSearchProviderFromJson.ts | 2 +- 13 files changed, 19 insertions(+), 33 deletions(-) diff --git a/lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts index a0a62d02afd..d2c0209c731 100644 --- a/lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/CatalogSearchProviderMixin.ts @@ -1,10 +1,10 @@ import { computed, makeObservable } from "mobx"; +import { fromPromise } from "mobx-utils"; import AbstractConstructor from "../../Core/AbstractConstructor"; +import isDefined from "../../Core/isDefined"; import Model from "../../Models/Definition/Model"; import SearchProviderTraits from "../../Traits/SearchProviders/SearchProviderTraits"; import SearchProviderMixin from "./SearchProviderMixin"; -import isDefined from "../../Core/isDefined"; -import { fromPromise } from "mobx-utils"; type CatalogSearchProviderModel = Model; diff --git a/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts index 33a73f8372c..80543f209f2 100644 --- a/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts @@ -2,13 +2,12 @@ import { action, makeObservable } from "mobx"; import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; -import Constructor from "../../Core/Constructor"; +import AbstractConstructor from "../../Core/AbstractConstructor"; import CommonStrata from "../../Models/Definition/CommonStrata"; import Model from "../../Models/Definition/Model"; import Terria from "../../Models/Terria"; import LocationSearchProviderTraits from "../../Traits/SearchProviders/LocationSearchProviderTraits"; import SearchProviderMixin from "./SearchProviderMixin"; -import AbstractConstructor from "../../Core/AbstractConstructor"; type LocationSearchProviderModel = Model; diff --git a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts index 8825b2102bb..b2fb69345e9 100644 --- a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts @@ -1,11 +1,9 @@ -import { action, computed, makeObservable } from "mobx"; +import { action, makeObservable } from "mobx"; import { fromPromise } from "mobx-utils"; -import Constructor from "../../Core/Constructor"; -import isDefined from "../../Core/isDefined"; +import AbstractConstructor from "../../Core/AbstractConstructor"; import Model from "../../Models/Definition/Model"; import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; import SearchProviderTraits from "../../Traits/SearchProviders/SearchProviderTraits"; -import AbstractConstructor from "../../Core/AbstractConstructor"; type SearchProviderModel = Model; diff --git a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index ef81a3e1160..7e8bf92b56b 100644 --- a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -1,7 +1,7 @@ import { makeObservable, runInAction } from "mobx"; import Resource from "terriajs-cesium/Source/Core/Resource"; import URI from "urijs"; -import Constructor from "../../Core/Constructor"; +import AbstractConstructor from "../../Core/AbstractConstructor"; import zoomRectangleFromPoint from "../../Map/Vector/zoomRectangleFromPoint"; import Model from "../../Models/Definition/Model"; import SearchProviderResults from "../../Models/SearchProviders/SearchProviderResults"; @@ -9,7 +9,6 @@ import SearchResult from "../../Models/SearchProviders/SearchResult"; import xml2json from "../../ThirdParty/xml2json"; import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProviders/WebFeatureServiceSearchProviderTraits"; import LocationSearchProviderMixin from "./LocationSearchProviderMixin"; -import AbstractConstructor from "../../Core/AbstractConstructor"; function WebFeatureServiceSearchProviderMixin< T extends AbstractConstructor> diff --git a/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts b/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts index f919f526285..2072841b206 100644 --- a/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts +++ b/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts @@ -6,8 +6,8 @@ import { import WebFeatureServiceSearchProviderMixin from "../../ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin"; import WebFeatureServiceSearchProviderTraits from "../../Traits/SearchProviders/WebFeatureServiceSearchProviderTraits"; import CreateModel from "../Definition/CreateModel"; -import SearchResult from "./SearchResult"; import { ModelConstructorParameters } from "../Definition/Model"; +import SearchResult from "./SearchResult"; const featureCodesToNamesMap = new Map([ ["AF", "Aviation"], diff --git a/lib/Models/SearchProviders/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts index 04feab4bc99..a95be334e0a 100644 --- a/lib/Models/SearchProviders/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -1,7 +1,8 @@ -import { runInAction, makeObservable } from "mobx"; -import defined from "terriajs-cesium/Source/Core/defined"; +import i18next from "i18next"; +import { makeObservable, 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 @@ -17,7 +18,6 @@ import Terria from "../Terria"; import CommonStrata from "./../Definition/CommonStrata"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; -import i18next from "i18next"; export default class BingMapsSearchProvider extends LocationSearchProviderMixin( CreateModel(BingMapsSearchProviderTraits) diff --git a/lib/Models/SearchProviders/CatalogIndex.ts b/lib/Models/SearchProviders/CatalogIndex.ts index 5450fdeb2c3..bf3a79bfcd1 100644 --- a/lib/Models/SearchProviders/CatalogIndex.ts +++ b/lib/Models/SearchProviders/CatalogIndex.ts @@ -1,5 +1,5 @@ import { Document } from "flexsearch"; -import { action, observable, runInAction, makeObservable } from "mobx"; +import { action, makeObservable, observable, runInAction } from "mobx"; import { isJsonObject, isJsonString, isJsonStringArray } from "../../Core/Json"; import loadBlob, { isZip, parseZipJsonBlob } from "../../Core/loadBlob"; import loadJson from "../../Core/loadJson"; diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index 175a4600c1a..b3eff29c5f7 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -1,12 +1,4 @@ -import { - autorun, - computed, - observable, - runInAction, - makeObservable, - override -} from "mobx"; -import { fromPromise } from "mobx-utils"; +import { autorun, makeObservable, observable, runInAction } from "mobx"; import { Category, SearchAction @@ -14,7 +6,7 @@ import { import { TerriaErrorSeverity } from "../../Core/TerriaError"; import GroupMixin from "../../ModelMixins/GroupMixin"; import ReferenceMixin from "../../ModelMixins/ReferenceMixin"; -import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProviderMixin"; +import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; import CatalogSearchProviderTraits from "../../Traits/SearchProviders/CatalogSearchProviderTraits"; import CommonStrata from "../Definition/CommonStrata"; import CreateModel from "../Definition/CreateModel"; @@ -22,8 +14,6 @@ import { BaseModel } from "../Definition/Model"; import Terria from "../Terria"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; -import isDefined from "../../Core/isDefined"; -import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; type UniqueIdString = string; type ResultMap = Map; diff --git a/lib/Models/SearchProviders/SearchProviderResults.ts b/lib/Models/SearchProviders/SearchProviderResults.ts index 1b2ce0cf6d0..afd03f3f08e 100644 --- a/lib/Models/SearchProviders/SearchProviderResults.ts +++ b/lib/Models/SearchProviders/SearchProviderResults.ts @@ -1,5 +1,5 @@ -import { observable, makeObservable } from "mobx"; -import { fromPromise, IPromiseBasedObservable } from "mobx-utils"; +import { makeObservable, observable } from "mobx"; +import { IPromiseBasedObservable, fromPromise } from "mobx-utils"; import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProviderMixin"; import SearchResult from "./SearchResult"; diff --git a/lib/Models/SearchProviders/SearchResult.ts b/lib/Models/SearchProviders/SearchResult.ts index 24582fa6021..55495726876 100644 --- a/lib/Models/SearchProviders/SearchResult.ts +++ b/lib/Models/SearchProviders/SearchResult.ts @@ -1,4 +1,4 @@ -import { action, observable, makeObservable } from "mobx"; +import { action, makeObservable, observable } from "mobx"; import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; import defined from "terriajs-cesium/Source/Core/defined"; import GroupMixin from "../../ModelMixins/GroupMixin"; diff --git a/lib/Models/SearchProviders/StubSearchProvider.ts b/lib/Models/SearchProviders/StubSearchProvider.ts index 70909a4e002..6ba2399bff7 100644 --- a/lib/Models/SearchProviders/StubSearchProvider.ts +++ b/lib/Models/SearchProviders/StubSearchProvider.ts @@ -3,8 +3,8 @@ import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProvide import primitiveTrait from "../../Traits/Decorators/primitiveTrait"; import LocationSearchProviderTraits from "../../Traits/SearchProviders/LocationSearchProviderTraits"; import CreateModel from "../Definition/CreateModel"; -import SearchProviderResults from "./SearchProviderResults"; import { ModelConstructorParameters } from "../Definition/Model"; +import SearchProviderResults from "./SearchProviderResults"; export class StubSearchProviderTraits extends LocationSearchProviderTraits { @primitiveTrait({ diff --git a/lib/Models/SearchProviders/registerSearchProviders.ts b/lib/Models/SearchProviders/registerSearchProviders.ts index 919a6da697c..49d2ca854f2 100644 --- a/lib/Models/SearchProviders/registerSearchProviders.ts +++ b/lib/Models/SearchProviders/registerSearchProviders.ts @@ -1,5 +1,5 @@ -import BingMapsSearchProvider from "./BingMapsSearchProvider"; import AustralianGazetteerSearchProvider from "./AustralianGazetteerSearchProvider"; +import BingMapsSearchProvider from "./BingMapsSearchProvider"; import SearchProviderFactory from "./SearchProviderFactory"; export default function registerSearchProviders() { diff --git a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index b19664e3def..96fa81654f1 100644 --- a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -7,8 +7,8 @@ import { BaseModel } from "../Definition/Model"; import ModelFactory from "../Definition/ModelFactory"; import updateModelFromJson from "../Definition/updateModelFromJson"; import Terria from "../Terria"; -import createStubSearchProvider from "./createStubSearchProvider"; import StubSearchProvider from "./StubSearchProvider"; +import createStubSearchProvider from "./createStubSearchProvider"; export default function upsertSearchProviderFromJson( factory: ModelFactory, From 7a529e312d45a3390f2494231562f00f8e6ca46b Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 18:36:18 +0100 Subject: [PATCH 089/129] fix: replace deprecated substr method --- lib/Language/languageHelpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Language/languageHelpers.ts b/lib/Language/languageHelpers.ts index 396fadcebf4..4e6f2de4d24 100644 --- a/lib/Language/languageHelpers.ts +++ b/lib/Language/languageHelpers.ts @@ -22,7 +22,7 @@ export function applyTranslationIfExists( // keyOrString could be undefined in some cases even if we type it as string if (isJsonString(keyOrString as unknown)) { if (keyOrString.indexOf(TRANSLATE_KEY_PREFIX) === 0) { - const translationKey = keyOrString.substr(TRANSLATE_KEY_PREFIX.length); + const translationKey = keyOrString.substring(TRANSLATE_KEY_PREFIX.length); return i18n.exists(translationKey) ? i18n.t(translationKey, options) : translationKey; From 8013f541fe41a346f5fef6a5ed7c89f2c79d24cc Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 18:39:29 +0100 Subject: [PATCH 090/129] fix: search model type definition --- .../SearchProviders/SearchProviderMixin.ts | 5 +---- .../WebFeatureServiceSearchProviderMixin.ts | 2 +- lib/Models/SearchProviders/CatalogSearchProvider.ts | 2 +- .../SearchProviders/createStubSearchProvider.ts | 2 +- .../SearchProviders/upsertSearchProviderFromJson.ts | 8 ++++---- lib/Models/Terria.ts | 9 ++++----- lib/ReactViews/Mobile/MobileHeader.jsx | 12 ++++-------- lib/ReactViews/Search/LocationSearchResults.tsx | 2 +- lib/ReactViews/SidePanel/SidePanel.tsx | 12 ++++-------- 9 files changed, 21 insertions(+), 33 deletions(-) diff --git a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts index b2fb69345e9..7d58e2683e2 100644 --- a/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/SearchProviderMixin.ts @@ -49,10 +49,7 @@ function SearchProviderMixin< if ( searchText === undefined || /^\s*$/.test(searchText) || - (this.minCharacters && searchText.length < this.minCharacters) || - (this.minCharacters === undefined && - searchText.length < - this.terria.configParameters.searchBarModel!.minCharacters) + (this.minCharacters && searchText.length < this.minCharacters) ) { return false; } diff --git a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index 7e8bf92b56b..082b32176f3 100644 --- a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -224,7 +224,7 @@ function createZoomToFunction( const flightDurationSeconds: number = model.flightDurationSeconds || - model.terria.configParameters.searchBarModel!.flightDurationSeconds; + model.terria.configParameters.searchBarModel.flightDurationSeconds; return function () { model.terria.currentViewer.zoomTo(rectangle, flightDurationSeconds); diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index b3eff29c5f7..8bb3f6f0f74 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -121,7 +121,7 @@ export default class CatalogSearchProvider extends CatalogSearchProviderMixin( this.setTrait( CommonStrata.defaults, "minCharacters", - terria.configParameters.searchBarModel!.minCharacters + terria.configParameters.searchBarModel.minCharacters ); } diff --git a/lib/Models/SearchProviders/createStubSearchProvider.ts b/lib/Models/SearchProviders/createStubSearchProvider.ts index e7098bad3fb..8658a0b3aac 100644 --- a/lib/Models/SearchProviders/createStubSearchProvider.ts +++ b/lib/Models/SearchProviders/createStubSearchProvider.ts @@ -22,6 +22,6 @@ export default function createStubSearchProvider( const stub = new StubSearchProvider(idToUse, terria); stub.setTrait(CommonStrata.underride, "name", stub.uniqueId); - terria.configParameters.searchBarModel?.addSearchProvider(stub); + terria.configParameters.searchBarModel.addSearchProvider(stub); return stub; } diff --git a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index 96fa81654f1..31e5a79c32e 100644 --- a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -53,7 +53,7 @@ export default function upsertSearchProviderFromJson( if (model.type !== StubSearchProvider.type) { try { - model.terria.configParameters.searchBarModel?.addSearchProvider(model); + model.terria.configParameters.searchBarModel.addSearchProvider(model); } catch (error) { errors.push(TerriaError.from(error)); } @@ -85,18 +85,18 @@ function setDefaultTraits(model: BaseModel) { model.setTrait( CommonStrata.defaults, "flightDurationSeconds", - terria.configParameters.searchBarModel?.flightDurationSeconds + terria.configParameters.searchBarModel.flightDurationSeconds ); model.setTrait( CommonStrata.defaults, "minCharacters", - terria.configParameters.searchBarModel?.minCharacters + terria.configParameters.searchBarModel.minCharacters ); model.setTrait( CommonStrata.defaults, "recommendedListLength", - terria.configParameters.searchBarModel?.recommendedListLength + terria.configParameters.searchBarModel.recommendedListLength ); } diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index d02935554e8..776e08dea20 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -345,7 +345,7 @@ export interface ConfigParameters { /** * The search bar allows requesting information from various search services at once. */ - searchBarModel?: SearchBarModel; + searchBarModel: SearchBarModel; searchProviders: any[]; } @@ -995,8 +995,7 @@ export default class Terria { if (isJsonObject(config) && isJsonObject(config.parameters)) { this.updateParameters(config.parameters); } - if (isJsonObject(config) && Array.isArray(config.searchProviders)) { - } + if (this.configParameters.errorService) { this.setupErrorServiceProvider(this.configParameters.errorService); } @@ -1055,7 +1054,7 @@ export default class Terria { ); this.configParameters.searchBarModel - ?.initializeSearchProviders() + .initializeSearchProviders() .catchError((error) => this.raiseErrorToUser( TerriaError.from(error, "Failed to initialize searchProviders") @@ -1302,7 +1301,7 @@ export default class Terria { this.configParameters.searchBarModel = new SearchBarModel(this); } updateModelFromJson( - this.configParameters.searchBarModel!, + this.configParameters.searchBarModel, CommonStrata.definition, value ); diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index d346df0ea96..41a04e3858b 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -137,14 +137,10 @@ class MobileHeader extends React.Component { searchText={searchState.locationSearchText} onSearchTextChanged={this.changeLocationSearchText.bind(this)} onDoSearch={this.searchLocations.bind(this)} - placeholder={ - viewState.terria.configParameters.searchBarModel - ? applyTranslationIfExists( - viewState.terria.configParameters.searchBarModel.placeholder, - this.props.i18n - ) - : t("search.placeholder") - } + placeholder={applyTranslationIfExists( + viewState.terria.configParameters.searchBarModel.placeholder, + this.props.i18n + )} alwaysShowClear={true} onClear={this.closeLocationSearch.bind(this)} autoFocus={true} diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 1984b20a5b0..0c7b5af5623 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -68,7 +68,7 @@ class LocationSearchResults extends React.Component { get validResults() { const { search, terria } = this.props; const locationSearchBoundingBox = - terria.configParameters.searchBarModel?.boundingBoxLimit; + terria.configParameters.searchBarModel.boundingBoxLimit; let filterResults = false; let west: number | undefined, east: number | undefined, diff --git a/lib/ReactViews/SidePanel/SidePanel.tsx b/lib/ReactViews/SidePanel/SidePanel.tsx index 9abecaea621..6f6c584cff4 100644 --- a/lib/ReactViews/SidePanel/SidePanel.tsx +++ b/lib/ReactViews/SidePanel/SidePanel.tsx @@ -163,14 +163,10 @@ const SidePanel = observer>( From 64e2f8970c7f55099b32d22fe2c1fb343264e34b Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 18:40:24 +0100 Subject: [PATCH 091/129] fix: remove duplicate code --- lib/Models/SearchProviders/CatalogSearchProvider.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index 8bb3f6f0f74..a30ec3646d6 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -163,18 +163,6 @@ export default class CatalogSearchProvider extends CatalogSearchProviderMixin( } } - // Load catalogIndex if needed - if (this.terria.catalogIndex && !this.terria.catalogIndex.loadPromise) { - try { - await this.terria.catalogIndex.load(); - } catch (e) { - this.terria.raiseErrorToUser( - e, - "Failed to load catalog index. Searching may be slow/inaccurate" - ); - } - } - const resultMap: ResultMap = new Map(); try { From 34d2dfc3df107eaca435a1b1672f04907c53dc7c Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 18:56:13 +0100 Subject: [PATCH 092/129] fix: search result styling --- lib/ReactViews/Search/LocationSearchResults.tsx | 2 +- lib/Styled/List.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 0c7b5af5623..475715f46aa 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -144,7 +144,7 @@ class LocationSearchResults extends React.Component { searchResults={search} isWaitingForSearchToStart={this.props.isWaitingForSearchToStart} /> -
        +
          {results.map((result: SearchResultModel, i: number) => ( ` + padding-left: 0; list-style: none; margin: 0; ${(props) => From 53eaf78abf33b9819e4ec6b004cdf74265c24f9d Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 18:56:37 +0100 Subject: [PATCH 093/129] fix: translation --- .../SearchProviders/WebFeatureServiceSearchProviderMixin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index 082b32176f3..ef7bc29df00 100644 --- a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -106,7 +106,7 @@ function WebFeatureServiceSearchProviderMixin< features = json.featureMember; } else { results.message = { - content: "translate#translate#viewModels.searchNoPlaceNames" + content: "translate#viewModels.searchNoPlaceNames" }; return; } From c08b84dbf68a9c9aea1f8214a210d1011b14d50c Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 19:40:12 +0100 Subject: [PATCH 094/129] change initialization of search state --- .../SearchProviders/BingMapsSearchProvider.ts | 6 ++-- lib/Models/SearchProviders/SearchBarModel.ts | 20 ++++++++--- .../upsertSearchProviderFromJson.ts | 33 ++++++++++--------- lib/ReactViewModels/SearchState.ts | 19 +++++++---- lib/ReactViewModels/ViewState.ts | 6 ++-- lib/Traits/ModelTraitsInterface.ts | 7 ++++ test/Map/StyledHtmlSpec.tsx | 3 +- .../MapNavigation/MapNavigationModelSpec.ts | 3 +- test/Models/TerriaSpec.ts | 9 ++--- .../SelectableDimensionWorkflowSpec.ts | 3 +- test/ReactViewModels/ViewStateSpec.ts | 3 +- test/ReactViews/BottomDock/BottomDockSpec.tsx | 3 +- .../BottomDock/MapDataCountSpec.tsx | 3 +- .../DataCatalog/DataCatalogItemSpec.tsx | 3 +- test/ReactViews/DisclaimerSpec.tsx | 3 +- test/ReactViews/FeatureInfoPanelSpec.tsx | 3 +- test/ReactViews/FeatureInfoSectionSpec.tsx | 3 +- test/ReactViews/Generic/PromptSpec.tsx | 3 +- .../Map/Navigation/Compass/CompassSpec.tsx | 3 +- .../Compass/GyroscopeGuidanceSpec.tsx | 3 +- .../Map/Panels/HelpPanel/HelpPanelSpec.tsx | 3 +- .../Map/Panels/HelpPanel/VideoGuideSpec.tsx | 3 +- .../Map/Panels/LangPanel/LangPanelSpec.tsx | 3 +- .../Panels/SharePanel/BuildShareLinkSpec.ts | 3 +- test/ReactViews/Search/BreadcrumbsSpec.tsx | 3 +- .../Search/SearchBoxAndResultsSpec.tsx | 3 +- test/ReactViews/Search/SearchBoxSpec.tsx | 3 +- test/ReactViews/SidePanel/BrandingSpec.tsx | 3 +- .../TrainerBar/TrainerBarSpec.tsx | 3 +- test/ReactViews/ToolButtonSpec.tsx | 3 +- test/ReactViews/ToolSpec.tsx | 3 +- .../ItemSearchTool/ItemSearchToolSpec.tsx | 3 +- test/ReactViews/Tour/TourPortalSpec.tsx | 3 +- test/ReactViews/WelcomeMessageSpec.tsx | 3 +- .../Workbench/Controls/IdealZoomSpec.tsx | 3 +- .../Controls/ViewingControlsSpec.tsx | 3 +- .../Workflows/WorkflowPanelSpec.tsx | 3 +- test/ViewModels/FeatureInfoPanelSpec.ts | 3 +- .../MapNavigation/MapToolbarSpec.ts | 3 +- test/ViewModels/ViewingControlsMenuSpec.ts | 3 +- 40 files changed, 95 insertions(+), 104 deletions(-) create mode 100644 lib/Traits/ModelTraitsInterface.ts diff --git a/lib/Models/SearchProviders/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts index a95be334e0a..6393a7e2f1d 100644 --- a/lib/Models/SearchProviders/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -1,5 +1,5 @@ import i18next from "i18next"; -import { makeObservable, runInAction } from "mobx"; +import { action, makeObservable, 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"; @@ -111,9 +111,9 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( return; } - const locations = this.sortByPriority(resourceSet.resources); - runInAction(() => { + const locations = this.sortByPriority(resourceSet.resources); + searchResults.results.push(...locations.primaryCountry); searchResults.results.push(...locations.other); }); diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts index ac4463cfa9f..603e3287d85 100644 --- a/lib/Models/SearchProviders/SearchBarModel.ts +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -1,4 +1,10 @@ -import { action, isObservableArray, makeObservable, observable } from "mobx"; +import { + action, + computed, + isObservableArray, + makeObservable, + observable +} from "mobx"; import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; @@ -9,6 +15,7 @@ import { BaseModel } from "../Definition/Model"; import Terria from "../Terria"; import SearchProviderFactory from "./SearchProviderFactory"; import upsertSearchProviderFromJson from "./upsertSearchProviderFromJson"; +import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; export class SearchBarModel extends CreateModel(SearchBarTraits) { private locationSearchProviders = observable.map(); @@ -73,9 +80,14 @@ export class SearchBarModel extends CreateModel(SearchBarTraits) { this.locationSearchProviders.set(model.uniqueId, model); } + @computed get locationSearchProvidersArray() { - return [...this.locationSearchProviders.entries()].map(function (entry) { - return entry[1]; - }); + return [...this.locationSearchProviders.entries()] + .filter((entry) => { + return LocationSearchProviderMixin.isMixedInto(entry[1]); + }) + .map(function (entry) { + return entry[1] as LocationSearchProviderMixin.Instance; + }); } } diff --git a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index 31e5a79c32e..331cd2e43c2 100644 --- a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -9,6 +9,7 @@ import updateModelFromJson from "../Definition/updateModelFromJson"; import Terria from "../Terria"; import StubSearchProvider from "./StubSearchProvider"; import createStubSearchProvider from "./createStubSearchProvider"; +import { runInAction } from "mobx"; export default function upsertSearchProviderFromJson( factory: ModelFactory, @@ -82,21 +83,23 @@ export default function upsertSearchProviderFromJson( function setDefaultTraits(model: BaseModel) { const terria = model.terria; - model.setTrait( - CommonStrata.defaults, - "flightDurationSeconds", - terria.configParameters.searchBarModel.flightDurationSeconds - ); + runInAction(() => { + model.setTrait( + CommonStrata.defaults, + "flightDurationSeconds", + terria.configParameters.searchBarModel.flightDurationSeconds + ); - model.setTrait( - CommonStrata.defaults, - "minCharacters", - terria.configParameters.searchBarModel.minCharacters - ); + model.setTrait( + CommonStrata.defaults, + "minCharacters", + terria.configParameters.searchBarModel.minCharacters + ); - model.setTrait( - CommonStrata.defaults, - "recommendedListLength", - terria.configParameters.searchBarModel.recommendedListLength - ); + model.setTrait( + CommonStrata.defaults, + "recommendedListLength", + terria.configParameters.searchBarModel?.recommendedListLength + ); + }); } diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index df12571996d..9cf3470ac0d 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -17,16 +17,12 @@ import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSe interface SearchStateOptions { terria: Terria; catalogSearchProvider?: CatalogSearchProviderMixin.Instance; - locationSearchProviders?: LocationSearchProviderMixin.Instance[]; } export default class SearchState { @observable catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; - @observable - locationSearchProviders: LocationSearchProviderMixin.Instance[]; - @observable catalogSearchText: string = ""; @observable isWaitingToStartCatalogSearch: boolean = false; @@ -48,13 +44,16 @@ export default class SearchState { private _locationSearchDisposer: IReactionDisposer; private _unifiedSearchDisposer: IReactionDisposer; + private readonly terria: Terria; + constructor(options: SearchStateOptions) { makeObservable(this); + + this.terria = options.terria; + this.catalogSearchProvider = options.catalogSearchProvider || new CatalogSearchProvider("catalog-search-provider", options.terria); - this.locationSearchProviders = options.locationSearchProviders || []; - const self = this; this._catalogSearchDisposer = reaction( @@ -98,6 +97,14 @@ export default class SearchState { this._unifiedSearchDisposer(); } + @computed + get locationSearchProviders(): LocationSearchProviderMixin.Instance[] { + return ( + this.terria.configParameters.searchBarModel + .locationSearchProvidersArray ?? [] + ); + } + @computed get unifiedSearchProviders(): SearchProviderMixin.Instance[] { return filterOutUndefined([ diff --git a/lib/ReactViewModels/ViewState.ts b/lib/ReactViewModels/ViewState.ts index 360fa325e00..803d3206515 100644 --- a/lib/ReactViewModels/ViewState.ts +++ b/lib/ReactViewModels/ViewState.ts @@ -51,7 +51,6 @@ export const WORKBENCH_RESIZE_ANIMATION_DURATION = 500; interface ViewStateOptions { terria: Terria; catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; - locationSearchProviders: LocationSearchProviderMixin.Instance[]; errorHandlingProvider?: any; } @@ -378,9 +377,8 @@ export default class ViewState { makeObservable(this); const terria = options.terria; this.searchState = new SearchState({ - terria: terria, - catalogSearchProvider: options.catalogSearchProvider, - locationSearchProviders: options.locationSearchProviders + terria, + catalogSearchProvider: options.catalogSearchProvider }); this.errorProvider = options.errorHandlingProvider diff --git a/lib/Traits/ModelTraitsInterface.ts b/lib/Traits/ModelTraitsInterface.ts new file mode 100644 index 00000000000..3f71a80c042 --- /dev/null +++ b/lib/Traits/ModelTraitsInterface.ts @@ -0,0 +1,7 @@ +import ModelTraits from "./ModelTraits"; + +export type ModelTraitsInterface = { + [Member in keyof ClassType]: Member extends ModelTraits + ? ModelTraitsInterface> + : ClassType[Member]; +}; diff --git a/test/Map/StyledHtmlSpec.tsx b/test/Map/StyledHtmlSpec.tsx index a223f649e16..861619da303 100644 --- a/test/Map/StyledHtmlSpec.tsx +++ b/test/Map/StyledHtmlSpec.tsx @@ -22,8 +22,7 @@ describe("StyledHtml", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/Models/MapNavigation/MapNavigationModelSpec.ts b/test/Models/MapNavigation/MapNavigationModelSpec.ts index faa55d53ed6..0a7e88ded3f 100644 --- a/test/Models/MapNavigation/MapNavigationModelSpec.ts +++ b/test/Models/MapNavigation/MapNavigationModelSpec.ts @@ -20,8 +20,7 @@ describe("MapNavigationModel", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); item1 = { id: "item1", diff --git a/test/Models/TerriaSpec.ts b/test/Models/TerriaSpec.ts index 7be9acbef87..ed096187703 100644 --- a/test/Models/TerriaSpec.ts +++ b/test/Models/TerriaSpec.ts @@ -584,8 +584,7 @@ describe("Terria", function () { newTerria = new Terria({ appBaseHref: "/", baseUrl: "./" }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); UrlToCatalogMemberMapping.register( @@ -801,8 +800,7 @@ describe("Terria", function () { newTerria = new Terria({ baseUrl: "./" }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); await Promise.all( @@ -913,8 +911,7 @@ describe("Terria", function () { viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); newTerria = new Terria({ baseUrl: "./" }); diff --git a/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts b/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts index 385fd974197..dcac113c67f 100644 --- a/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts +++ b/test/Models/Workflows/SelectableDimensionWorkflowSpec.ts @@ -14,8 +14,7 @@ describe("SelectableDimensionWorkflow", function () { terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViewModels/ViewStateSpec.ts b/test/ReactViewModels/ViewStateSpec.ts index ff04eeb9e6b..b403493c6b8 100644 --- a/test/ReactViewModels/ViewStateSpec.ts +++ b/test/ReactViewModels/ViewStateSpec.ts @@ -14,8 +14,7 @@ describe("ViewState", function () { terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/BottomDock/BottomDockSpec.tsx b/test/ReactViews/BottomDock/BottomDockSpec.tsx index 5b797a6c53a..d89936a04c5 100644 --- a/test/ReactViews/BottomDock/BottomDockSpec.tsx +++ b/test/ReactViews/BottomDock/BottomDockSpec.tsx @@ -15,8 +15,7 @@ describe("BottomDock", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/BottomDock/MapDataCountSpec.tsx b/test/ReactViews/BottomDock/MapDataCountSpec.tsx index ff35577ff86..fa8a2ee5242 100644 --- a/test/ReactViews/BottomDock/MapDataCountSpec.tsx +++ b/test/ReactViews/BottomDock/MapDataCountSpec.tsx @@ -19,8 +19,7 @@ describe("MapDataCount", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx b/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx index 82f551f2505..16ed60e92ff 100644 --- a/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx +++ b/test/ReactViews/DataCatalog/DataCatalogItemSpec.tsx @@ -26,8 +26,7 @@ describe("DataCatalogItem", () => { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); wmsItem = new WebMapServiceCatalogItem("test", terria); diff --git a/test/ReactViews/DisclaimerSpec.tsx b/test/ReactViews/DisclaimerSpec.tsx index f23132f2f1d..49ed398fb0c 100644 --- a/test/ReactViews/DisclaimerSpec.tsx +++ b/test/ReactViews/DisclaimerSpec.tsx @@ -19,8 +19,7 @@ describe("Disclaimer", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/FeatureInfoPanelSpec.tsx b/test/ReactViews/FeatureInfoPanelSpec.tsx index 5d498d89298..b4d632648fc 100644 --- a/test/ReactViews/FeatureInfoPanelSpec.tsx +++ b/test/ReactViews/FeatureInfoPanelSpec.tsx @@ -37,8 +37,7 @@ describe("FeatureInfoPanel", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/FeatureInfoSectionSpec.tsx b/test/ReactViews/FeatureInfoSectionSpec.tsx index 349b78e67b3..f773fb1025d 100644 --- a/test/ReactViews/FeatureInfoSectionSpec.tsx +++ b/test/ReactViews/FeatureInfoSectionSpec.tsx @@ -66,8 +66,7 @@ describe("FeatureInfoSection", function () { viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); const properties = { name: "Kay", diff --git a/test/ReactViews/Generic/PromptSpec.tsx b/test/ReactViews/Generic/PromptSpec.tsx index 5bd08e5153e..d44afbf3cd7 100644 --- a/test/ReactViews/Generic/PromptSpec.tsx +++ b/test/ReactViews/Generic/PromptSpec.tsx @@ -19,8 +19,7 @@ describe("HelpPrompt", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx b/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx index 426a6456deb..5daafb1a59a 100644 --- a/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx +++ b/test/ReactViews/Map/Navigation/Compass/CompassSpec.tsx @@ -21,8 +21,7 @@ describe("Compass", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx b/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx index 40797f13eae..fa870e14cff 100644 --- a/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx +++ b/test/ReactViews/Map/Navigation/Compass/GyroscopeGuidanceSpec.tsx @@ -18,8 +18,7 @@ describe("GyroscopeGuidance", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx b/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx index 9d1aa56fc21..cb81e15d93e 100644 --- a/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx +++ b/test/ReactViews/Map/Panels/HelpPanel/HelpPanelSpec.tsx @@ -23,8 +23,7 @@ describe("HelpPanel", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx b/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx index 30a95b5e0a8..0674dea8b45 100644 --- a/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx +++ b/test/ReactViews/Map/Panels/HelpPanel/VideoGuideSpec.tsx @@ -23,8 +23,7 @@ describe("VideoGuide", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx b/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx index 37c04b4be7b..2003f843a51 100644 --- a/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx +++ b/test/ReactViews/Map/Panels/LangPanel/LangPanelSpec.tsx @@ -20,8 +20,7 @@ describe("LangPanel", function () { viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts b/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts index f1ed6bb81ad..87b5027a8bb 100644 --- a/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts +++ b/test/ReactViews/Map/Panels/SharePanel/BuildShareLinkSpec.ts @@ -36,8 +36,7 @@ beforeEach(function () { viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Search/BreadcrumbsSpec.tsx b/test/ReactViews/Search/BreadcrumbsSpec.tsx index c15c314be8f..08d15e39fe9 100644 --- a/test/ReactViews/Search/BreadcrumbsSpec.tsx +++ b/test/ReactViews/Search/BreadcrumbsSpec.tsx @@ -25,8 +25,7 @@ describe("Breadcrumbs", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); catalogGroup = new CatalogGroup("group-of-geospatial-cats", terria); terria.addModel(catalogGroup); diff --git a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx index 11b936b9f57..a0e0a77d6fb 100644 --- a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx +++ b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx @@ -22,8 +22,7 @@ describe("SearchBoxAndResults", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); runInAction(() => { diff --git a/test/ReactViews/Search/SearchBoxSpec.tsx b/test/ReactViews/Search/SearchBoxSpec.tsx index 7c06ec6c542..6c285caa4b1 100644 --- a/test/ReactViews/Search/SearchBoxSpec.tsx +++ b/test/ReactViews/Search/SearchBoxSpec.tsx @@ -18,8 +18,7 @@ describe("SearchBox", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/SidePanel/BrandingSpec.tsx b/test/ReactViews/SidePanel/BrandingSpec.tsx index fbe4094a574..dab452d71ee 100644 --- a/test/ReactViews/SidePanel/BrandingSpec.tsx +++ b/test/ReactViews/SidePanel/BrandingSpec.tsx @@ -15,8 +15,7 @@ describe("Branding", function () { terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx b/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx index 86cb68eccff..d05c06f4652 100644 --- a/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx +++ b/test/ReactViews/StandardUserInterface/TrainerBar/TrainerBarSpec.tsx @@ -20,8 +20,7 @@ describe("TrainerBar", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/ToolButtonSpec.tsx b/test/ReactViews/ToolButtonSpec.tsx index c0d52244919..35cdbd1820d 100644 --- a/test/ReactViews/ToolButtonSpec.tsx +++ b/test/ReactViews/ToolButtonSpec.tsx @@ -15,8 +15,7 @@ describe("ToolButton", function () { const terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); }); diff --git a/test/ReactViews/ToolSpec.tsx b/test/ReactViews/ToolSpec.tsx index f9a14750a76..97eafd33d5f 100644 --- a/test/ReactViews/ToolSpec.tsx +++ b/test/ReactViews/ToolSpec.tsx @@ -14,8 +14,7 @@ describe("Tool", function () { const terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx b/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx index 7c810247bb1..84286cc1966 100644 --- a/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx +++ b/test/ReactViews/Tools/ItemSearchTool/ItemSearchToolSpec.tsx @@ -58,8 +58,7 @@ describe("ItemSearchTool", function () { const terria: Terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); item = new MockSearchableItem("test", terria); item.setTrait(CommonStrata.user, "search", { diff --git a/test/ReactViews/Tour/TourPortalSpec.tsx b/test/ReactViews/Tour/TourPortalSpec.tsx index 5c16fe79011..67f5a0576cf 100644 --- a/test/ReactViews/Tour/TourPortalSpec.tsx +++ b/test/ReactViews/Tour/TourPortalSpec.tsx @@ -23,8 +23,7 @@ describe("TourPortal", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/WelcomeMessageSpec.tsx b/test/ReactViews/WelcomeMessageSpec.tsx index 6d4a5be70dd..646ceecb493 100644 --- a/test/ReactViews/WelcomeMessageSpec.tsx +++ b/test/ReactViews/WelcomeMessageSpec.tsx @@ -20,8 +20,7 @@ describe("WelcomeMessage", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx b/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx index 34d2e06ed29..bb553cb84f8 100644 --- a/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx +++ b/test/ReactViews/Workbench/Controls/IdealZoomSpec.tsx @@ -30,8 +30,7 @@ describe("Ideal Zoom", function () { const options = { terria: terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }; viewState = new ViewState(options); }); diff --git a/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx b/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx index 3df68ffd857..b271fb5f5ce 100644 --- a/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx +++ b/test/ReactViews/Workbench/Controls/ViewingControlsSpec.tsx @@ -16,8 +16,7 @@ describe("ViewingControls", function () { terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ReactViews/Workflows/WorkflowPanelSpec.tsx b/test/ReactViews/Workflows/WorkflowPanelSpec.tsx index 48bf6dd2242..28f81461995 100644 --- a/test/ReactViews/Workflows/WorkflowPanelSpec.tsx +++ b/test/ReactViews/Workflows/WorkflowPanelSpec.tsx @@ -17,8 +17,7 @@ describe("WorkflowPanel", function () { "./data/regionMapping.json"; viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ViewModels/FeatureInfoPanelSpec.ts b/test/ViewModels/FeatureInfoPanelSpec.ts index b82f45d109e..388979f9c84 100644 --- a/test/ViewModels/FeatureInfoPanelSpec.ts +++ b/test/ViewModels/FeatureInfoPanelSpec.ts @@ -12,8 +12,7 @@ describe("FeatureInfoPanel", function () { terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ViewModels/MapNavigation/MapToolbarSpec.ts b/test/ViewModels/MapNavigation/MapToolbarSpec.ts index 3df9e1a316e..7e2b533e00b 100644 --- a/test/ViewModels/MapNavigation/MapToolbarSpec.ts +++ b/test/ViewModels/MapNavigation/MapToolbarSpec.ts @@ -11,8 +11,7 @@ describe("MapToolbar", function () { terria = new Terria(); viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); }); diff --git a/test/ViewModels/ViewingControlsMenuSpec.ts b/test/ViewModels/ViewingControlsMenuSpec.ts index fa00d863487..4e77287f0e0 100644 --- a/test/ViewModels/ViewingControlsMenuSpec.ts +++ b/test/ViewModels/ViewingControlsMenuSpec.ts @@ -11,8 +11,7 @@ describe("ViewingControlsMenu", function () { const terria = new Terria(); const viewState = new ViewState({ terria, - catalogSearchProvider: undefined, - locationSearchProviders: [] + catalogSearchProvider: undefined }); expect(viewState.globalViewingControlOptions.length).toEqual(0); const generateFunction = (item: CatalogMemberMixin.Instance) => ({ From 7d67dc24dd7830beb459b3a5da72b76d68bbe812 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 19:58:14 +0100 Subject: [PATCH 095/129] test: fix search box result test --- lib/ReactViewModels/SearchState.ts | 6 ++---- test/ReactViews/Search/SearchBoxAndResultsSpec.tsx | 8 ++++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index 9cf3470ac0d..f40998eb9a9 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -99,10 +99,8 @@ export default class SearchState { @computed get locationSearchProviders(): LocationSearchProviderMixin.Instance[] { - return ( - this.terria.configParameters.searchBarModel - .locationSearchProvidersArray ?? [] - ); + return this.terria.configParameters.searchBarModel + .locationSearchProvidersArray; } @computed diff --git a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx index a0e0a77d6fb..b4dae977cd4 100644 --- a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx +++ b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx @@ -9,6 +9,7 @@ import SearchBoxAndResults, { } from "../../../lib/ReactViews/Search/SearchBoxAndResults"; import { ThemeProvider } from "styled-components"; import { terriaTheme } from "../../../lib/ReactViews/StandardUserInterface"; +import CatalogSearchProvider from "../../../lib/Models/SearchProviders/CatalogSearchProvider"; describe("SearchBoxAndResults", function () { let terria: Terria; @@ -26,7 +27,10 @@ describe("SearchBoxAndResults", function () { }); runInAction(() => { - (viewState as any).searchState.catalogSearchProvider = true; + viewState.searchState.catalogSearchProvider = new CatalogSearchProvider( + "catalog", + terria + ); }); }); @@ -88,7 +92,7 @@ describe("SearchBoxAndResults", function () { viewState.searchState.locationSearchText = searchText; viewState.searchState.showLocationSearchResults = true; viewState.searchState.locationSearchResults = []; - (viewState as any).searchState.catalogSearchProvider = false; + (viewState as any).searchState.catalogSearchProvider = undefined; }); act(() => { testRenderer = create( From d75446e4b132096e0f4e50e93e4c751eb82a7c16 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 20:37:02 +0100 Subject: [PATCH 096/129] feat: enforce stronger types for search providers --- lib/Models/SearchProviders/SearchBarModel.ts | 17 +++++++++++++---- .../SearchProviders/SearchModelFactory.ts | 12 ++++++++++++ .../SearchProviders/SearchProviderFactory.ts | 4 ++-- .../upsertSearchProviderFromJson.ts | 7 ++++--- lib/ReactViewModels/SearchState.ts | 13 ++++++++----- .../Search/SearchBoxAndResultsSpec.tsx | 12 +++--------- 6 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 lib/Models/SearchProviders/SearchModelFactory.ts diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts index 603e3287d85..a6364d4d651 100644 --- a/lib/Models/SearchProviders/SearchBarModel.ts +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -16,10 +16,15 @@ import Terria from "../Terria"; import SearchProviderFactory from "./SearchProviderFactory"; import upsertSearchProviderFromJson from "./upsertSearchProviderFromJson"; import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; +import RuntimeError from "terriajs-cesium/Source/Core/RuntimeError"; export class SearchBarModel extends CreateModel(SearchBarTraits) { private locationSearchProviders = observable.map(); + @observable + catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; + constructor(readonly terria: Terria) { super("search-bar-model", terria); @@ -70,10 +75,14 @@ export class SearchBarModel extends CreateModel(SearchBarTraits) { } if (this.locationSearchProviders.has(model.uniqueId)) { - console.log( - new DeveloperError( - "A SearchProvider with the specified ID already exists." - ) + throw new RuntimeError( + "A SearchProvider with the specified ID already exists." + ); + } + + if (!LocationSearchProviderMixin.isMixedInto(model)) { + throw new RuntimeError( + "SearchProvider must be a LocationSearchProvider." ); } diff --git a/lib/Models/SearchProviders/SearchModelFactory.ts b/lib/Models/SearchProviders/SearchModelFactory.ts new file mode 100644 index 00000000000..e5f05182d8c --- /dev/null +++ b/lib/Models/SearchProviders/SearchModelFactory.ts @@ -0,0 +1,12 @@ +import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import { ModelConstructor } from "../Definition/Model"; +import ModelFactory from "../Definition/ModelFactory"; + +export class SearchModelFactory extends ModelFactory { + register( + type: string, + constructor: ModelConstructor + ) { + super.register(type, constructor); + } +} diff --git a/lib/Models/SearchProviders/SearchProviderFactory.ts b/lib/Models/SearchProviders/SearchProviderFactory.ts index 23d93e86ba6..e42059bbb78 100644 --- a/lib/Models/SearchProviders/SearchProviderFactory.ts +++ b/lib/Models/SearchProviders/SearchProviderFactory.ts @@ -1,4 +1,4 @@ -import ModelFactory from "../Definition/ModelFactory"; +import { SearchModelFactory } from "./SearchModelFactory"; -const SearchProviderFactory = new ModelFactory(); +const SearchProviderFactory = new SearchModelFactory(); export default SearchProviderFactory; diff --git a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index 331cd2e43c2..08a0efd42e1 100644 --- a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -1,18 +1,19 @@ import i18next from "i18next"; +import { runInAction } from "mobx"; import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; import { applyTranslationIfExists } from "../../Language/languageHelpers"; +import SearchProviderMixin from "../../ModelMixins/SearchProviders/SearchProviderMixin"; import CommonStrata from "../Definition/CommonStrata"; import { BaseModel } from "../Definition/Model"; -import ModelFactory from "../Definition/ModelFactory"; import updateModelFromJson from "../Definition/updateModelFromJson"; import Terria from "../Terria"; +import { SearchModelFactory } from "./SearchModelFactory"; import StubSearchProvider from "./StubSearchProvider"; import createStubSearchProvider from "./createStubSearchProvider"; -import { runInAction } from "mobx"; export default function upsertSearchProviderFromJson( - factory: ModelFactory, + factory: SearchModelFactory, terria: Terria, stratumName: string, json: any diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index f40998eb9a9..0376607d77d 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -20,9 +20,6 @@ interface SearchStateOptions { } export default class SearchState { - @observable - catalogSearchProvider: CatalogSearchProviderMixin.Instance | undefined; - @observable catalogSearchText: string = ""; @observable isWaitingToStartCatalogSearch: boolean = false; @@ -51,9 +48,10 @@ export default class SearchState { this.terria = options.terria; - this.catalogSearchProvider = + this.terria.configParameters.searchBarModel.catalogSearchProvider = options.catalogSearchProvider || new CatalogSearchProvider("catalog-search-provider", options.terria); + const self = this; this._catalogSearchDisposer = reaction( @@ -98,11 +96,16 @@ export default class SearchState { } @computed - get locationSearchProviders(): LocationSearchProviderMixin.Instance[] { + private get locationSearchProviders(): LocationSearchProviderMixin.Instance[] { return this.terria.configParameters.searchBarModel .locationSearchProvidersArray; } + @computed + get catalogSearchProvider(): CatalogSearchProviderMixin.Instance | undefined { + return this.terria.configParameters.searchBarModel.catalogSearchProvider; + } + @computed get unifiedSearchProviders(): SearchProviderMixin.Instance[] { return filterOutUndefined([ diff --git a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx index b4dae977cd4..0071d4dde8a 100644 --- a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx +++ b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx @@ -23,14 +23,7 @@ describe("SearchBoxAndResults", function () { }); viewState = new ViewState({ terria: terria, - catalogSearchProvider: undefined - }); - - runInAction(() => { - viewState.searchState.catalogSearchProvider = new CatalogSearchProvider( - "catalog", - terria - ); + catalogSearchProvider: new CatalogSearchProvider("catalog", terria) }); }); @@ -92,7 +85,8 @@ describe("SearchBoxAndResults", function () { viewState.searchState.locationSearchText = searchText; viewState.searchState.showLocationSearchResults = true; viewState.searchState.locationSearchResults = []; - (viewState as any).searchState.catalogSearchProvider = undefined; + viewState.terria.configParameters.searchBarModel.catalogSearchProvider = + undefined; }); act(() => { testRenderer = create( From 67b585b82d52694bf7b3c59ae3feab8022755520 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 20:39:55 +0100 Subject: [PATCH 097/129] fix: catalog search provider no results translation --- lib/Models/SearchProviders/CatalogSearchProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index a30ec3646d6..dfde8265a3a 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -193,7 +193,7 @@ export default class CatalogSearchProvider extends CatalogSearchProviderMixin( if (searchResults.results.length === 0) { searchResults.message = { - content: "translate#viewModels.searchErrorOccurred" + content: "translate#viewModels.searchNoCatalogueItem" }; } } catch (e) { From 5543250668d61d380ab976157a934e99d5ab9c1e Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sat, 4 Nov 2023 22:21:32 +0100 Subject: [PATCH 098/129] test: write more tests for bing maps search provider --- lib/Core/loadJsonp.js | 25 ---- lib/Core/loadJsonp.ts | 30 +++++ .../SearchProviders/BingMapsSearchProvider.ts | 7 +- lib/ReactViewModels/SearchState.ts | 11 +- .../BingMapsSearchProviderTraits.ts | 7 + .../BingMapsSearchProviderSpec.ts | 122 +++++++++++++++++- .../LocationSearchProviderSpec.ts | 2 +- 7 files changed, 165 insertions(+), 39 deletions(-) delete mode 100644 lib/Core/loadJsonp.js create mode 100644 lib/Core/loadJsonp.ts diff --git a/lib/Core/loadJsonp.js b/lib/Core/loadJsonp.js deleted file mode 100644 index 4b666d219fd..00000000000 --- a/lib/Core/loadJsonp.js +++ /dev/null @@ -1,25 +0,0 @@ -const Resource = require("terriajs-cesium/Source/Core/Resource").default; - -function loadJsonp(urlOrResource, callbackParameterName) { - var resource = Resource.createIfNeeded(urlOrResource); - return resource.fetchJsonp(callbackParameterName); -} - -Object.defineProperties(loadJsonp, { - loadAndExecuteScript: { - get: function () { - return Resource._Implementations.loadAndExecuteScript; - }, - set: function (value) { - Resource._Implementations.loadAndExecuteScript = value; - } - }, - - defaultLoadAndExecuteScript: { - get: function () { - return Resource._DefaultImplementations.loadAndExecuteScript; - } - } -}); - -module.exports = loadJsonp; diff --git a/lib/Core/loadJsonp.ts b/lib/Core/loadJsonp.ts new file mode 100644 index 00000000000..8a4eece71c5 --- /dev/null +++ b/lib/Core/loadJsonp.ts @@ -0,0 +1,30 @@ +import Resource from "terriajs-cesium/Source/Core/Resource"; + +export function loadJsonp( + urlOrResource: Resource, + callbackParameterName: string +): Promise { + const resource = + typeof urlOrResource === "string" + ? new Resource({ url: urlOrResource }) + : urlOrResource; + + return resource.fetchJsonp(callbackParameterName)!; +} + +Object.defineProperties(loadJsonp, { + loadAndExecuteScript: { + get: function () { + return (Resource as any)._Implementations.loadAndExecuteScript; + }, + set: function (value) { + (Resource as any)._Implementations.loadAndExecuteScript = value; + } + }, + + defaultLoadAndExecuteScript: { + get: function () { + return (Resource as any)._DefaultImplementations.loadAndExecuteScript; + } + } +}); diff --git a/lib/Models/SearchProviders/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts index 6393a7e2f1d..8e1c6d16a16 100644 --- a/lib/Models/SearchProviders/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -7,7 +7,7 @@ import { Category, SearchAction } from "../../Core/AnalyticEvents/analyticEvents"; -import loadJsonp from "../../Core/loadJsonp"; +import { loadJsonp } from "../../Core/loadJsonp"; import { applyTranslationIfExists } from "../../Language/languageHelpers"; import LocationSearchProviderMixin, { getMapCenter @@ -75,7 +75,8 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( queryParameters: { culture: this.culture, query: searchText, - key: this.key + key: this.key, + maxResults: this.maxResults } }); @@ -103,7 +104,7 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( return; } - var resourceSet = result.resourceSets[0]; + const resourceSet = result.resourceSets[0]; if (resourceSet.resources.length === 0) { searchResults.message = { content: "translate#viewModels.searchNoLocations" diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index 0376607d77d..3cdb884dc0c 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -4,7 +4,8 @@ import { IReactionDisposer, observable, reaction, - makeObservable + makeObservable, + runInAction } from "mobx"; import filterOutUndefined from "../Core/filterOutUndefined"; import LocationSearchProviderMixin from "../ModelMixins/SearchProviders/LocationSearchProviderMixin"; @@ -48,9 +49,11 @@ export default class SearchState { this.terria = options.terria; - this.terria.configParameters.searchBarModel.catalogSearchProvider = - options.catalogSearchProvider || - new CatalogSearchProvider("catalog-search-provider", options.terria); + runInAction(() => { + this.terria.configParameters.searchBarModel.catalogSearchProvider = + options.catalogSearchProvider || + new CatalogSearchProvider("catalog-search-provider", options.terria); + }); const self = this; diff --git a/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts b/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts index d233d921f0d..4ec8f1a0f41 100644 --- a/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/BingMapsSearchProviderTraits.ts @@ -32,4 +32,11 @@ export default class BingMapsSearchProviderTraits extends mixTraits( For a list of supported cultures, see [Supported Culture Codes](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes)` }) culture: string = "en-au"; + + @primitiveTrait({ + type: "number", + name: "Max results", + description: "The maximum number of results to return." + }) + maxResults: number = 5; } diff --git a/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts b/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts index 4c932a4167f..4035b4bc26b 100644 --- a/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts +++ b/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts @@ -1,10 +1,16 @@ +import Resource from "terriajs-cesium/Source/Core/Resource"; import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProviders/LocationSearchProviderMixin"; import BingMapsSearchProvider from "../../../lib/Models/SearchProviders/BingMapsSearchProvider"; import Terria from "../../../lib/Models/Terria"; +import { exp } from "protomaps"; +import { runInAction } from "mobx"; +import { CommonStrata } from "terriajs-plugin-api"; +import * as loadJsonp from "../../../lib/Core/loadJsonp"; -describe("BingMapsSearchProviderTraits", function () { +describe("BingMapsSearchProvider", function () { let terria: Terria; let bingMapsSearchProvider: BingMapsSearchProvider; + beforeEach(async function () { terria = new Terria({ baseUrl: "./" @@ -12,17 +18,121 @@ describe("BingMapsSearchProviderTraits", function () { bingMapsSearchProvider = new BingMapsSearchProvider("test", terria); }); - it(" - properly mixed", function () { + it(" - properly mixed", () => { expect( LocationSearchProviderMixin.isMixedInto(bingMapsSearchProvider) ).toBeTruthy(); }); - describe(" - default values", function () { - it(" - propperly defines default url", function () { - expect(bingMapsSearchProvider.url).toEqual( - "https://dev.virtualearth.net/" + it(" - propperly defines default url", () => { + expect(bingMapsSearchProvider.url).toEqual("https://dev.virtualearth.net/"); + }); + + it(" - propperly sets query parameters", () => { + runInAction(() => { + bingMapsSearchProvider.setTrait( + CommonStrata.definition, + "key", + "test-key" + ); + bingMapsSearchProvider.setTrait( + CommonStrata.definition, + "minCharacters", + 3 + ); + bingMapsSearchProvider.setTrait( + CommonStrata.definition, + "mapCenter", + false ); }); + const test = spyOn(loadJsonp, "loadJsonp").and.returnValue( + Promise.resolve({ + resourceSets: [] + }) + ); + + const searchResult = bingMapsSearchProvider.search("test"); + + expect(test).toHaveBeenCalledWith( + new Resource({ + url: "https://dev.virtualearth.net/REST/v1/Locations", + queryParameters: { + culture: "en-au", + query: "test", + key: "test-key", + maxResults: 5 + } + }), + "jsonp" + ); + }); + + it(" - propperly sort the search results", async () => { + runInAction(() => { + bingMapsSearchProvider.setTrait( + CommonStrata.definition, + "key", + "test-key" + ); + bingMapsSearchProvider.setTrait( + CommonStrata.definition, + "minCharacters", + 3 + ); + bingMapsSearchProvider.setTrait( + CommonStrata.definition, + "mapCenter", + false + ); + }); + const test = spyOn(loadJsonp, "loadJsonp").and.returnValue( + Promise.resolve({ + resourceSets: [ + { + resources: [ + { + name: "test result 1", + address: { + countryRegion: "Italy" + }, + point: { + type: "Point", + coordinates: [46.06452179, 12.08810234] + }, + bbox: [ + 46.06022262573242, 12.072776794433594, 46.06576919555664, + 12.101446151733398 + ] + }, + { + name: "test result 2", + address: { + countryRegion: "Australia" + }, + point: { + type: "Point", + coordinates: [46.06452179, 12.08810234] + }, + bbox: [ + 45.96084213256836, 11.978724479675293, 46.09341049194336, + 12.2274169921875 + ] + }, + { + name: undefined + } + ] + } + ] + }) + ); + + const searchResult = await bingMapsSearchProvider.search("test"); + + expect(searchResult.results.length).toEqual(2); + expect(searchResult.message).toBeUndefined(); + expect(searchResult.results[0].name).toEqual("test result 2"); + expect(searchResult.results[1].name).toEqual("test result 1, Italy"); }); }); diff --git a/test/Models/SearchProviders/LocationSearchProviderSpec.ts b/test/Models/SearchProviders/LocationSearchProviderSpec.ts index e9893902038..0f744648c79 100644 --- a/test/Models/SearchProviders/LocationSearchProviderSpec.ts +++ b/test/Models/SearchProviders/LocationSearchProviderSpec.ts @@ -2,7 +2,7 @@ import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProvider import BingMapsSearchProvider from "../../../lib/Models/SearchProviders/BingMapsSearchProvider"; import Terria from "../../../lib/Models/Terria"; -describe("LocationSearchProviderTraits", function () { +describe("LocationSearchProvider", function () { let terria: Terria; let bingMapsSearchProvider: BingMapsSearchProvider; beforeEach(async function () { From d58943db196bde3734a09859c6ddf8d6b5629d92 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 10 Nov 2023 16:17:53 +1100 Subject: [PATCH 099/129] only use GetTimeseries if there is more than one timeslice --- lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts index 5c9dddf6e50..4426c8d540b 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts @@ -737,9 +737,12 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( @computed get supportsGetTimeseries() { + // Don't use GetTimeseries if there is only one timeslice + if ((this.catalogItem.discreteTimes?.length ?? 0) <= 1) return false; + const capabilities = this.capabilities?.json?.Capability; - return ( + return !!( isDefined(capabilities?.Request?.GetTimeseries) || (Array.isArray(capabilities?.ExtendedCapabilities?.ExtendedRequest) && capabilities.ExtendedCapabilities.ExtendedRequest.find( From d74313d11dcfa7a0f56ad33cf8fc914db5aefe84 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 10 Nov 2023 16:21:07 +1100 Subject: [PATCH 100/129] add test --- .../Ows/WebMapServiceCatalogItemSpec.ts | 26 ++ wwwroot/test/WMS/colorscalerange.xml | 365 ++++++++++-------- 2 files changed, 225 insertions(+), 166 deletions(-) diff --git a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts index fa67a33dfd5..d1ce0507e0f 100644 --- a/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts +++ b/test/Models/Catalog/Ows/WebMapServiceCatalogItemSpec.ts @@ -391,6 +391,32 @@ describe("WebMapServiceCatalogItem", function () { } }); + it("doesn't supportGettimeseries if only a single timeslice", async function () { + let wms: WebMapServiceCatalogItem; + const terria = new Terria(); + wms = new WebMapServiceCatalogItem("test", terria); + runInAction(() => { + wms.setTrait("definition", "url", "test/WMS/colorscalerange.xml"); + wms.setTrait("definition", "layers", "mylayer-singletime"); + }); + let mapItems: ImageryParts[] = []; + const cleanup = autorun(() => { + mapItems = wms.mapItems.slice(); + }); + try { + await wms.loadMetadata(); + console.log(wms); + expect(mapItems.length).toBe(1); + expect(mapItems[0].alpha).toBeCloseTo(0.8); + expect( + mapItems[0].imageryProvider instanceof WebMapServiceImageryProvider + ).toBeTruthy(); + expect(wms.supportsGetTimeseries).toBeFalsy(); + } finally { + cleanup(); + } + }); + it("constructs correct ImageryProvider when layers trait provided Title", async function () { let wms: WebMapServiceCatalogItem; const terria = new Terria(); diff --git a/wwwroot/test/WMS/colorscalerange.xml b/wwwroot/test/WMS/colorscalerange.xml index f39bd62a9de..98a78150961 100644 --- a/wwwroot/test/WMS/colorscalerange.xml +++ b/wwwroot/test/WMS/colorscalerange.xml @@ -1,141 +1,145 @@ - - - WMS - Some Server - - - - - - - - - - - - - - none - none - 1 - 1024 - 1024 - - - - - text/xml - - - - - - - - - - image/png - image/png;mode=32bit - image/gif - image/jpeg - application/vnd.google-earth.kmz - - - - - - - - - - text/plain - text/xml - - - - - - - - - - - ncWMS2 - - GetMap - - COLORSCALERANGE - Of the form min,max this is the scale range used for plotting the data. - - - NUMCOLORBANDS - The number of discrete colours to plot the data. Must be between 2 and 250 - - - ABOVEMAXCOLOR - The colour to plot values which are above the maximum end of the scale range. Colours are as defined above, with the addition of "extend", which will use the maximum value of the palette. - - - BELOWMINCOLOR - The colour to plot values which are below the minimum end of the scale range. Colours are as defined above, with the addition of "extend", which will use the minimum value of the palette. - - - LOGSCALE - "true" or "false" - whether to plot data with a logarithmic scale - - - TARGETTIME - For in-situ data, all points which fall within the time range (specified in the TIME parameter) will be plotted. In the case that an in-situ point has multiple time readings within that range, the colour used to plot them will depend on the time value which is closest to this given value - - - TARGETELEVATION - For in-situ data, all points which fall within the elevation range (specified in the ELEVATION parameter) will be plotted. In the case that an in-situ point has multiple elevation readings within that range, the colour used to plot them will depend on the elevation value which is closest to this given value - - - OPACITY - The percentage opacity of the final output image - - - ANIMATION - "true" or "false" - whether to generate an animation. This also needs the TIME to be of the formstarttime/endtime, and currently is only implemented for features with a discrete time axis. - - - - GetTimeseries - This produces either a timeseries graph or, if downloading is enabled, a CSV file containing the data. The URL parameters are identical to those of a GetFeatureInfo request. The TIME parameter should specify a range of times in the form starttime/endtime, and the supported formats are: image/png,image/jpg,image/jpeg,text/csv - - - GetVerticalProfile - This produces either a vertical profile graph or, if downloading is enabled, a CSV file containing the data. The URL parameters are identical to those of a GetFeatureInfo request. The ELEVATION parameter should specify a range of elevations in the form startelevation/endelevation, and the supported formats are: image/png,image/jpg,image/jpeg,text/csv - - - GetTransect - This produces a graph of data values along an arbitrary path. Additionally if there is vertical information present in the dataset, it will produce a vertical section along the same path. It accepts the same URL parameters as a GetMap requestion, with the additional mandatory parameter LINESTRING - - LINESTRING - The points which define the path of the transect to plot. Of the form x1 y1,x2 y2,x3 y3... - - - - GetMetadata - Fetches small pieces of metadata. Many of these are also present in this capabilities document, but GetMetadata provides a more convenient method of accessing such data. GetMetadata always returns data in the JSON format - - ITEM - This specifies the metadata to return. This can take the values: + + + WMS + Some Server + + + + + + + + + + + + + + none + none + 1 + 1024 + 1024 + + + + + text/xml + + + + + + + + + + image/png + image/png;mode=32bit + image/gif + image/jpeg + application/vnd.google-earth.kmz + + + + + + + + + + text/plain + text/xml + + + + + + + + + + + ncWMS2 + + GetMap + + COLORSCALERANGE + Of the form min,max this is the scale range used for plotting the data. + + + NUMCOLORBANDS + The number of discrete colours to plot the data. Must be between 2 and 250 + + + ABOVEMAXCOLOR + The colour to plot values which are above the maximum end of the scale range. Colours are as defined above, with the addition of "extend", which will use the maximum value of the palette. + + + BELOWMINCOLOR + The colour to plot values which are below the minimum end of the scale range. Colours are as defined above, with the addition of "extend", which will use the minimum value of the palette. + + + LOGSCALE + "true" or "false" - whether to plot data with a logarithmic scale + + + TARGETTIME + For in-situ data, all points which fall within the time range (specified in the TIME parameter) will be plotted. In the case that an in-situ point has multiple time readings within that range, the colour used to plot them will depend on the time value which is closest to this given value + + + TARGETELEVATION + For in-situ data, all points which fall within the elevation range (specified in the ELEVATION parameter) will be plotted. In the case that an in-situ point has multiple elevation readings within that range, the colour used to plot them will depend on the elevation value which is closest to this given value + + + OPACITY + The percentage opacity of the final output image + + + ANIMATION + "true" or "false" - whether to generate an animation. This also needs the TIME to be of the formstarttime/endtime, and currently is only implemented for features with a discrete time axis. + + + + GetTimeseries + This produces either a timeseries graph or, if downloading is enabled, a CSV file containing the data. The URL parameters are identical to those of a GetFeatureInfo request. The TIME parameter should specify a range of times in the form starttime/endtime, and the supported formats are: image/png,image/jpg,image/jpeg,text/csv + + + GetVerticalProfile + This produces either a vertical profile graph or, if downloading is enabled, a CSV file containing the data. The URL parameters are identical to those of a GetFeatureInfo request. The ELEVATION parameter should specify a range of elevations in the form startelevation/endelevation, and the supported formats are: image/png,image/jpg,image/jpeg,text/csv + + + GetTransect + This produces a graph of data values along an arbitrary path. Additionally if there is vertical information present in the dataset, it will produce a vertical section along the same path. It accepts the same URL parameters as a GetMap requestion, with the additional mandatory parameter LINESTRING + + LINESTRING + The points which define the path of the transect to plot. Of the form x1 y1,x2 y2,x3 y3... + + + + GetMetadata + Fetches small pieces of metadata. Many of these are also present in this capabilities document, but GetMetadata provides a more convenient method of accessing such data. GetMetadata always returns data in the JSON format + + ITEM + This specifies the metadata to return. This can take the values: menu: Returns a tree representation of the available WMS layers, with IDs. Takes the optional parameter DATASET to return the same tree for a single dataset layerDetails: Returns a set of details needed to plot a given layer. This includes such data as units, layer bounding box, configured scale range, etc. Takes the parameters LAYERNAME and TIME. The TIME parameter is optional, and if it is specified then the nearest available time is returned as part of the layer's details. minmax: Calculates the range of values in the given area. Takes the same parameters as a GetMap request. timesteps: Returns the available times for a given day. Takes the parameters LAYERNAME and DAY (yyyy-mm-dd) animationTimesteps: Returns a list of time strings at different temporal resolutions for a given time range. This is used to present to the user different frequencies for the generation of an animation. Takes the parameters LAYERNAME, START, and END - - - - GetLegendGraphic - The GetLegendGraphic request generates an image which can be used as a legend. There are two main options: Generating just a colourbar, and generating a full legend. - - COLORBARONLY - "true" or "false". Whether to generate a full legend or just the colour bar. If it's "true", the following URL parameters are required: + + + + GetLegendGraphic + The GetLegendGraphic request generates an image which can be used as a legend. There are two main options: Generating just a colourbar, and generating a full legend. + + COLORBARONLY + "true" or "false". Whether to generate a full legend or just the colour bar. If it's "true", the following URL parameters are required: PALETTE: The name of the palette to use. If missing, set to "default" NUMCOLORBANDS: The number of colour bands to use. If missing, set to 250 VERTICAL: Whether to very colours vertically. If missing, defaults to true @@ -143,39 +147,68 @@ HEIGHT: The height of the image to generate. If missing, defaults to 200 For a full legend, the additional parameters LAYERS and either STYLES, SLD, or SLD_BODY must be supplied. This is because a single WMS layer may depend on an arbitrary number of sub-layers, depending on the style it is plotted in. In addition to these parameters, the optional parameters controlling the style may be supplied (these are the same as documented in the GetMap request). Note that for full legends, the supplied width and height are NOT the final height of the image, but rather the width and height of each individual coloured plot area (i.e. the 1d/2d colourbar) - - - - - XML - - - Some Server - EPSG:4326 - - Some Layer - - mylayer - A queryable layer - Abtract goes here - - -180.0 - 180.0 - -90.0 - 90.0 - - - - - + + + + + XML + + + Some Server + EPSG:4326 + + Some Layer + + mylayer + A queryable layer + Abtract goes here + + -180.0 + 180.0 + -90.0 + 90.0 + + + + 2002-01-01T00:00:00.000Z,2003-01-01T00:00:00.000Z,2004-01-01T00:00:00.000Z,2005-01-01T00:00:00.000Z,2006-01-01T00:00:00.000Z,2007-01-01T00:00:00.000Z,2008-01-01T00:00:00.000Z,2009-01-01T00:00:00.000Z,2010-01-01T00:00:00.000Z,2011-01-01T00:00:00.000Z,2012-01-01T00:00:00.000Z,2013-01-01T00:00:00.000Z,2014-01-01T00:00:00.000Z + - + + + Some Layer with a single timestep + + mylayer-singletime + A queryable layer + Abtract goes here + + -180.0 + 180.0 + -90.0 + 90.0 + + + + + 2002-01-01T00:00:00.000Z + + + + + From 244e43bc3276b0c8e35454f1134b84146c4d0585 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Mon, 13 Nov 2023 13:48:48 +1100 Subject: [PATCH 101/129] Update lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx Co-authored-by: Lawrence Owen --- lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx index bfec39c7c72..cec82e8d5b6 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx +++ b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx @@ -123,7 +123,7 @@ class MyDataTab extends React.Component { } render() { - const showTwoColumn = !!(this.hasUserAddedData() & !this.state.activeTab); + const showTwoColumn = !!(this.hasUserAddedData() && !this.state.activeTab); const { t, className } = this.props; return ( Date: Mon, 13 Nov 2023 17:03:04 +1100 Subject: [PATCH 102/129] `CatalogGroup` will now not show members until loaded --- CHANGES.md | 1 + lib/ReactViews/DataCatalog/CatalogGroup.jsx | 2 +- .../DataCatalog/CatalogGroupSpec.tsx | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index fe3c529ec23..2c30d8be3c5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,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`) +- `CatalogGroup` will now not show members until loaded - [The next improvement] #### 8.3.7 - 2023-10-26 diff --git a/lib/ReactViews/DataCatalog/CatalogGroup.jsx b/lib/ReactViews/DataCatalog/CatalogGroup.jsx index ceddfb2a093..196e6bda311 100644 --- a/lib/ReactViews/DataCatalog/CatalogGroup.jsx +++ b/lib/ReactViews/DataCatalog/CatalogGroup.jsx @@ -138,7 +138,7 @@ function CatalogGroup(props) { )} - {props.children} + {!props.loading ? props.children : null}
        )} diff --git a/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx b/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx index 731d8363357..434e51ee35d 100644 --- a/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx +++ b/test/ReactViews/DataCatalog/CatalogGroupSpec.tsx @@ -27,6 +27,32 @@ describe("CatalogGroup", () => { 0 ); }); + it("Doesn't show children when loading", () => { + const TestChild = () => some child; + act(() => { + testRenderer = create( + + {}} loading={true}> + + + + ); + }); + expect(testRenderer.root.findAllByType(TestChild).length).toEqual(0); + }); + it("Shows children when not loading", () => { + const TestChild = () => some child; + act(() => { + testRenderer = create( + + {}} loading={false}> + + + + ); + }); + expect(testRenderer.root.findAllByType(TestChild).length).toEqual(1); + }); }); describe("Empty", () => { it("Shows empty message", () => { From feabaf2b42470e278d96883ba356873456baa9cc Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Tue, 17 Oct 2023 10:40:52 +1000 Subject: [PATCH 103/129] Create CesiumIonSearchProvider --- lib/Models/SearchProviders/CesiumIonSearchProvider.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/Models/SearchProviders/CesiumIonSearchProvider.ts diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts new file mode 100644 index 00000000000..e69de29bb2d From b73034f610b5178422a60d2b0f89a1fe8307a858 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 19 Oct 2023 17:15:41 +1000 Subject: [PATCH 104/129] Create CesiumIonSearchProvider - add type generic to loadJson --- lib/Core/loadJson.ts | 6 +- .../CesiumIonSearchProvider.ts | 98 +++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/lib/Core/loadJson.ts b/lib/Core/loadJson.ts index 7067f9fc5b9..61be0e52a59 100644 --- a/lib/Core/loadJson.ts +++ b/lib/Core/loadJson.ts @@ -1,14 +1,14 @@ import Resource from "terriajs-cesium/Source/Core/Resource"; -export default function loadJson( +export default function loadJson( urlOrResource: any, headers?: any, body?: any, asForm: boolean = false -): Promise { +): Promise { let responseType: XMLHttpRequestResponseType = "json"; - let jsonPromise: Promise; + let jsonPromise: Promise; let params: any = { url: urlOrResource, headers: headers diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index e69de29bb2d..2c77f78001f 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -0,0 +1,98 @@ +import Cesium from "../Cesium"; +import SearchProvider from "./SearchProvider"; +import { observable, makeObservable } from "mobx"; +import Terria from "../Terria"; +import { defaultValue } from "terriajs-cesium"; +import Scene from "terriajs-cesium/Source/Scene/Scene"; +import SearchProviderResults from "./SearchProviderResults"; +import SearchResult from "./SearchResult"; +import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; +import Cartesian3 from "terriajs-cesium/Source/Core/Cartesian3"; +import Cartographic from "terriajs-cesium/Source/Core/Cartographic"; +import GeocoderService from "terriajs-cesium/Source/Core/GeocoderService"; +import loadJson from "../../Core/loadJson"; +import bbox from "@turf/bbox"; +interface CesiumIonSearchProviderOptions { + terria: Terria; + url?: string; + key?: string; + flightDurationSeconds?: number; + primaryCountry?: string; + culture?: string; +} + +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.url = defaultValue( + options.url, + "https://api.cesium.com/v1/geocode/search" + ); + this.key = options.key; + this.flightDurationSeconds = defaultValue( + options.flightDurationSeconds, + 1.5 + ); + } + + protected async doSearch( + searchText: string, + results: SearchProviderResults + ): Promise { + if (searchText === undefined || /^\s*$/.test(searchText)) { + return Promise.resolve(); + } + + const response = await loadJson( + `${this.url}?text=${searchText}&access_token=${this.key}` + ); + + if (!response.features) { + return; + } + + results.results.push( + ...response.features.map((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); + }; +} From 58792614d0065cc69e8b8777e1058425b02b8b42 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Wed, 1 Nov 2023 15:57:12 +1000 Subject: [PATCH 105/129] Build using cesium-ion-geocoder terriaMap branch --- buildprocess/ci-deploy.sh | 2 +- lib/Models/SearchProviders/CesiumIonSearchProvider.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/buildprocess/ci-deploy.sh b/buildprocess/ci-deploy.sh index 06ff60bc1b0..ab453002d23 100644 --- a/buildprocess/ci-deploy.sh +++ b/buildprocess/ci-deploy.sh @@ -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 diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index 2c77f78001f..c0dbc4b5ce3 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -7,11 +7,7 @@ import Scene from "terriajs-cesium/Source/Scene/Scene"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; -import Cartesian3 from "terriajs-cesium/Source/Core/Cartesian3"; -import Cartographic from "terriajs-cesium/Source/Core/Cartographic"; -import GeocoderService from "terriajs-cesium/Source/Core/GeocoderService"; import loadJson from "../../Core/loadJson"; -import bbox from "@turf/bbox"; interface CesiumIonSearchProviderOptions { terria: Terria; url?: string; From c3057446fb68699b0a46e7b58dd75197e8b357b3 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 2 Nov 2023 11:14:28 +1000 Subject: [PATCH 106/129] Fix terriajs-cesium import --- lib/Models/SearchProviders/CesiumIonSearchProvider.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index c0dbc4b5ce3..032b6348a7f 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -1,9 +1,7 @@ -import Cesium from "../Cesium"; import SearchProvider from "./SearchProvider"; import { observable, makeObservable } from "mobx"; import Terria from "../Terria"; -import { defaultValue } from "terriajs-cesium"; -import Scene from "terriajs-cesium/Source/Scene/Scene"; +import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; From 44c1592fa76581b724b14adc4a427eea8cf3affa Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Tue, 14 Nov 2023 12:39:18 +1000 Subject: [PATCH 107/129] - Handle errors - log to analytics - handle empty result - add unit test --- CHANGES.md | 2 +- lib/Core/AnalyticEvents/analyticEvents.ts | 3 +- .../CesiumIonSearchProvider.ts | 71 +++++++++++++------ .../CesiumIonSearchProviderSpec.ts | 59 +++++++++++++++ 4 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts diff --git a/CHANGES.md b/CHANGES.md index f350fab13ea..196b54828b5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 diff --git a/lib/Core/AnalyticEvents/analyticEvents.ts b/lib/Core/AnalyticEvents/analyticEvents.ts index daecf95c482..39b4cbc4abe 100644 --- a/lib/Core/AnalyticEvents/analyticEvents.ts +++ b/lib/Core/AnalyticEvents/analyticEvents.ts @@ -16,7 +16,8 @@ export enum SearchAction { bing = "Bing", catalog = "Catalog", gazetteer = "Gazetteer", - nominatim = "nominatim" + nominatim = "nominatim", + cesium = "Cesium" } export enum LaunchAction { diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index 032b6348a7f..48bfd1f3986 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -1,18 +1,22 @@ import SearchProvider from "./SearchProvider"; import { observable, makeObservable } from "mobx"; -import Terria from "../Terria"; +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 Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import loadJson from "../../Core/loadJson"; +import { + Category, + SearchAction +} from "../../Core/AnalyticEvents/analyticEvents"; + interface CesiumIonSearchProviderOptions { terria: Terria; url?: string; - key?: string; + key: string; flightDurationSeconds?: number; - primaryCountry?: string; - culture?: string; } interface CesiumIonGeocodeResultFeature { @@ -36,6 +40,7 @@ export default class CesiumIonSearchProvider extends SearchProvider { makeObservable(this); this.terria = options.terria; + this.name = i18next.t("viewModels.searchLocations"); this.url = defaultValue( options.url, "https://api.cesium.com/v1/geocode/search" @@ -45,39 +50,61 @@ export default class CesiumIonSearchProvider extends SearchProvider { 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, - results: SearchProviderResults + searchResults: SearchProviderResults ): Promise { if (searchText === undefined || /^\s*$/.test(searchText)) { return Promise.resolve(); } - const response = await loadJson( - `${this.url}?text=${searchText}&access_token=${this.key}` + this.terria.analytics?.logEvent( + Category.search, + SearchAction.cesium, + searchText ); + let response; + try { + response = await loadJson( + `${this.url}?text=${searchText}&access_token=${this.key}` + ); + } catch (e) { + searchResults.message = i18next.t("viewModels.searchErrorOccurred"); + return; + } if (!response.features) { + searchResults.message = i18next.t("viewModels.searchNoLocations"); return; } - results.results.push( - ...response.features.map((feature) => { - const [w, s, e, n] = feature.bbox; - const rectangle = Rectangle.fromDegrees(w, s, e, n); + if (response.features.length === 0) { + searchResults.message = i18next.t("viewModels.searchNoLocations"); + } - return new SearchResult({ - name: feature.properties.label, - clickAction: createZoomToFunction(this, rectangle), - location: { - latitude: (s + n) / 2, - longitude: (e + w) / 2 - } - }); - }) - ); + searchResults.results = response.features.map((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 + } + }); + }); } } diff --git a/test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts b/test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts new file mode 100644 index 00000000000..f000088d6f9 --- /dev/null +++ b/test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts @@ -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"); + }); +}); From a6748e20d1b7a17b23be6b58e29ad7e5cbb1d710 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Tue, 14 Nov 2023 16:14:44 +1000 Subject: [PATCH 108/129] Wrap state changes in runInAction --- .../CesiumIonSearchProvider.ts | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index 48bfd1f3986..e0e958c6f68 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -1,5 +1,5 @@ import SearchProvider from "./SearchProvider"; -import { observable, makeObservable } from "mobx"; +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"; @@ -73,36 +73,41 @@ export default class CesiumIonSearchProvider extends SearchProvider { SearchAction.cesium, searchText ); - let response; + + let response: CesiumIonGeocodeResult; try { response = await loadJson( `${this.url}?text=${searchText}&access_token=${this.key}` ); } catch (e) { - searchResults.message = i18next.t("viewModels.searchErrorOccurred"); + runInAction(() => { + searchResults.message = i18next.t("viewModels.searchErrorOccurred"); + }); return; } - if (!response.features) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); - return; - } + runInAction(() => { + if (!response.features) { + searchResults.message = i18next.t("viewModels.searchNoLocations"); + return; + } - if (response.features.length === 0) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); - } + if (response.features.length === 0) { + searchResults.message = i18next.t("viewModels.searchNoLocations"); + } + + searchResults.results = response.features.map((feature) => { + const [w, s, e, n] = feature.bbox; + const rectangle = Rectangle.fromDegrees(w, s, e, n); - searchResults.results = response.features.map((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 - } + return new SearchResult({ + name: feature.properties.label, + clickAction: createZoomToFunction(this, rectangle), + location: { + latitude: (s + n) / 2, + longitude: (e + w) / 2 + } + }); }); }); } From 435defcc4ade0a91b65dd66a5b4d981bcd297cbf Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Wed, 15 Nov 2023 12:33:47 +1100 Subject: [PATCH 109/129] update changes --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 8674420c327..105291a3fbe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ - Fix WMS `GetMap`/`GetFeatureInfo` requests not having `styles` parameter (will use empty string instead of `undefined`) - Add CesiumIon geocoder - `CatalogGroup` will now not show members until loaded +- Add `GetTimeseries` support to `WebMapServiceCatalogItem`. This adds a new `supportsGetTimeseries` trait, which when true will replace `GetFeatureInfo` with `GetTimeseries` requests. It will also change `info_format` to `text/csv`, and show a chart in the feature info panel. Servers which advertise `GetTimeseries` capability will have this trait set to true by default. `GetTimeseries` requests will have `time = ""`. - [The next improvement] #### 8.3.7 - 2023-10-26 @@ -35,7 +36,6 @@ #### 8.3.6 - 2023-10-03 -- Add `GetTimeseries` support to `WebMapServiceCatalogItem`. This adds a new `supportsGetTimeseries` trait, which when true will replace `GetFeatureInfo` with `GetTimeseries` requests. It will also change `info_format` to `text/csv`, and show a chart in the feature info panel. Servers which advertise `GetTimeseries` capability will have this trait set to true by default. `GetTimeseries` requests will have `time = ""`. - Fixed a bug where incorrect "Remove all" icon is shown when the trait `displayGroup` of some group types (e.g.`wms-group`) is set to `true` but the members have not been populated yet. - Fix regression in `excludeMembers`, `id` and `name` should be lower-case for comparing. From 78e0384a3654fe3a85ce4075c776117fc7f1c261 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Wed, 15 Nov 2023 14:42:15 +1100 Subject: [PATCH 110/129] Release 8.3.8 --- CHANGES.md | 6 +++++- package.json | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 105291a3fbe..8836a065777 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ # Change Log -#### next release (8.3.8) +#### next release (8.3.9) + +- [The next improvement] + +#### 8.3.8 - 2023-11-15 - Fix maximum call stack size exceeded on Math.min/max when creating Charts - Fix boolean flag in `MyDataTab` displaying number diff --git a/package.json b/package.json index b20b8417ffa..89b2ff726fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "terriajs", - "version": "8.3.7", + "version": "8.3.8", "description": "Geospatial data visualization platform.", "license": "Apache-2.0", "engines": { @@ -243,4 +243,4 @@ "build-for-node": "tsc -b tsconfig-node.json", "prepare": "yarn build-for-node && husky install" } -} +} \ No newline at end of file From bf837cf98f92b33882d79596b97846a1a9c07a4f Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Wed, 15 Nov 2023 14:43:22 +1100 Subject: [PATCH 111/129] fix line ending --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89b2ff726fc..4890917d05e 100644 --- a/package.json +++ b/package.json @@ -243,4 +243,4 @@ "build-for-node": "tsc -b tsconfig-node.json", "prepare": "yarn build-for-node && husky install" } -} \ No newline at end of file +} From bdf80179c8265bd008344fe7eb7a10db5f4336d3 Mon Sep 17 00:00:00 2001 From: Yusuke Kiuchi Date: Tue, 26 Sep 2023 01:39:26 +0000 Subject: [PATCH 112/129] Translated using Weblate (Japanese) for TerriaJSNext Currently translated at 100.0% (1217 of 1217 strings) Translation: TerriaJS/TerriaJSNext Translate-URL: https://hosted.weblate.org/projects/terriajs/terriajsnext/ja/ --- wwwroot/languages/ja/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wwwroot/languages/ja/translation.json b/wwwroot/languages/ja/translation.json index fd99b4dd3e7..18c7626896c 100644 --- a/wwwroot/languages/ja/translation.json +++ b/wwwroot/languages/ja/translation.json @@ -2092,7 +2092,7 @@ "basemap": "背景地図", "disclaimer": "免責事項", "dataAttribution": "データの帰属", - "dataProvider": "データ提供先:" + "dataProvider": "データの出典" } }, "toast": { From 76633bb8d627417ee03099fd010ee3930ff64ec4 Mon Sep 17 00:00:00 2001 From: raf Date: Mon, 16 Oct 2023 08:21:25 +0000 Subject: [PATCH 113/129] Translated using Weblate (Catalan) for TerriaJSNext Currently translated at 100.0% (1377 of 1377 strings) Translation: TerriaJS/TerriaJSNext Translate-URL: https://hosted.weblate.org/projects/terriajs/terriajsnext/ca/ --- wwwroot/languages/ca/translation.json | 517 +++++++++++++++++++++++++- 1 file changed, 516 insertions(+), 1 deletion(-) diff --git a/wwwroot/languages/ca/translation.json b/wwwroot/languages/ca/translation.json index ac3ae5af7d6..146a03d8b9a 100644 --- a/wwwroot/languages/ca/translation.json +++ b/wwwroot/languages/ca/translation.json @@ -1275,7 +1275,13 @@ "zFilterEnabled": "Valors extrems amagats (feu clic per mostrar-los)", "zFilterDisabled": "Valors extrems detectats (feu clic per amagar-los)", "timeDimensionEnabled": "Temps activat (feu clic per desactivar)", - "timeDimensionDisabled": "Temps desactivat (feu clic per activar)" + "timeDimensionDisabled": "Temps desactivat (feu clic per activar)", + "custom": "Personalitzat", + "regionMapping": "Cartografiar regió", + "editStyle": "Edita l'estil", + "regionColumn": "Columna regió", + "manualRegionMapping": "Cartografiar regió manualment", + "activeStyle": "Mostra la variable" }, "terria": { "initErrorMessage": "S'ha produït un problema amb el servidor Terria. Això pot fer que algunes capes o el servei de conversió no estiguin disponibles.", @@ -1564,6 +1570,507 @@ "clipModel": "Model de clip", "showClippingBox": "Mostra el quadre de retall", "keepBoxAboveGround": "Només sobre terra" + }, + "tableStyling": { + "colors": { + "selectableDimensions": { + "value": { + "name": "Valor" + }, + "remove": { + "value": "Eliminar" + }, + "add": { + "value": "Afegir color" + }, + "color": { + "name": "Color" + } + }, + "name": "Colors" + }, + "workbenchOptions": { + "selectableDimensions": { + "tableStyleEnalbed": { + "options": { + "false": { + "name": "Estil ocult" + }, + "true": { + "name": "Mostrant estil" + } + }, + "name": "Mostra l'estil al banc de treball" + }, + "showDisableStyleOption": { + "name": "Mostra l'opció per desactivar estil" + }, + "enableManualRegionMapping": { + "name": "Activa la cartografia manual de regions" + }, + "showDisableTimeOption": { + "name": "Mostra l'opció per desactivar el temps" + } + }, + "name": "Opcions del banc de treball" + }, + "pointSize": { + "selectableDimensions": { + "pointSizeOffset": { + "name": "Desplaçament de mida" + }, + "pointSizeColumn": { + "name": "Variable" + }, + "pointSizeNull": { + "name": "Mida predeterminada" + }, + "pointSizesFactor": { + "name": "Factor de mida" + } + }, + "name": "Mida del punt" + }, + "styleOptions": { + "name": "Opcions d'estil", + "selectableDimensions": { + "styleTitle": { + "name": "Títol" + }, + "longitudeColumn": { + "name": "Columna de longitud" + }, + "latitudeColumn": { + "name": "Columna de latitud" + } + } + }, + "outline": { + "selectableDimensions": { + "color": { + "name": "Color" + }, + "width": { + "name": "Amplada" + } + }, + "name": "Estil de contorn" + }, + "label": { + "selectableDimensions": { + "verticalOrigin": { + "options": { + "bottom": { + "name": "Inferior" + }, + "top": { + "name": "Superior" + }, + "center": { + "name": "Centre" + }, + "baseline": { + "name": "Línia de base" + } + }, + "name": "Origen vertical" + }, + "horizontalOrigin": { + "options": { + "right": { + "name": "Dreta" + }, + "center": { + "name": "Centre" + }, + "left": { + "name": "Esquerra" + } + }, + "name": "Origen horitzontal" + }, + "outlineWidth": { + "name": "Amplada de contorn" + }, + "font": { + "name": "Tipus de lletra" + }, + "style": { + "options": { + "fillAndOutline": { + "name": "Emplenat i contorn" + }, + "fill": { + "name": "Només emplenat" + }, + "outline": { + "name": "Només contorn" + } + }, + "name": "Estil d'etiqueta" + }, + "offsetX": { + "name": "Desplaçament de píxel en X" + }, + "outlineColor": { + "name": "Color de contorn" + }, + "column": { + "name": "Columna d'etiquetes" + }, + "fillColor": { + "name": "Color d'emplenat" + }, + "offsetY": { + "name": "Desplaçament de píxel en Y" + }, + "scale": { + "name": "Escala" + } + }, + "name": "Estil d'etiqueta" + }, + "style": { + "selectableDimensions": { + "enum": { + "selectableDimensions": { + "enum": { + "selectableDimensions": { + "remove": { + "value": "Elimina" + }, + "value": { + "name": "Valor" + } + }, + "add": { + "value": "Afegeix estil per al valor" + }, + "noValue": "Cap valor" + } + }, + "name": "Enumera estils" + }, + "bin": { + "selectableDimensions": { + "bin": { + "noValue": "Cap valor", + "selectableDimensions": { + "stop": { + "name": "Final" + }, + "remove": { + "value": "Ellimina" + }, + "start": { + "name": "Inici" + } + }, + "range": "{{value1}} a {{value2}}" + }, + "add": { + "value": "Afegeix paperera d'estil" + } + }, + "name": "Papereres d'estil" + }, + "styleType": { + "constant": { + "name": "Cap estil" + }, + "bin": { + "name": "Discret" + }, + "name": "Tipus", + "undefinedLabel": "Si us plau concreteu", + "enum": { + "name": "Qualitatiu" + } + }, + "column": { + "name": "Variable" + } + }, + "null": { + "name": "Predeterminat" + } + }, + "bins": { + "selectableDimensions": { + "start": { + "selectableDimensions": { + "stop": { + "name": "Aturada" + }, + "color": { + "name": "Color" + }, + "start": { + "name": "Inici" + } + }, + "name": "{{value1}} a {{value2}}" + } + }, + "name": "Paperera" + }, + "timeOptions": { + "selectableDimensions": { + "tableTimeSpreadFinishTime": { + "options": { + "false": { + "name": "No" + }, + "true": { + "name": "Si" + } + }, + "name": "Distribució de l'hora d'acabament" + }, + "tableTimeSpreadStartTime": { + "options": { + "true": { + "name": "Si" + }, + "false": { + "name": "No" + } + }, + "name": "Hora d'inici de propagació" + }, + "tableEndTimeColumn": { + "name": "Columna de l'hora de finalització" + }, + "tableTimeColumn": { + "name": "Columna de temps" + }, + "tableTimeIsSampled": { + "options": { + "true": { + "name": "Si" + }, + "false": { + "name": "No" + } + }, + "name": "Mostrejat" + }, + "tableTimeDisplayDuration": { + "name": "Durada de la visualització" + }, + "tableTimeIdColumns": { + "name": "Columnes ID" + } + }, + "name": "Opcions de temps" + }, + "fill": { + "selectableDimensions": { + "type": { + "options": { + "sequentialContinuous": { + "name": "Seqüencial (continu)" + }, + "divergingContinuous": { + "name": "Divergent (continu)" + }, + "customDiscrete": { + "name": "Personalitzat (discret)" + }, + "customQualitative": { + "name": "Personalitzat (qualitatiu)" + }, + "sequentialDiscrete": { + "name": "Seqüencial (discret)" + }, + "noStyle": { + "name": "Sense estil" + }, + "divergingDiscrete": { + "name": "Divergent (discret)" + }, + "qualitative": { + "name": "Qualitatiu" + } + }, + "undefinedLabel": "Si us plau concreteu", + "name": "Tipus" + }, + "scheme": { + "name": "Esquema" + }, + "dataType": { + "name": "Tipus de columna (avançat)" + }, + "numberOfBins": { + "name": "Nombre de papereres" + }, + "tableColorColumn": { + "name": "Variable" + } + }, + "name": "Color d'ompliment" + }, + "data": { + "selectableDimensions": { + "tableStyleType": { + "options": { + "fill": { + "name": "Color d'ompliment" + }, + "point": { + "name": "Estil punt/marcador" + }, + "outline": { + "name": "Color de contorn" + }, + "trail": { + "name": "Estil de traça" + }, + "pointSize": { + "name": "Mida del punt" + }, + "label": { + "name": "Estil d'etiqueta" + } + }, + "name": "Simbologia" + }, + "tableStyle": { + "name": "Estil" + }, + "dataset": { + "name": "Conjunt de dades" + } + }, + "name": "Dades" + }, + "trail": { + "selectableDimensions": { + "resolution": { + "name": "Resolució" + }, + "trailStyleOptions": { + "name": "Opcions d'estil de sender", + "selectableDimensions": { + "material": { + "name": "Tipus de material", + "options": { + "polylineGlow": { + "name": "Resplendor de polilínia" + }, + "solidColor": { + "name": "Color sòlid" + } + } + } + } + }, + "leadTime": { + "name": "Temps per davant (segons)" + }, + "growColor": { + "name": "Color brillant" + }, + "trailTime": { + "name": "Temps per darrera (segons)" + }, + "width": { + "name": "Amplada" + }, + "growPower": { + "name": "Potència de resplendor" + }, + "solidColor": { + "name": "Color sòlid" + }, + "taperPower": { + "name": "Potència cónica" + } + } + }, + "additionalColors": { + "name": "Colors addicionals", + "selectableDimensions": { + "regionColor": { + "name": "Color de la regió" + }, + "outlierColor": { + "name": "Color atípic" + }, + "nullColor": { + "name": "Color predeterminat" + } + } + }, + "point": { + "selectableDimensions": { + "width": { + "name": "Amplada" + }, + "marker": { + "name": "Marcador", + "tooltip": "El marcador admet URL i base64 de qualsevol format d'imatge compatible (p. ex. PNG, SVG)" + }, + "rotation": { + "name": "Rotació" + }, + "height": { + "name": "Altura" + } + }, + "name": "Estil de marcador" + }, + "legend": { + "selectableDimensions": { + "legendTitle": { + "name": "Títol" + }, + "title": { + "name": "Element {{index}} Títol" + }, + "legendTicks": { + "name": "Marques" + } + }, + "name": "Llegenda" + }, + "copyUserStratum": "Copia l'estrat d'usuari al porta-retalls", + "outlierColor": { + "name": "Color atípic" + }, + "variableAndColumn": { + "selectableDimensions": { + "columnTitle": { + "name": "Títol" + }, + "columnUnits": { + "name": "Unitats" + } + }, + "name": "Variable/columna" + }, + "nullColor": { + "name": "Color predeterminat" + }, + "regionMapping": { + "name": "Cartografia de regions" + }, + "displayRange": { + "selectableDimensions": { + "max": { + "name": "Màx" + } + }, + "name": "Interval de visualització" + }, + "hideAdvancedOptions": "Amaga les opcions avançades", + "reset": "Restableix l'estil predeterminat", + "showAdvancedOptions": "Mostra les opcions avançades", + "min": { + "name": "Mín" + }, + "name": "Estil" } }, "deltaTool": { @@ -1682,5 +2189,13 @@ }, "includeStory": { "message": "Inclou la història a Share" + }, + "selectableDimensions": { + "colorRemove": "Eliminar", + "disabled": "Desactivat", + "colorAdd": "Afegir", + "undefinedLabel": "Sense definir", + "invalid": "Invàlid", + "enabled": "Activat" } } From c4cff5819e10bf2048309d7c7bd0b134c8aada0e Mon Sep 17 00:00:00 2001 From: Sven Wiemers Date: Thu, 2 Nov 2023 14:35:28 +0000 Subject: [PATCH 114/129] Translated using Weblate (German) for TerriaJSNext Currently translated at 88.4% (1218 of 1377 strings) Translation: TerriaJS/TerriaJSNext Translate-URL: https://hosted.weblate.org/projects/terriajs/terriajsnext/de/ --- wwwroot/languages/de/translation.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wwwroot/languages/de/translation.json b/wwwroot/languages/de/translation.json index b94d4d499c5..f3587df11d5 100644 --- a/wwwroot/languages/de/translation.json +++ b/wwwroot/languages/de/translation.json @@ -149,7 +149,7 @@ "browserCannotProvide": "Ihr Browser kann Ihren Standort nicht zur Verfügung stellen.", "errorGettingLocation": "Fehler beim Abrufen des Standorts", "myLocation": "Mein Standort", - "originError": "Ihr Browser kann Ihren Standort nur übermitteln, wenn Sie https verwenden. Möglicherweise können Sie stattdessen {{secureUrl}} verwenden.", + "originError": "Ihr Browser kann Ihren Standort nur übermitteln, wenn Sie HTTPS verwenden. Möglicherweise können Sie stattdessen {{secureUrl}} verwenden.", "centreMap": "Karte an Ihrem aktuellen Standort zentrieren" }, "measure": { @@ -361,7 +361,12 @@ "zFilterEnabled": "Extremwerte ausgeblendet (zum Anzeigen anklicken)", "timeDimensionEnabled": "Zeit aktiviert (zum Deaktivieren anklicken)", "bulkGeocoderErrorMessage": "Die Zuordnung von Adressen zu Längen-Breiten-Koordinaten ist nicht möglich, da beim Abrufen der Adresskoordinaten ein Fehler aufgetreten ist. Bitte überprüfen Sie Ihre Internetverbindung oder versuchen Sie es später noch einmal.", - "legendZFilterLabel": "Extremwerte" + "legendZFilterLabel": "Extremwerte", + "custom": "Benutzerdefiniert", + "regionMapping": "Regionale Kartierung", + "editStyle": "Stil bearbeiten", + "regionColumn": "Spalte \"Region\"", + "activeStyle": "Anzeige Variable" }, "terria": { "loadingInitSourcesErrorTitle": "Fehler beim Laden der Kartenkonfiguration", From 6d5a8b14b7a65743cd7b496fe587c976623cc420 Mon Sep 17 00:00:00 2001 From: Hiroo Imaki Date: Tue, 7 Nov 2023 22:19:41 +0000 Subject: [PATCH 115/129] Translated using Weblate (Japanese) for TerriaJSNext Currently translated at 100.0% (1377 of 1377 strings) Translation: TerriaJS/TerriaJSNext Translate-URL: https://hosted.weblate.org/projects/terriajs/terriajsnext/ja/ --- wwwroot/languages/ja/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wwwroot/languages/ja/translation.json b/wwwroot/languages/ja/translation.json index 18c7626896c..a824c5a6e2b 100644 --- a/wwwroot/languages/ja/translation.json +++ b/wwwroot/languages/ja/translation.json @@ -65,7 +65,7 @@ "title": "クリック+ドラッグでカメラを回転", "guidanceBtnText": "コンパスの詳しい使い方を見る", "guidanceBtnTitle": "ジャイロスコープの使い方", - "description": "・外側の輪:地図を回転 (Nが北)\n・内側の円:視点の自由な移動\n・ダブルクリック:ホームポジションに戻る\n回転の簡便操作:Ctrlキーと地図のドラッグ操作で視点の自由な移動が可能", + "description": "・外側の輪:地図を回転 (Nが北)\n・内側の円:視点の自由な移動\n・ダブルクリック:真上からの視点にリセット\n回転の簡便操作:Ctrlキーと地図のドラッグ操作で視点の自由な移動が可能", "guidance": { "ctrlDragDescription": "CTRLキーを押しながらドラッグしても地図を傾けたり回したりできます。", "dismissText": "閉じて今後は表示しない", @@ -538,7 +538,7 @@ }, "zoomCotrol": { "zoomOut": "ズームアウト", - "zoomReset": "拡大・縮小をリセット", + "zoomReset": "初期表示位置に戻る", "zoomIn": "ズームイン" }, "location": { From 0058106bff2a95e3cbdb433424d9b1a7576f1a16 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Wed, 15 Nov 2023 21:32:47 +0100 Subject: [PATCH 116/129] feat: migrate cesium ion search provider to model system --- .../CesiumIonSearchProvider.ts | 99 ++++++++++--------- .../registerSearchProviders.ts | 6 ++ .../CesiumIonSearchProviderTraits.ts | 19 ++++ .../CesiumIonSearchProviderSpec.ts | 28 ++++-- 4 files changed, 96 insertions(+), 56 deletions(-) create mode 100644 lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index e0e958c6f68..e414b0a222b 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -1,16 +1,20 @@ -import SearchProvider from "./SearchProvider"; -import { observable, makeObservable, runInAction } from "mobx"; +import i18next from "i18next"; +import { makeObservable, observable, 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 { CommonStrata } from "terriajs-plugin-api"; import { Category, SearchAction } from "../../Core/AnalyticEvents/analyticEvents"; +import loadJson from "../../Core/loadJson"; +import { applyTranslationIfExists } from "../../Language/languageHelpers"; +import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; +import CesiumIonSearchProviderTraits from "../../Traits/SearchProviders/CesiumIonSearchProviderTraits"; +import CreateModel from "../Definition/CreateModel"; +import Terria from "../Terria"; +import SearchProviderResults from "./SearchProviderResults"; +import SearchResult from "./SearchResult"; interface CesiumIonSearchProviderOptions { terria: Terria; @@ -28,51 +32,56 @@ interface CesiumIonGeocodeResult { features: CesiumIonGeocodeResultFeature[]; } -export default class CesiumIonSearchProvider extends SearchProvider { - readonly terria: Terria; - @observable key: string | undefined; - @observable flightDurationSeconds: number; - @observable url: string; +export default class CesiumIonSearchProvider extends LocationSearchProviderMixin( + CreateModel(CesiumIonSearchProviderTraits) +) { + static readonly type = "cesium-ion-search-provider"; + + get type() { + return CesiumIonSearchProvider.type; + } - constructor(options: CesiumIonSearchProviderOptions) { - super(); + constructor(uniqueId: string | undefined, terria: Terria) { + super(uniqueId, terria); 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 - ); + runInAction(() => { + if (!this.key && this.terria.configParameters.cesiumIonAccessToken) { + this.setTrait( + CommonStrata.defaults, + "key", + this.terria.configParameters.cesiumIonAccessToken + ); + } + this.showWarning(); + }); + } - if (!this.key) { + showWarning() { + if (!this.key || 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." + `The ${applyTranslationIfExists(this.name, i18next)}(${ + this.type + }) 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 searchProvider.key or parameters.cesiumIonAccessToken in config.json.` ); } } - protected async doSearch( - searchText: string, - searchResults: SearchProviderResults - ): Promise { - if (searchText === undefined || /^\s*$/.test(searchText)) { - return Promise.resolve(); - } - + protected logEvent(searchText: string): void { this.terria.analytics?.logEvent( Category.search, SearchAction.cesium, searchText ); + } + + protected async doSearch( + searchText: string, + searchResults: SearchProviderResults + ): Promise { + searchResults.results.length = 0; + searchResults.message = undefined; let response: CesiumIonGeocodeResult; try { @@ -80,22 +89,20 @@ export default class CesiumIonSearchProvider extends SearchProvider { `${this.url}?text=${searchText}&access_token=${this.key}` ); } catch (e) { - runInAction(() => { - searchResults.message = i18next.t("viewModels.searchErrorOccurred"); - }); + searchResults.message = { + content: "translate#viewModels.searchErrorOccurred" + }; return; } runInAction(() => { - if (!response.features) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); + if (!response.features || response.features.length === 0) { + searchResults.message = { + content: "translate#viewModels.searchNoLocations" + }; return; } - if (response.features.length === 0) { - searchResults.message = i18next.t("viewModels.searchNoLocations"); - } - searchResults.results = response.features.map((feature) => { const [w, s, e, n] = feature.bbox; const rectangle = Rectangle.fromDegrees(w, s, e, n); diff --git a/lib/Models/SearchProviders/registerSearchProviders.ts b/lib/Models/SearchProviders/registerSearchProviders.ts index 49d2ca854f2..5eb5a1e1fdd 100644 --- a/lib/Models/SearchProviders/registerSearchProviders.ts +++ b/lib/Models/SearchProviders/registerSearchProviders.ts @@ -1,5 +1,6 @@ import AustralianGazetteerSearchProvider from "./AustralianGazetteerSearchProvider"; import BingMapsSearchProvider from "./BingMapsSearchProvider"; +import CesiumIonSearchProvider from "./CesiumIonSearchProvider"; import SearchProviderFactory from "./SearchProviderFactory"; export default function registerSearchProviders() { @@ -8,6 +9,11 @@ export default function registerSearchProviders() { BingMapsSearchProvider ); + SearchProviderFactory.register( + CesiumIonSearchProvider.type, + CesiumIonSearchProvider + ); + SearchProviderFactory.register( AustralianGazetteerSearchProvider.type, AustralianGazetteerSearchProvider diff --git a/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts b/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts new file mode 100644 index 00000000000..7c7e7e08c05 --- /dev/null +++ b/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts @@ -0,0 +1,19 @@ +import { mixTraits, primitiveTrait } from "terriajs-plugin-api"; +import LocationSearchProviderTraits, { + SearchProviderMapCenterTraits +} from "./LocationSearchProviderTraits"; + +export default class BingMapsSearchProviderTraits extends mixTraits( + LocationSearchProviderTraits, + SearchProviderMapCenterTraits +) { + url: string = "https://api.cesium.com/v1/geocode/search"; + + @primitiveTrait({ + type: "string", + name: "Key", + description: + "The Cesium ION key. If not provided, will try to use the global cesium ion key." + }) + key?: string; +} diff --git a/test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts b/test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts index f000088d6f9..e0858fc3542 100644 --- a/test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts +++ b/test/Models/SearchProviders/CesiumIonSearchProviderSpec.ts @@ -1,6 +1,7 @@ import CesiumIonSearchProvider from "../../../lib/Models/SearchProviders/CesiumIonSearchProvider"; import Terria from "../../../lib/Models//Terria"; import * as loadJson from "../../../lib/Core/loadJson"; +import CommonStrata from "../../../lib/Models/Definition/CommonStrata"; const fixture = { features: [ @@ -17,15 +18,18 @@ const fixture = { }; describe("CesiumIonSearchProvider", () => { - const searchProvider = new CesiumIonSearchProvider({ - key: "testkey", - url: "api.test.com", - terria: { - currentViewer: { - zoomTo: () => {} - } - } as unknown as Terria + let terria: Terria; + let searchProvider: CesiumIonSearchProvider; + + beforeEach(async function () { + terria = new Terria({ + baseUrl: "./" + }); + searchProvider = new CesiumIonSearchProvider("test-cesium-ion", terria); + searchProvider.setTrait(CommonStrata.definition, "key", "testkey"); + searchProvider.setTrait(CommonStrata.definition, "url", "api.test.com"); }); + it("Handles valid results", async () => { spyOn(loadJson, "default").and.returnValue( new Promise((resolve) => resolve(fixture)) @@ -47,13 +51,17 @@ describe("CesiumIonSearchProvider", () => { const result = await searchProvider.search("test"); console.log(result); expect(result.results.length).toBe(0); - expect(result.message).toBe("viewModels.searchNoLocations"); + expect(result.message?.content).toBe( + "translate#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"); + expect(result.message?.content).toBe( + "translate#viewModels.searchErrorOccurred" + ); }); }); From 6dc41280f8b6a383e4fdad3d002fdb08bf820e45 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 19 Nov 2023 16:55:34 +0100 Subject: [PATCH 117/129] feat: move searchBarModel from configParametes to terria property --- .../WebFeatureServiceSearchProviderMixin.ts | 2 +- .../SearchProviders/CatalogSearchProvider.ts | 2 +- lib/Models/SearchProviders/SearchBarModel.ts | 27 ++++++++++++++---- .../createStubSearchProvider.ts | 2 +- .../upsertSearchProviderFromJson.ts | 8 +++--- lib/Models/Terria.ts | 28 ++++++++----------- lib/ReactViewModels/SearchState.ts | 7 ++--- lib/ReactViewModels/ViewState.ts | 1 - lib/ReactViews/Mobile/MobileHeader.jsx | 2 +- .../Search/LocationSearchResults.tsx | 3 +- lib/ReactViews/SidePanel/SidePanel.tsx | 2 +- lib/Traits/ModelTraitsInterface.ts | 7 ----- .../Search/SearchBoxAndResultsSpec.tsx | 3 +- 13 files changed, 46 insertions(+), 48 deletions(-) delete mode 100644 lib/Traits/ModelTraitsInterface.ts diff --git a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts index ef7bc29df00..622a5bddb57 100644 --- a/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/WebFeatureServiceSearchProviderMixin.ts @@ -224,7 +224,7 @@ function createZoomToFunction( const flightDurationSeconds: number = model.flightDurationSeconds || - model.terria.configParameters.searchBarModel.flightDurationSeconds; + model.terria.searchBarModel.flightDurationSeconds; return function () { model.terria.currentViewer.zoomTo(rectangle, flightDurationSeconds); diff --git a/lib/Models/SearchProviders/CatalogSearchProvider.ts b/lib/Models/SearchProviders/CatalogSearchProvider.ts index dfde8265a3a..81063efcc85 100644 --- a/lib/Models/SearchProviders/CatalogSearchProvider.ts +++ b/lib/Models/SearchProviders/CatalogSearchProvider.ts @@ -121,7 +121,7 @@ export default class CatalogSearchProvider extends CatalogSearchProviderMixin( this.setTrait( CommonStrata.defaults, "minCharacters", - terria.configParameters.searchBarModel.minCharacters + terria.searchBarModel.minCharacters ); } diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts index a6364d4d651..2959d38f2b7 100644 --- a/lib/Models/SearchProviders/SearchBarModel.ts +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -5,19 +5,23 @@ import { makeObservable, observable } from "mobx"; +import { JsonObject } from "protomaps"; import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; +import RuntimeError from "terriajs-cesium/Source/Core/RuntimeError"; import Result from "../../Core/Result"; import TerriaError from "../../Core/TerriaError"; +import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; +import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; import { SearchBarTraits } from "../../Traits/SearchProviders/SearchBarTraits"; +import SearchProviderTraits from "../../Traits/SearchProviders/SearchProviderTraits"; import CommonStrata from "../Definition/CommonStrata"; import CreateModel from "../Definition/CreateModel"; import { BaseModel } from "../Definition/Model"; +import ModelPropertiesFromTraits from "../Definition/ModelPropertiesFromTraits"; +import updateModelFromJson from "../Definition/updateModelFromJson"; import Terria from "../Terria"; import SearchProviderFactory from "./SearchProviderFactory"; import upsertSearchProviderFromJson from "./upsertSearchProviderFromJson"; -import LocationSearchProviderMixin from "../../ModelMixins/SearchProviders/LocationSearchProviderMixin"; -import CatalogSearchProviderMixin from "../../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; -import RuntimeError from "terriajs-cesium/Source/Core/RuntimeError"; export class SearchBarModel extends CreateModel(SearchBarTraits) { private locationSearchProviders = observable.map(); @@ -31,10 +35,21 @@ export class SearchBarModel extends CreateModel(SearchBarTraits) { makeObservable(this); } - initializeSearchProviders() { - const errors: TerriaError[] = []; + updateModelConfig(config?: ModelPropertiesFromTraits) { + if (config) { + updateModelFromJson( + this, + CommonStrata.definition, + config as never as JsonObject + ); + } + return this; + } - const searchProviders = this.terria.configParameters.searchProviders; + initializeSearchProviders( + searchProviders: ModelPropertiesFromTraits[] + ) { + const errors: TerriaError[] = []; if (!isObservableArray(searchProviders)) { errors.push( diff --git a/lib/Models/SearchProviders/createStubSearchProvider.ts b/lib/Models/SearchProviders/createStubSearchProvider.ts index 8658a0b3aac..9f8b9a74c88 100644 --- a/lib/Models/SearchProviders/createStubSearchProvider.ts +++ b/lib/Models/SearchProviders/createStubSearchProvider.ts @@ -22,6 +22,6 @@ export default function createStubSearchProvider( const stub = new StubSearchProvider(idToUse, terria); stub.setTrait(CommonStrata.underride, "name", stub.uniqueId); - terria.configParameters.searchBarModel.addSearchProvider(stub); + terria.searchBarModel.addSearchProvider(stub); return stub; } diff --git a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts index 08a0efd42e1..81c863b1ad0 100644 --- a/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts +++ b/lib/Models/SearchProviders/upsertSearchProviderFromJson.ts @@ -55,7 +55,7 @@ export default function upsertSearchProviderFromJson( if (model.type !== StubSearchProvider.type) { try { - model.terria.configParameters.searchBarModel.addSearchProvider(model); + model.terria.searchBarModel.addSearchProvider(model); } catch (error) { errors.push(TerriaError.from(error)); } @@ -88,19 +88,19 @@ function setDefaultTraits(model: BaseModel) { model.setTrait( CommonStrata.defaults, "flightDurationSeconds", - terria.configParameters.searchBarModel.flightDurationSeconds + terria.searchBarModel.flightDurationSeconds ); model.setTrait( CommonStrata.defaults, "minCharacters", - terria.configParameters.searchBarModel.minCharacters + terria.searchBarModel.minCharacters ); model.setTrait( CommonStrata.defaults, "recommendedListLength", - terria.configParameters.searchBarModel?.recommendedListLength + terria.searchBarModel.recommendedListLength ); }); } diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 776e08dea20..24773609c63 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -124,6 +124,9 @@ import TimelineStack from "./TimelineStack"; import { isViewerMode, setViewerMode } from "./ViewerMode"; import Workbench from "./Workbench"; import SelectableDimensionWorkflow from "./Workflows/SelectableDimensionWorkflow"; +import { SearchBarTraits } from "../Traits/SearchProviders/SearchBarTraits"; +import ModelPropertiesFromTraits from "./Definition/ModelPropertiesFromTraits"; +import SearchProviderTraits from "../Traits/SearchProviders/SearchProviderTraits"; // import overrides from "../Overrides/defaults.jsx"; @@ -345,8 +348,8 @@ export interface ConfigParameters { /** * The search bar allows requesting information from various search services at once. */ - searchBarModel: SearchBarModel; - searchProviders: any[]; + searchBarConfig?: ModelPropertiesFromTraits; + searchProviders: ModelPropertiesFromTraits[]; } interface StartOptions { @@ -442,6 +445,7 @@ export default class Terria { readonly overlays = new Workbench(); readonly catalog = new Catalog(this); readonly baseMapsModel = new BaseMapsModel("basemaps", this); + readonly searchBarModel = new SearchBarModel(this); readonly timelineClock = new Clock({ shouldAnimate: false }); // readonly overrides: any = overrides; // TODO: add options.functionOverrides like in master @@ -560,7 +564,7 @@ export default class Terria { relatedMaps: defaultRelatedMaps, aboutButtonHrefUrl: "about.html", plugins: undefined, - searchBarModel: new SearchBarModel(this), + searchBarConfig: undefined, searchProviders: [] }; @@ -1053,8 +1057,9 @@ export default class Terria { ) ); - this.configParameters.searchBarModel - .initializeSearchProviders() + this.searchBarModel + .updateModelConfig(this.configParameters.searchBarConfig) + .initializeSearchProviders(this.configParameters.searchProviders) .catchError((error) => this.raiseErrorToUser( TerriaError.from(error, "Failed to initialize searchProviders") @@ -1296,18 +1301,7 @@ export default class Terria { updateParameters(parameters: ConfigParameters | JsonObject): void { Object.entries(parameters).forEach(([key, value]) => { if (this.configParameters.hasOwnProperty(key)) { - if (key === "searchBarModel") { - if (!isDefined(this.configParameters.searchBarModel)) { - this.configParameters.searchBarModel = new SearchBarModel(this); - } - updateModelFromJson( - this.configParameters.searchBarModel, - CommonStrata.definition, - value - ); - } else { - (this.configParameters as any)[key] = value; - } + (this.configParameters as any)[key] = value; } }); diff --git a/lib/ReactViewModels/SearchState.ts b/lib/ReactViewModels/SearchState.ts index 3cdb884dc0c..cae172c8b99 100644 --- a/lib/ReactViewModels/SearchState.ts +++ b/lib/ReactViewModels/SearchState.ts @@ -50,7 +50,7 @@ export default class SearchState { this.terria = options.terria; runInAction(() => { - this.terria.configParameters.searchBarModel.catalogSearchProvider = + this.terria.searchBarModel.catalogSearchProvider = options.catalogSearchProvider || new CatalogSearchProvider("catalog-search-provider", options.terria); }); @@ -100,13 +100,12 @@ export default class SearchState { @computed private get locationSearchProviders(): LocationSearchProviderMixin.Instance[] { - return this.terria.configParameters.searchBarModel - .locationSearchProvidersArray; + return this.terria.searchBarModel.locationSearchProvidersArray; } @computed get catalogSearchProvider(): CatalogSearchProviderMixin.Instance | undefined { - return this.terria.configParameters.searchBarModel.catalogSearchProvider; + return this.terria.searchBarModel.catalogSearchProvider; } @computed diff --git a/lib/ReactViewModels/ViewState.ts b/lib/ReactViewModels/ViewState.ts index 803d3206515..0c7b80b4240 100644 --- a/lib/ReactViewModels/ViewState.ts +++ b/lib/ReactViewModels/ViewState.ts @@ -39,7 +39,6 @@ import { import DisclaimerHandler from "./DisclaimerHandler"; import SearchState from "./SearchState"; import CatalogSearchProviderMixin from "../ModelMixins/SearchProviders/CatalogSearchProviderMixin"; -import LocationSearchProviderMixin from "../ModelMixins/SearchProviders/LocationSearchProviderMixin"; export const DATA_CATALOG_NAME = "data-catalog"; export const USER_DATA_NAME = "my-data"; diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index 41a04e3858b..9600c11404c 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -138,7 +138,7 @@ class MobileHeader extends React.Component { onSearchTextChanged={this.changeLocationSearchText.bind(this)} onDoSearch={this.searchLocations.bind(this)} placeholder={applyTranslationIfExists( - viewState.terria.configParameters.searchBarModel.placeholder, + viewState.terria.searchBarModel.placeholder, this.props.i18n )} alwaysShowClear={true} diff --git a/lib/ReactViews/Search/LocationSearchResults.tsx b/lib/ReactViews/Search/LocationSearchResults.tsx index 475715f46aa..191e7db1b33 100644 --- a/lib/ReactViews/Search/LocationSearchResults.tsx +++ b/lib/ReactViews/Search/LocationSearchResults.tsx @@ -67,8 +67,7 @@ class LocationSearchResults extends React.Component { @computed get validResults() { const { search, terria } = this.props; - const locationSearchBoundingBox = - terria.configParameters.searchBarModel.boundingBoxLimit; + const locationSearchBoundingBox = terria.searchBarModel.boundingBoxLimit; let filterResults = false; let west: number | undefined, east: number | undefined, diff --git a/lib/ReactViews/SidePanel/SidePanel.tsx b/lib/ReactViews/SidePanel/SidePanel.tsx index 6f6c584cff4..d020e0bdb01 100644 --- a/lib/ReactViews/SidePanel/SidePanel.tsx +++ b/lib/ReactViews/SidePanel/SidePanel.tsx @@ -164,7 +164,7 @@ const SidePanel = observer>( viewState={viewState} terria={terria} placeholder={applyTranslationIfExists( - terria.configParameters.searchBarModel.placeholder, + terria.searchBarModel.placeholder, i18n )} /> diff --git a/lib/Traits/ModelTraitsInterface.ts b/lib/Traits/ModelTraitsInterface.ts deleted file mode 100644 index 3f71a80c042..00000000000 --- a/lib/Traits/ModelTraitsInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import ModelTraits from "./ModelTraits"; - -export type ModelTraitsInterface = { - [Member in keyof ClassType]: Member extends ModelTraits - ? ModelTraitsInterface> - : ClassType[Member]; -}; diff --git a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx index 0071d4dde8a..20f7a276921 100644 --- a/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx +++ b/test/ReactViews/Search/SearchBoxAndResultsSpec.tsx @@ -85,8 +85,7 @@ describe("SearchBoxAndResults", function () { viewState.searchState.locationSearchText = searchText; viewState.searchState.showLocationSearchResults = true; viewState.searchState.locationSearchResults = []; - viewState.terria.configParameters.searchBarModel.catalogSearchProvider = - undefined; + viewState.terria.searchBarModel.catalogSearchProvider = undefined; }); act(() => { testRenderer = create( From 8d1ad637958618d99bbec04c16af857796dee707 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Sun, 19 Nov 2023 17:35:01 +0100 Subject: [PATCH 118/129] fix: replace terriajs-plugin-api imports with proper ones --- lib/Models/SearchProviders/CesiumIonSearchProvider.ts | 6 +++--- lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts | 3 ++- test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts | 3 +-- test/Models/SearchProviders/BingMapsSearchProviderSpec.ts | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index e414b0a222b..37c79972ce6 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -1,8 +1,7 @@ import i18next from "i18next"; -import { makeObservable, observable, runInAction } from "mobx"; +import { makeObservable, runInAction } from "mobx"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; -import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; -import { CommonStrata } from "terriajs-plugin-api"; + import { Category, SearchAction @@ -15,6 +14,7 @@ import CreateModel from "../Definition/CreateModel"; import Terria from "../Terria"; import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; +import CommonStrata from "../Definition/CommonStrata"; interface CesiumIonSearchProviderOptions { terria: Terria; diff --git a/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts b/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts index 7c7e7e08c05..5f7da950585 100644 --- a/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts @@ -1,4 +1,5 @@ -import { mixTraits, primitiveTrait } from "terriajs-plugin-api"; +import primitiveTrait from "../Decorators/primitiveTrait"; +import mixTraits from "../mixTraits"; import LocationSearchProviderTraits, { SearchProviderMapCenterTraits } from "./LocationSearchProviderTraits"; diff --git a/test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts b/test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts index 54c07c34b38..44227b87351 100644 --- a/test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts +++ b/test/ModelMixins/SearchProviders/SearchProviderMixinSpec.ts @@ -1,7 +1,6 @@ -import { CommonStrata } from "terriajs-plugin-api"; import SearchProviderMixin from "../../../lib/ModelMixins/SearchProviders/SearchProviderMixin"; +import CommonStrata from "../../../lib/Models/Definition/CommonStrata"; import CreateModel from "../../../lib/Models/Definition/CreateModel"; -import SearchProviderResults from "../../../lib/Models/SearchProviders/SearchProviderResults"; import Terria from "../../../lib/Models/Terria"; import BingMapsSearchProviderTraits from "../../../lib/Traits/SearchProviders/BingMapsSearchProviderTraits"; diff --git a/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts b/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts index 4035b4bc26b..557fe5b848e 100644 --- a/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts +++ b/test/Models/SearchProviders/BingMapsSearchProviderSpec.ts @@ -1,11 +1,11 @@ +import { runInAction } from "mobx"; import Resource from "terriajs-cesium/Source/Core/Resource"; import LocationSearchProviderMixin from "../../../lib/ModelMixins/SearchProviders/LocationSearchProviderMixin"; import BingMapsSearchProvider from "../../../lib/Models/SearchProviders/BingMapsSearchProvider"; import Terria from "../../../lib/Models/Terria"; -import { exp } from "protomaps"; -import { runInAction } from "mobx"; -import { CommonStrata } from "terriajs-plugin-api"; + import * as loadJsonp from "../../../lib/Core/loadJsonp"; +import CommonStrata from "../../../lib/Models/Definition/CommonStrata"; describe("BingMapsSearchProvider", function () { let terria: Terria; From 04785d4f1e4fd9c3c39decb6463a3f541b4902bf Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Mon, 20 Nov 2023 14:44:52 +0100 Subject: [PATCH 119/129] fix: rename traits class --- lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts b/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts index 5f7da950585..350a3f9ec04 100644 --- a/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts +++ b/lib/Traits/SearchProviders/CesiumIonSearchProviderTraits.ts @@ -4,7 +4,7 @@ import LocationSearchProviderTraits, { SearchProviderMapCenterTraits } from "./LocationSearchProviderTraits"; -export default class BingMapsSearchProviderTraits extends mixTraits( +export default class CesiumIonSearchProviderTraits extends mixTraits( LocationSearchProviderTraits, SearchProviderMapCenterTraits ) { From d3fe2f4eb667a0a15db61d84bb528f460ddecff8 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Mon, 20 Nov 2023 14:45:29 +0100 Subject: [PATCH 120/129] doc: add cesium search provider to docs --- doc/customizing/search-providers.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/customizing/search-providers.md b/doc/customizing/search-providers.md index 1f2d49a97bd..3077a7e3fb5 100644 --- a/doc/customizing/search-providers.md +++ b/doc/customizing/search-providers.md @@ -53,6 +53,7 @@ For more details see [/buildprocess/generateCatalogIndex.ts](/buildprocess/gener Location search providers are used to search for locations on the map. TerriaJS currently supports two implementations of search providers: - [`BingMapsSearchProvider`](#bingmapssearchprovider) - implementation which in background uses Bing Map search API +- [`CesiumIonSearchProvider`](#cesiumionsearchprovider) - implementation which in background use CesiumIon geocoding API - [`AustralianGazetteerSearchProvider`](#australiangazetteersearchprovider) - uses `WebFeatureServiceSearchProvider` Each `LocationSearchProvider support following confing options @@ -93,6 +94,32 @@ It provides a default value for `url: https://dev.virtualearth.net/` }, ``` +### CesiumIonSearchProvider + +`type: cesium-ion-search-provider` + +CesiumIon search provider is based on CesiumIon geocoding API provided by Cesium. To enable it it is necessary to add appropriate cesium API key as config parameter. + +| Name | Required | Type | Default | Description | +| ----- | -------- | ---------- | --------------------------------------- | ------------------ | +| `key` | no | **string** | `configParameters.cesiumIonAccessToken` | The CesiumIon key. | + +It provides a default value for `url: https://api.cesium.com/v1/geocode/search/` + +**Example** + +```json +{ + "id": "search-provider/cesium-ion", + "type": "cesium-ion-search-provider", + "name": "translate#viewModels.searchLocations", + "url": "https://api.cesium.com/v1/geocode/search/", + "flightDurationSeconds": 1.5, + "minCharacters": 5, + "isOpen": true +}, +``` + ### AustralianGazetteerSearchProvider `type: australian-gazetteer-search-provider` From e9853768e791ff17004c19704010ddab4b34f666 Mon Sep 17 00:00:00 2001 From: Zoran Kokeza Date: Mon, 20 Nov 2023 14:48:05 +0100 Subject: [PATCH 121/129] fix: show warning --- .../LocationSearchProviderMixin.ts | 3 +++ .../SearchProviders/BingMapsSearchProvider.ts | 8 ++++---- .../SearchProviders/CesiumIonSearchProvider.ts | 15 ++++----------- lib/Models/SearchProviders/SearchBarModel.ts | 6 +++++- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts index 80543f209f2..d1bd12985e7 100644 --- a/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts +++ b/lib/ModelMixins/SearchProviders/LocationSearchProviderMixin.ts @@ -28,6 +28,9 @@ function LocationSearchProviderMixin< toggleOpen(stratumId: CommonStrata = CommonStrata.user) { this.setTrait(stratumId, "isOpen", !this.isOpen); } + + @action + showWarning() {} } return LocationSearchProviderMixin; diff --git a/lib/Models/SearchProviders/BingMapsSearchProvider.ts b/lib/Models/SearchProviders/BingMapsSearchProvider.ts index 8e1c6d16a16..b9ebb6b0ab7 100644 --- a/lib/Models/SearchProviders/BingMapsSearchProvider.ts +++ b/lib/Models/SearchProviders/BingMapsSearchProvider.ts @@ -1,5 +1,5 @@ import i18next from "i18next"; -import { action, makeObservable, runInAction } from "mobx"; +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"; @@ -34,18 +34,18 @@ export default class BingMapsSearchProvider extends LocationSearchProviderMixin( makeObservable(this); runInAction(() => { - if (!this.key && this.terria.configParameters.bingMapsKey) { + if (!!this.terria.configParameters.bingMapsKey) { this.setTrait( CommonStrata.defaults, "key", this.terria.configParameters.bingMapsKey ); } - this.showWarning(); }); } - showWarning() { + @override + override showWarning() { if (!this.key || this.key === "") { console.warn( `The ${applyTranslationIfExists(this.name, i18next)}(${ diff --git a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts index 37c79972ce6..b1e39b33605 100644 --- a/lib/Models/SearchProviders/CesiumIonSearchProvider.ts +++ b/lib/Models/SearchProviders/CesiumIonSearchProvider.ts @@ -1,5 +1,5 @@ import i18next from "i18next"; -import { makeObservable, runInAction } from "mobx"; +import { makeObservable, override, runInAction } from "mobx"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import { @@ -16,13 +16,6 @@ import SearchProviderResults from "./SearchProviderResults"; import SearchResult from "./SearchResult"; import CommonStrata from "../Definition/CommonStrata"; -interface CesiumIonSearchProviderOptions { - terria: Terria; - url?: string; - key: string; - flightDurationSeconds?: number; -} - interface CesiumIonGeocodeResultFeature { bbox: [number, number, number, number]; properties: { label: string }; @@ -47,18 +40,18 @@ export default class CesiumIonSearchProvider extends LocationSearchProviderMixin makeObservable(this); runInAction(() => { - if (!this.key && this.terria.configParameters.cesiumIonAccessToken) { + if (!!this.terria.configParameters.cesiumIonAccessToken) { this.setTrait( CommonStrata.defaults, "key", this.terria.configParameters.cesiumIonAccessToken ); } - this.showWarning(); }); } - showWarning() { + @override + override showWarning() { if (!this.key || this.key === "") { console.warn( `The ${applyTranslationIfExists(this.name, i18next)}(${ diff --git a/lib/Models/SearchProviders/SearchBarModel.ts b/lib/Models/SearchProviders/SearchBarModel.ts index 2959d38f2b7..3dc1a21a0a9 100644 --- a/lib/Models/SearchProviders/SearchBarModel.ts +++ b/lib/Models/SearchProviders/SearchBarModel.ts @@ -61,12 +61,16 @@ export class SearchBarModel extends CreateModel(SearchBarTraits) { ); } searchProviders?.forEach((searchProvider) => { - upsertSearchProviderFromJson( + const loadedModel = upsertSearchProviderFromJson( SearchProviderFactory, this.terria, CommonStrata.definition, searchProvider ).pushErrorTo(errors); + + if (LocationSearchProviderMixin.isMixedInto(loadedModel)) { + loadedModel.showWarning(); + } }); return new Result( From d6d0ec335b2509faaf336f07f4386c5d21a73106 Mon Sep 17 00:00:00 2001 From: Mike Wu <41275384+mwu2018@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:30:13 +1100 Subject: [PATCH 122/129] Make lock icon black (#6982) * Make lock icon black. * Temporarily make defaut access type non-public for testing. * Update docs. * Adjust lock icon position in workbench. * Improve lock icon position in workbench. * Update doc. * Remove testing change. --- CHANGES.md | 1 + lib/ReactViews/DataCatalog/CatalogGroup.jsx | 6 ++++++ lib/ReactViews/PrivateIndicator/PrivateIndicator.jsx | 4 +--- lib/ReactViews/Workbench/WorkbenchItem.tsx | 10 +++++----- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8836a065777..a397ddcf4a4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ #### next release (8.3.9) +- Make all icons in `CatalogGroup` black by default and white when a catalog group is focused, selected or hovered over. Improve lock icon position in workbench. - [The next improvement] #### 8.3.8 - 2023-11-15 diff --git a/lib/ReactViews/DataCatalog/CatalogGroup.jsx b/lib/ReactViews/DataCatalog/CatalogGroup.jsx index 196e6bda311..1c4cb015409 100644 --- a/lib/ReactViews/DataCatalog/CatalogGroup.jsx +++ b/lib/ReactViews/DataCatalog/CatalogGroup.jsx @@ -17,12 +17,18 @@ const CatalogGroupButton = styled.button` &:focus { color: ${props.theme.textLight}; background-color: ${props.theme.modalHighlight}; + svg { + fill: white; + } } ${ props.active && ` color: ${props.theme.textLight}; background-color: ${props.theme.modalHighlight}; + svg { + fill: white; + } ` } `} diff --git a/lib/ReactViews/PrivateIndicator/PrivateIndicator.jsx b/lib/ReactViews/PrivateIndicator/PrivateIndicator.jsx index 559e8c51616..06c2cce9781 100644 --- a/lib/ReactViews/PrivateIndicator/PrivateIndicator.jsx +++ b/lib/ReactViews/PrivateIndicator/PrivateIndicator.jsx @@ -19,13 +19,11 @@ export default function PrivateIndicator(props) { inWorkbench={props.inWorkbench} css={` margin-top: -1px; - ${(p) => p.inWorkbench && `margin-right: 2px;`} - svg { width: 15px; height: 15px; fill: ${(p) => - p.inWorkbench ? p.theme.textLight : p.theme.colorPrimary}; + p.inWorkbench ? p.theme.textLight : p.theme.charcoalGrey}; } `} > diff --git a/lib/ReactViews/Workbench/WorkbenchItem.tsx b/lib/ReactViews/Workbench/WorkbenchItem.tsx index d55b4762764..91239d805b5 100644 --- a/lib/ReactViews/Workbench/WorkbenchItem.tsx +++ b/lib/ReactViews/Workbench/WorkbenchItem.tsx @@ -149,12 +149,12 @@ class WorkbenchItemRaw extends React.Component { {CatalogMemberMixin.isMixedInto(item) ? ( + {item.isPrivate && ( + + + + )} this.toggleDisplay()}> - {item.isPrivate && ( - - - - )} {this.isOpen ? ( Date: Fri, 24 Nov 2023 15:59:02 +1100 Subject: [PATCH 123/129] Release 8.3.9 --- CHANGES.md | 12 +++++++++--- package.json | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a397ddcf4a4..8564480905c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,17 @@ # Change Log -#### next release (8.3.9) +#### next release (8.3.10) -- Make all icons in `CatalogGroup` black by default and white when a catalog group is focused, selected or hovered over. Improve lock icon position in workbench. - [The next improvement] +#### 8.3.9 - 2023-11-24 + +- Make all icons in `CatalogGroup` black by default and white when a catalog group is focused, selected or hovered over. Improve lock icon position in workbench. +- **Breaking change** - new Search Provider model + - added SearchProviderMixin to connect searchProviders with a model system + - Create a simple base Mixin (`SearchProviderMixin`) to attach SearchProviders to the Model system and enable easier creation of new search providers. + - Make SearchProviders configurable from `config.json`. + #### 8.3.8 - 2023-11-15 - Fix maximum call stack size exceeded on Math.min/max when creating Charts @@ -23,7 +30,6 @@ - Add CesiumIon geocoder - `CatalogGroup` will now not show members until loaded - Add `GetTimeseries` support to `WebMapServiceCatalogItem`. This adds a new `supportsGetTimeseries` trait, which when true will replace `GetFeatureInfo` with `GetTimeseries` requests. It will also change `info_format` to `text/csv`, and show a chart in the feature info panel. Servers which advertise `GetTimeseries` capability will have this trait set to true by default. `GetTimeseries` requests will have `time = ""`. -- [The next improvement] #### 8.3.7 - 2023-10-26 diff --git a/package.json b/package.json index 4890917d05e..ae540de8108 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "terriajs", - "version": "8.3.8", + "version": "8.3.9", "description": "Geospatial data visualization platform.", "license": "Apache-2.0", "engines": { From 8e77fb5ef9c98cc9f8dc42d75141800a0c040311 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 24 Nov 2023 15:59:33 +1100 Subject: [PATCH 124/129] revert ci branch to main --- buildprocess/ci-deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildprocess/ci-deploy.sh b/buildprocess/ci-deploy.sh index ab453002d23..06ff60bc1b0 100644 --- a/buildprocess/ci-deploy.sh +++ b/buildprocess/ci-deploy.sh @@ -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 use-cesium-ion-geocoder https://github.com/TerriaJS/TerriaMap.git +git clone -b main 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 From b5db5581b2b6566849f94009f1938cb713f02c17 Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 24 Nov 2023 16:02:14 +1100 Subject: [PATCH 125/129] add search provider docs to changes --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 8564480905c..17f7bb619a9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ - added SearchProviderMixin to connect searchProviders with a model system - Create a simple base Mixin (`SearchProviderMixin`) to attach SearchProviders to the Model system and enable easier creation of new search providers. - Make SearchProviders configurable from `config.json`. + - See [0011-configurable-search-providers ADR](./architecture/0011-configurable-search-providers.md) and [Search providers customization](./doc/customizing/search-providers.md) for more details #### 8.3.8 - 2023-11-15 From 4fe9e61e2e697fb2fdbe44cc624f281bcc5232ab Mon Sep 17 00:00:00 2001 From: Stephen Davies Date: Fri, 24 Nov 2023 16:28:36 +1100 Subject: [PATCH 126/129] Improve changelog entries --- CHANGES.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 17f7bb619a9..8844ca235fc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,12 +6,12 @@ #### 8.3.9 - 2023-11-24 -- Make all icons in `CatalogGroup` black by default and white when a catalog group is focused, selected or hovered over. Improve lock icon position in workbench. -- **Breaking change** - new Search Provider model - - added SearchProviderMixin to connect searchProviders with a model system - - Create a simple base Mixin (`SearchProviderMixin`) to attach SearchProviders to the Model system and enable easier creation of new search providers. - - Make SearchProviders configurable from `config.json`. +- **Breaking change:** new Search Provider model + - Added SearchProviderMixin to connect searchProviders with a model system + - Created a simple base Mixin (`SearchProviderMixin`) to attach SearchProviders to the Model system and enable easier creation of new search providers. + - Made SearchProviders configurable from `config.json`. - See [0011-configurable-search-providers ADR](./architecture/0011-configurable-search-providers.md) and [Search providers customization](./doc/customizing/search-providers.md) for more details +- Make all icons in `CatalogGroup` black by default and white when a catalog group is focused, selected or hovered over. Improve lock icon position in workbench. #### 8.3.8 - 2023-11-15 From 07cd83e3106f63fee28f2b4b0b37e8c80200adfb Mon Sep 17 00:00:00 2001 From: Nick Forbes-Smith Date: Fri, 24 Nov 2023 17:07:49 +1100 Subject: [PATCH 127/129] Add searchProviders to Ci config --- buildprocess/ci-values.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/buildprocess/ci-values.yml b/buildprocess/ci-values.yml index 682f168b435..fbbd37eba02 100644 --- a/buildprocess/ci-values.yml +++ b/buildprocess/ci-values.yml @@ -46,7 +46,6 @@ terriamap: initializationUrls: - simple parameters: - bingMapsKey: "ApZeR4iLSH_Pl2w5OSXrIqjvc_KzgPn_UXjn6jTtQDiueg72InBThCeIO4OSs6Fq" cesiumIonAccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxZDY5MDE1YS0yNGFlLTQ1MzctYWNkNy0wNDQ1YWNiNTM5MDIiLCJpZCI6Mjk5Miwic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODAzMzgyNn0.a2uTotdiHUo8FiHsO4MVNZ1KT5pOF0rb7CFdwbRAsOo" useCesiumIonBingImagery: true googleAnalyticsKey: @@ -67,6 +66,12 @@ terriamap: mobileDefaultViewerMode: "2d" experimentalFeatures: true feedbackUrl: "feedback" + searchProviders: + - id: search-provider/cesium-ion + type: cesium-ion-search-provider + name: "translate#viewModels.searchLocations" + flightDurationSeconds: 1.5 + minCharacters: 3 languageConfiguration: enabled: true languages: From 04b07fe3d74904980c4dc09aecce964eb5696bab Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Tue, 28 Nov 2023 09:21:00 +1000 Subject: [PATCH 128/129] Update minimum supported node version to v16 - use node v16 in CI actions - add .nvmrc - update doc --- .github/workflows/ci.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/npm-publish.yml | 2 +- .nvmrc | 1 + doc/customizing/cloning-and-building.md | 2 +- package.json | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 .nvmrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f84d346a5a..c4a58d8c3d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: "14.x" + node-version: "16.x" - run: npm install -g yarn@^1.19.0 && yarn install - name: Check formatting with prettier diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9fcea52b7a1..827d5d43855 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: "14.x" + node-version: "16.x" - uses: google-github-actions/setup-gcloud@v0.2.1 with: service_account_key: ${{ secrets.GCP_CREDENTIALS }} diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index cac8a87486c..6d86d81a5da 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -36,7 +36,7 @@ jobs: uses: actions/setup-node@v2 with: registry-url: "https://registry.npmjs.org" - node-version: "14.x" + node-version: "16.x" - name: Install yarn if: steps.detect.outputs.previous-version != steps.detect.outputs.current-version diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000000..2ab3d4be550 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v16.20.2 diff --git a/doc/customizing/cloning-and-building.md b/doc/customizing/cloning-and-building.md index 133711c5a01..4e261d0d96a 100644 --- a/doc/customizing/cloning-and-building.md +++ b/doc/customizing/cloning-and-building.md @@ -21,7 +21,7 @@ If you run into trouble or want more explanation, read on. TerriaJS can be built and run on almost any macOS, Linux, or Windows system. The following are required to build TerriaJS: - The Bash command shell. On macOS or Linux you almost certainly already have this. On Windows, you can easily get it by installing [Git for Windows](https://gitforwindows.org/). In the instructions below, we assume you're using a Bash command prompt. -- [Node.js](https://nodejs.org) v14.0 or later. You can check your node version by running `node --version` on the command-line. +- [Node.js](https://nodejs.org) v16.0 or later. You can check your node version by running `node --version` on the command-line. - [npm](https://www.npmjs.com/) v6.0 or later. npm is usually installed automatically alongside the above. You can check your npm version by running `npm --version`. - [yarn](https://yarnpkg.com/) v1.19.0 or later. This can be installed using `npm install -g yarn@^1.19.0` diff --git a/package.json b/package.json index ae540de8108..e81c7cfd621 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Geospatial data visualization platform.", "license": "Apache-2.0", "engines": { - "node": ">= 14.0.0" + "node": ">= 16.0.0" }, "repository": { "type": "git", From 3ff4b86e8830380d58825dc2e1d39735be74ffcb Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Wed, 29 Nov 2023 09:26:33 +1000 Subject: [PATCH 129/129] Use v4 setup node gh action, rely on .nvmrc for node version - yarn install request for deploy script to avoid peer dependency conflict --- .github/workflows/ci.yml | 4 ++-- .github/workflows/deploy.yml | 4 ++-- .github/workflows/npm-publish.yml | 4 ++-- buildprocess/ci-deploy.sh | 2 +- doc/customizing/cloning-and-building.md | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4a58d8c3d6..2ad165cca4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,9 @@ jobs: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: "16.x" + node-version-file: ".nvmrc" - run: npm install -g yarn@^1.19.0 && yarn install - name: Check formatting with prettier diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 827d5d43855..3728b1f2ced 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,9 +7,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: "16.x" + node-version-file: ".nvmrc" - uses: google-github-actions/setup-gcloud@v0.2.1 with: service_account_key: ${{ secrets.GCP_CREDENTIALS }} diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 6d86d81a5da..2a0a5a9edae 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -33,10 +33,10 @@ jobs: - name: Set up Node.js for NPM if: steps.detect.outputs.previous-version != steps.detect.outputs.current-version - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: registry-url: "https://registry.npmjs.org" - node-version: "16.x" + node-version-file: ".nvmrc" - name: Install yarn if: steps.detect.outputs.previous-version != steps.detect.outputs.current-version diff --git a/buildprocess/ci-deploy.sh b/buildprocess/ci-deploy.sh index 06ff60bc1b0..17853023a67 100644 --- a/buildprocess/ci-deploy.sh +++ b/buildprocess/ci-deploy.sh @@ -18,8 +18,8 @@ gh api /repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA} -f state=pending -f co # Install some tools we need from npm npm install -g https://github.com/terriajs/sync-dependencies -npm install request@^2.83.0 npm install -g yarn@^1.19.0 +yarn add -W request@2.83.0 # Clone and build TerriaMap, using this version of TerriaJS TERRIAJS_COMMIT_HASH=$(git rev-parse HEAD) diff --git a/doc/customizing/cloning-and-building.md b/doc/customizing/cloning-and-building.md index 4e261d0d96a..a80078559dd 100644 --- a/doc/customizing/cloning-and-building.md +++ b/doc/customizing/cloning-and-building.md @@ -21,8 +21,8 @@ If you run into trouble or want more explanation, read on. TerriaJS can be built and run on almost any macOS, Linux, or Windows system. The following are required to build TerriaJS: - The Bash command shell. On macOS or Linux you almost certainly already have this. On Windows, you can easily get it by installing [Git for Windows](https://gitforwindows.org/). In the instructions below, we assume you're using a Bash command prompt. -- [Node.js](https://nodejs.org) v16.0 or later. You can check your node version by running `node --version` on the command-line. -- [npm](https://www.npmjs.com/) v6.0 or later. npm is usually installed automatically alongside the above. You can check your npm version by running `npm --version`. +- [Node.js](https://nodejs.org) v16.0. You can check your node version by running `node --version` on the command-line. +- [npm](https://www.npmjs.com/) v8.0. npm is usually installed automatically alongside the above. You can check your npm version by running `npm --version`. - [yarn](https://yarnpkg.com/) v1.19.0 or later. This can be installed using `npm install -g yarn@^1.19.0` ### Cloning TerriaMap