Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into workbench-type-item
Browse files Browse the repository at this point in the history
  • Loading branch information
nf-s committed Aug 4, 2022
2 parents f6bc184 + 30866c9 commit 61863d1
Show file tree
Hide file tree
Showing 91 changed files with 3,266 additions and 1,961 deletions.
23 changes: 20 additions & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
Change Log
==========

#### next release (8.2.10)
#### next release (8.2.11)

* Added ability to customise the getting started video in the StoryBuilder panel.
* Fixed a bug where menu items were rendered in the wrong style if the window was resized from small to large, or large to small.
* [The next improvement]

#### release 8.2.10 - 2022-08-02

* **Breaking changes:**
* **Minimum NodeJS version is now 14**
Expand All @@ -27,7 +33,19 @@ Change Log
* Limit workbench item title to 2 lines and show overflow: ellipsis after.
* Add `allowFeaturePicking` trait to Cesium3dTileMixin.
* Feature Info now hidden on Cesium3dTiles items if `allowFeaturePicking` set to false. Default is true.
* [The next improvement]
* Add `initFragmentPaths` support for hostnames different to `configUrl`/`applicationUrl`
* Add DOMPurify to `parseCustomHtmlToReact` (it was already present in `parseCustomMarkdownToReact`)
* Update `html-to-react` to `1.4.7`
* Add `ViewState` React context provider to `StandardUserInterface` - instead of passing `viewState` or `terria` props through components, please use
* `useViewState` hook
* `withViewState` HOC
* Move `GlobalTerriaStyles` from `StandardUserInterface` to separate file
* Add `ExternalLinkWithWarning` component - this will replace all URLs in story body and add a warning message when URLs are clicked on.
* Fixed a bug where adding `CesiumTerrainCatalogItem` to workbench didn't apply it when `configParameters.cesiumTerrainAssetId` or `configParameters.cesiumTerrainUrl` was set.
* `CesiumTerrainCatalogItem` will now show a status `In use` or `Not in use` in the workbench.
* Rewrote `CesiumTerrainCatalogItem` to handle and report network errors.
* Set `JulianDate.toIso8601` second precision to nanosecond - this prevents weird date strings with scientific/exponent notation (eg `2008-05-07T22:54:45.7275957614183426e-11Z`)
* Add attribution for Natural Earth II and NASA Black Marble basemaps.

#### 8.2.9 - 2022-07-13

Expand All @@ -39,7 +57,6 @@ Change Log
* Rename `FeatureInfoMixin` to `FeatureInfoUrlTemplateMixin`
* Move `featureInfoTemplate` and `showStringIfPropertyValueIsNull` from `FeatureInfoTraits` to `MappableTraits` (all mappable catalog items)
* Remove `FeatureInfoUrlTemplateTraits` from all models that don't use `FeatureInfoUrlTemplateMixin`
* [The next improvement]
* Fix "Regions: xxx" short report showing for non region mapped items
* Fix `showInChartPanel` default for mappable items

Expand Down
805 changes: 593 additions & 212 deletions doc/acknowledgements/attributions.md

Large diffs are not rendered by default.

31 changes: 25 additions & 6 deletions doc/customizing/client-side-config.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Client-side config

The file `wwwroot/config.json` in TerriaMap contains client-side configuration parameters. See [this file for an example](https://github.com/TerriaJS/TerriaMap/blob/main/wwwroot/config.json).

It has following structure:
Expand All @@ -12,8 +14,8 @@ It has following structure:
```json5
{
"initializationUrls" : [
"myinitfile",
"anotherinitfile"
"https://someurl.com/file.json",
"init-fragment"
],
"parameters": {
"bingMapsKey": "...",
Expand All @@ -22,17 +24,34 @@ It has following structure:
}
```

## intializationUrls
## `intializationUrls`

Each string in the array specifies a single [initialization file](initialization-files.md) (catalog) to be loaded by TerriaJS. The init files are loaded in the order they're specified.

### Init URL

If a string ends with `.json`, it is assumed to be a complete relative or absolute URL to an init file. The file may be on an entirely separate web server, but in that case it must be accessible for [Cross-Origin Resource Sharing (CORS)](../connecting-to-data/cross-origin-resource-sharing.md). It may also be generated by a service rather than being a simple static file. If the URL is relative, it is relative to the config file.

### Init fragment

If the string does not end with `.json`, such as `"foo"`, it refers to an init file on the same web server at `init/foo.json`. In a TerriaMap directory on your computer, it can be found at `wwwroot/init/foo.json`.

### v7initializationUrls
### Init fragment paths

See `parameters.initFragmentPaths` for defining path to Init fragments (defaults to `init/`).

Note: relative paths will be resolved to the base URL of client-side config URL (which defaults to `config.json` - which means the base application URL is used)

It is also possible to add version 7 init files — these will be converted on-the-fly in `terriajs` when a map is loaded. See [`catalog-converter`](https://github.com/TerriaJS/catalog-converter) repo for more information.
For example a map hosted at http://something.com/map

- will have default `configUrl = http://something.com/map/config.json`
- therefore will resolve `initFragmentPaths` to `http://something.com/map`
- if using default `initFragmentPaths = ["init"]`
- init fragments will be resolved to `http://something.com/map/init`

### `v7initializationUrls`

It is also possible to add version 7 init files using the `v7initializationUrls` property — these will be converted on-the-fly in `terriajs` when a map is loaded. See [`catalog-converter`](https://github.com/TerriaJS/catalog-converter) repo for more information. Only v7 Init URLs are supported (v7 Init fragments are not supported).

## Parameters

Expand Down Expand Up @@ -90,7 +109,7 @@ Specifies various options for configuring TerriaJS:
|`theme`|no|**any**|`{}`|An object used to override theme properties - for example `{"logoHeight": "70px"}`.|
|`storyRouteUrlPrefix`|no|**string**|undefined|(Experimental) Prefix to which `:story-id` is added to fetch JSON for stories when using `/story/:story-id` routes. Should end in /|
|`leafletAttributionPrefix`|no|**string**|undefined|Attribution HTML string to show on Leaflet maps. Will use Leaflet's default if undefined. To hide Leaflet attribution - set `leafletAttributionPrefix:""`|

|`storyVideo.videoUrl`|no|**string**|https://www.youtube-nocookie.com/embed/fbiQawV8IYY|Video to show in Story Editor panel under Getting Started.|


### MagdaReferenceHeaders
Expand Down
11 changes: 9 additions & 2 deletions doc/deploying/controlling-with-url-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,16 @@ Parameter | Meaning

### Catalog files (init files)

Any unrecognised parameter (eg `foo`) is treated as the name of a catalog file, loaded from the directory `wwwroot/init/foo.json`). Multiple catalog files can be loaded this way, and will be combined. Later files can override earlier ones.
Any unrecognised parameter (eg `foo` or `http://foo.com/bar.json`) is treated as an "Init Source".
These can be [Init Fragments](../customizing/client-side-config.md#init-fragment) (eg `foo`) or [Init URLs](../customizing/client-side-config.md#init-url) (eg `http://foo.com/bar.json`)

Example: [http://nationalmap.gov.au#test](http://nationalmap.gov.au#test)
[Init fragments](../customizing/client-side-config.md#init-fragment) are resolved using Client-side config [`parameters.initFragmentPaths`](../customizing/client-side-config.md#parameters) (which defaults to `"init/"`). For example, `foo` would become `init/foo.json`

Multiple catalog files can be loaded this way, and will be combined. Files are loaded in order provided.

**Note:** relative URLs (and relative init fragments) in hash are resolved using **base URL of the map** - where as relative URLs in Client-side config [`initializationUrls`](../customizing/client-side-config.md#intializationurls) and resolved using **base URL of config URL**

For example, http://nationalmap.gov.au#test will load init file http://nationalmap.gov.au/init/test.json

### Start data

Expand Down
35 changes: 0 additions & 35 deletions lib/Core/DataUri.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
"use strict";

var defined = require("terriajs-cesium/Source/Core/defined").default;
var FeatureDetection = require("terriajs-cesium/Source/Core/FeatureDetection")
.default;
var TerriaError = require("../Core/TerriaError").default;
const i18next = require("i18next").default;

// Unfortunately there's no way to feature-detect for this, it's something that only MS browsers disallow for security reasons.
var canUseDataUriInHref = !(
FeatureDetection.isInternetExplorer() || /Edge/.exec(navigator.userAgent)
);

var DataUri = {
/**
* Turn a file with the supplied type and stringified data into a data uri that can be set as the href of an anchor tag.
Expand All @@ -23,30 +12,6 @@ var DataUri = {
// Using attachment/* mime type makes safari download as attachment. text/* works on Chrome (as does attachment).
return "data:attachment/" + type + "," + encodeURIComponent(dataString);
}
},

/**
* Returns a flag stating if data uri links are supported by the user's browser.
* If errorEvent is provided, presents an error message explaining why it won't work.
* @param {Error} [errorEvent] A Cesium Event, eg. terria.error, used to raise an error if the browser does not support data download.
* @param {String} [href] The link to provide in the error message. Required if errorEvent is provided.
* @param {Boolean} [forceError] If true, always show the error message. Defaults to false, which only shows it if the browser cannot download uri links.
* @return {Boolean} Returns whether the browser is compatible with data uris.
*/
checkCompatibility: function(errorEvent, href, forceError) {
if (!canUseDataUriInHref || forceError) {
if (defined(errorEvent)) {
errorEvent.raiseEvent(
new TerriaError({
title: i18next.t("core.dataUri.errorTitle"),
message: i18next.t("core.dataUri.errorMessage", { href: href })
})
);
}
return false;
} else {
return true;
}
}
};

Expand Down
20 changes: 14 additions & 6 deletions lib/ModelMixins/DiscretelyTimeVaryingMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import ChartableMixin, {
import CommonStrata from "../Models/Definition/CommonStrata";
import Model from "../Models/Definition/Model";
import DiscretelyTimeVaryingTraits from "../Traits/TraitsClasses/DiscretelyTimeVaryingTraits";
import TimeVarying from "./TimeVarying";
import TimeVarying, { DATE_SECONDS_PRECISION } from "./TimeVarying";

export interface AsJulian {
time: JulianDate;
Expand All @@ -41,7 +41,7 @@ function DiscretelyTimeVaryingMixin<
const time = super.currentTime;
if (time === undefined || time === null) {
if (this.initialTimeSource === "now") {
return JulianDate.toIso8601(JulianDate.now());
return JulianDate.toIso8601(JulianDate.now(), DATE_SECONDS_PRECISION);
} else if (this.initialTimeSource === "start") {
return this.startTime;
} else if (this.initialTimeSource === "stop") {
Expand Down Expand Up @@ -241,7 +241,8 @@ function DiscretelyTimeVaryingMixin<
this.discreteTimesAsSortedJulianDates.length > 0
) {
return JulianDate.toIso8601(
this.discreteTimesAsSortedJulianDates[0].time
this.discreteTimesAsSortedJulianDates[0].time,
DATE_SECONDS_PRECISION
);
}
return time;
Expand All @@ -258,7 +259,8 @@ function DiscretelyTimeVaryingMixin<
return JulianDate.toIso8601(
this.discreteTimesAsSortedJulianDates[
this.discreteTimesAsSortedJulianDates.length - 1
].time
].time,
DATE_SECONDS_PRECISION
);
}
return time;
Expand Down Expand Up @@ -302,7 +304,10 @@ function DiscretelyTimeVaryingMixin<
this.setTrait(
stratumId,
"currentTime",
JulianDate.toIso8601(this.discreteTimesAsSortedJulianDates![index].time)
JulianDate.toIso8601(
this.discreteTimesAsSortedJulianDates![index].time,
DATE_SECONDS_PRECISION
)
);
}

Expand All @@ -315,7 +320,10 @@ function DiscretelyTimeVaryingMixin<
this.setTrait(
stratumId,
"currentTime",
JulianDate.toIso8601(this.discreteTimesAsSortedJulianDates![index].time)
JulianDate.toIso8601(
this.discreteTimesAsSortedJulianDates![index].time,
DATE_SECONDS_PRECISION
)
);
}

Expand Down
5 changes: 5 additions & 0 deletions lib/ModelMixins/TimeVarying.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import JulianDate from "terriajs-cesium/Source/Core/JulianDate";
import Model, { BaseModel } from "../Models/Definition/Model";
import TimeVaryingTraits from "../Traits/TraitsClasses/TimeVaryingTraits";

/** To use as precision in JulianDate.toIso8601(date, precision) - so we don't get scientific/exponent in date string (eg `2008-05-07T22:54:45.7275957614183426e-11Z`).
* Is set to nanosecond precision
*/
export const DATE_SECONDS_PRECISION = 9;

interface TimeVarying extends Model<TimeVaryingTraits> {
readonly currentTimeAsJulianDate: JulianDate | undefined;
readonly startTimeAsJulianDate: JulianDate | undefined;
Expand Down
4 changes: 4 additions & 0 deletions lib/Models/BaseMaps/defaultBaseMaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ export function defaultBaseMaps(terria: Terria): BaseMapJson[] {
type: "url-template-imagery",
url:
"https://storage.googleapis.com/terria-datasets-public/basemaps/natural-earth-tiles/{z}/{x}/{reverseY}.png",
attribution:
"<a href='https://www.naturalearthdata.com/downloads/10m-raster-data/10m-natural-earth-2/'>Natural Earth II</a> - From Natural Earth. <a href='https://www.naturalearthdata.com/about/terms-of-use/'>Public Domain</a>.",
maximumLevel: 7,
opacity: 1.0
},
Expand All @@ -100,6 +102,8 @@ export function defaultBaseMaps(terria: Terria): BaseMapJson[] {
type: "wms",
url:
"http://geoserver.nationalmap.nicta.com.au/imagery/nasa-black-marble/wms",
attribution:
"<a href='https://earthobservatory.nasa.gov/Features/NightLights'>Black Marble</a> - From NASA's Earth Observatory. <a href='https://earthobservatory.nasa.gov/image-use-policy'>Use Policy</a>.",
layers: "nasa-black-marble:dnb_land_ocean_ice.2012.54000x27000_geo",
opacity: 1.0
},
Expand Down
100 changes: 80 additions & 20 deletions lib/Models/Catalog/CatalogItems/CesiumTerrainCatalogItem.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,103 @@
import { computed } from "mobx";
import { action, computed, observable, runInAction } from "mobx";
import CesiumTerrainProvider from "terriajs-cesium/Source/Core/CesiumTerrainProvider";
import IonResource from "terriajs-cesium/Source/Core/IonResource";
import MappableMixin from "../../../ModelMixins/MappableMixin";
import CatalogMemberMixin from "../../../ModelMixins/CatalogMemberMixin";
import MappableMixin from "../../../ModelMixins/MappableMixin";
import UrlMixin from "../../../ModelMixins/UrlMixin";
import CesiumTerrainCatalogItemTraits from "../../../Traits/TraitsClasses/CesiumTerrainCatalogItemTraits";
import CreateModel from "../../Definition/CreateModel";
import TerriaError from "../../../Core/TerriaError";

export default class CesiumTerrainCatalogItem extends UrlMixin(
MappableMixin(CatalogMemberMixin(CreateModel(CesiumTerrainCatalogItemTraits)))
) {
static type = "cesium-terrain";

/**
* An observable terrain provider instance set by forceLoadMapItems()
*/
@observable
private terrainProvider: CesiumTerrainProvider | undefined = undefined;

get type() {
return CesiumTerrainCatalogItem.type;
}

protected forceLoadMapItems(): Promise<void> {
return Promise.resolve();
@computed
get disableZoomTo() {
return true;
}

@computed
get mapItems() {
let resource: string | Promise<IonResource> = this.url!;
if (this.ionAssetId !== undefined) {
resource = IonResource.fromAssetId(this.ionAssetId, {
accessToken:
this.ionAccessToken ||
this.terria.configParameters.cesiumIonAccessToken,
server: this.ionServer
});
// Deal with errors from this better
private get isTerrainActive() {
return this.terria.terrainProvider === this.terrainProvider;
}

@computed
get shortReport() {
if (super.shortReport === undefined) {
const status = this.isTerrainActive ? "In use" : "Not in use";
return `Terrain status: ${status}`;
}
return super.shortReport;
}

return [
new CesiumTerrainProvider({
url: resource,
credit: this.attribution
})
];
/**
* Returns a Promise to load the terrain provider
*/
private async loadTerrainProvider(): Promise<
CesiumTerrainProvider | undefined
> {
const resource =
this.ionAssetId !== undefined
? IonResource.fromAssetId(this.ionAssetId, {
accessToken:
this.ionAccessToken ||
this.terria.configParameters.cesiumIonAccessToken,
server: this.ionServer
})
: this.url;

if (resource === undefined) {
return undefined;
}

const terrainProvider = new CesiumTerrainProvider({
url: resource,
credit: this.attribution
});

// Some network errors are not rejected through readyPromise, so we have to
// listen to them using the error event and dispose it later
let networkErrorListener: (err: any) => void;
const networkErrorPromise = new Promise((_resolve, reject) => {
networkErrorListener = reject;
terrainProvider.errorEvent.addEventListener(networkErrorListener);
});

const isReady = await Promise.race([
networkErrorPromise,
terrainProvider.readyPromise
])
.catch(() => false)
.finally(() =>
terrainProvider.errorEvent.removeEventListener(networkErrorListener)
);

return isReady
? terrainProvider
: Promise.reject(TerriaError.from("Failed to load terrain provider"));
}

protected async forceLoadMapItems(): Promise<void> {
const terrainProvider = await this.loadTerrainProvider();
runInAction(() => {
this.terrainProvider = terrainProvider;
});
}

@computed
get mapItems() {
return this.show && this.terrainProvider ? [this.terrainProvider] : [];
}
}
Loading

0 comments on commit 61863d1

Please sign in to comment.