Skip to content

Commit

Permalink
Merge pull request #7146 from TerriaJS/image-server
Browse files Browse the repository at this point in the history
Add ArcGis ImageServer support
  • Loading branch information
nf-s authored Aug 21, 2024
2 parents e2410aa + 8b8badc commit ed76cc1
Show file tree
Hide file tree
Showing 25 changed files with 2,758 additions and 36 deletions.
9 changes: 8 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
- extract common style logic to new Cesium3dTilesStyleMixin.ts
- Set default value for date and datetime WPS fields only when the field is marked as required.
- Add Proj4 definition for EPSG:8059
- Add support for ArcGis ImageServer - this includes
- Support for "dynamic" `exportImage` endpoint (using `102100` wkid)
- Support for web mercator and wgs84 precached tiles
- Basic support for raster functions - a dropdown is rendered in the workbench for custom raster functions
- Traits to configure `bandIds` and `renderingRule`
- Increase `maxRefreshIntervals` from 1000 to 10000 for `WebMapServiceCatalogItem` and `ArcGisMapServerCatalogItem`.
- Add `nextDiscreteJulianDate` helper computed value to `DiscretelyTimeVaryingMixin`
- Add `EPSG:7899` to `Proj4Definitions`
- [The next improvement]

#### 8.7.5 - 2024-06-26
Expand All @@ -16,7 +24,6 @@
- Show rectangle selector for WPS bounding box parameter
- Fix `store` and `status` values send in WPS Execute request.
- Add docs for `modelDimensions`
- [The next improvement]

#### 8.7.4 - 2024-06-07

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ For example - this renders a new drop down called "Color" that changes the `colo

## Example with Mustache template

Model dimensions also supports the use of [Mustache templates](https://mustache.github.io/) - this means you can refer to other parts of the model JSON in the value of the model dimension. For example, this changes the `colorPalette` trait based on the value of another trait `colorPalette2`:
Model dimensions also supports the use of [Mustache templates](https://mustache.github.io/) - this means you can refer to other parts of the model JSON in the value of the model dimension. For example, this changes the `colorPalette` trait:

```json
{
Expand Down
4 changes: 4 additions & 0 deletions lib/Core/getDataType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const builtinRemoteDataTypes: RemoteDataType[] = [
value: "esri-mapServer",
name: "core.dataType.esri-mapServer"
},
{
value: "esri-imageServer",
name: "core.dataType.esri-imageServer"
},
/* {
value: "esri-mapServer-group",
name: "Esri ArcGIS MapServer"
Expand Down
230 changes: 230 additions & 0 deletions lib/Map/ImageryProvider/ArcGisImageServerImageryProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import { makeObservable } from "mobx";
import Cartesian2 from "terriajs-cesium/Source/Core/Cartesian2";
import Cartographic from "terriajs-cesium/Source/Core/Cartographic";
import Credit from "terriajs-cesium/Source/Core/Credit";
import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid";
import CesiumEvent from "terriajs-cesium/Source/Core/Event";
import GeographicProjection from "terriajs-cesium/Source/Core/GeographicProjection";
import GeographicTilingScheme from "terriajs-cesium/Source/Core/GeographicTilingScheme";
import Math from "terriajs-cesium/Source/Core/Math";
import Rectangle from "terriajs-cesium/Source/Core/Rectangle";
import Resource from "terriajs-cesium/Source/Core/Resource";
import TilingScheme from "terriajs-cesium/Source/Core/TilingScheme";
import DiscardMissingTileImagePolicy from "terriajs-cesium/Source/Scene/DiscardMissingTileImagePolicy";
import ImageryLayerFeatureInfo from "terriajs-cesium/Source/Scene/ImageryLayerFeatureInfo";
import ImageryProvider from "terriajs-cesium/Source/Scene/ImageryProvider";
import TileDiscardPolicy from "terriajs-cesium/Source/Scene/TileDiscardPolicy";
import { JsonObject } from "../../Core/Json";
import { ImageServerIdentifyResult } from "../../Models/Catalog/Esri/ArcGisInterfaces";

interface Options {
url: string;
token?: string;
minimumLevel?: number;
maximumLevel?: number;
rectangle?: Rectangle;
credit: Credit | string;
enablePickFeatures?: boolean;
usePreCachedTiles?: boolean;
tileWidth?: number;
tileHeight?: number;
tilingScheme?: TilingScheme;
parameters?: JsonObject;
}

/** This is adapted from Cesium's ArcGisMapServerImageryProvider
* https://github.com/CesiumGS/cesium/blob/51aae2d21014cfc28e948b1719d07f1912df9434/packages/engine/Source/Scene/ArcGisMapServerImageryProvider.js
* Code licensed under the Apache License v2.0.
* For details, see https://github.com/CesiumGS/cesium/blob/main/LICENSE.md
*/

export default class ArcGisImageServerImageryProvider {
readonly tilingScheme: TilingScheme;
readonly ellipsoid: Ellipsoid;
readonly tileWidth: number;
readonly tileHeight: number;
readonly minimumLevel: number;
readonly maximumLevel: number;
readonly rectangle: Rectangle;
readonly errorEvent = new CesiumEvent();
readonly ready = true;
readonly credit: Credit;

/** Note: this can be set dynamically */
enablePickFeatures: boolean;
readonly usePreCachedTiles: boolean;
readonly tileDiscardPolicy: TileDiscardPolicy;
readonly baseResource: Resource;

readonly defaultNightAlpha = undefined;
readonly defaultDayAlpha = undefined;
readonly hasAlphaChannel = true;
readonly defaultAlpha = undefined;
readonly defaultBrightness = undefined;
readonly defaultContrast = undefined;
readonly defaultGamma = undefined;
readonly defaultHue = undefined;
readonly defaultSaturation = undefined;
readonly defaultMagnificationFilter = undefined;
readonly defaultMinificationFilter = undefined;
readonly readyPromise = Promise.resolve(true);

constructor(options: Options) {
makeObservable(this);

this.tilingScheme = options.tilingScheme ?? new GeographicTilingScheme();

this.rectangle = options.rectangle ?? this.tilingScheme.rectangle;
this.ellipsoid = Ellipsoid.WGS84;

this.tileWidth = options.tileWidth ?? 256;
this.tileHeight = options.tileHeight ?? 256;

this.minimumLevel = options.minimumLevel ?? 0;
this.maximumLevel = options.maximumLevel ?? 25;

this.ready = true;

this.credit =
typeof options.credit === "string"
? new Credit(options.credit)
: options.credit;

this.enablePickFeatures = options.enablePickFeatures ?? true;
this.usePreCachedTiles = options.usePreCachedTiles ?? false;

this.baseResource = new Resource(options.url);
this.baseResource.appendForwardSlash();

if (options.parameters) {
this.baseResource.appendQueryParameters(options.parameters);
}

if (options.token) {
this.baseResource.appendQueryParameters({
token: options.token
});
}

this.tileDiscardPolicy = new DiscardMissingTileImagePolicy({
missingImageUrl: this.buildImageResource(0, 0, this.maximumLevel).url,
pixelsToCheck: [
new Cartesian2(0, 0),
new Cartesian2(200, 20),
new Cartesian2(20, 200),
new Cartesian2(80, 110),
new Cartesian2(160, 130)
],
disableCheckIfAllPixelsAreTransparent: true
});
}

get proxy() {
return this.baseResource.proxy;
}

buildImageResource(x: number, y: number, level: number) {
if (this.usePreCachedTiles) {
return this.baseResource.getDerivedResource({
url: `tile/${level}/${y}/${x}`
});
} else {
const nativeRectangle = this.tilingScheme.tileXYToNativeRectangle(
x,
y,
level
);
const bbox = `${nativeRectangle.west},${nativeRectangle.south},${nativeRectangle.east},${nativeRectangle.north}`;

const query: JsonObject = {
bbox: bbox,
size: `${this.tileWidth},${this.tileHeight}`,
format: "png32",
transparent: true,
f: "image"
};

if (this.tilingScheme.projection instanceof GeographicProjection) {
query.bboxSR = 4326;
query.imageSR = 4326;
} else {
query.bboxSR = 3857;
query.imageSR = 3857;
}

return this.baseResource.getDerivedResource({
url: "exportImage",
queryParameters: query
});
}
}

getTileCredits(): Credit[] {
return [];
}

requestImage(x: number, y: number, level: number): any {
return ImageryProvider.loadImage(
this,
this.buildImageResource(x, y, level)
);
}

async pickFeatures(
x: number,
y: number,
level: number,
longitude: number,
latitude: number
): Promise<ImageryLayerFeatureInfo[]> {
if (!this.enablePickFeatures) {
return [];
}

let horizontal;
let vertical;
let sr;
if (this.tilingScheme.projection instanceof GeographicProjection) {
horizontal = Math.toDegrees(longitude);
vertical = Math.toDegrees(latitude);
sr = "4326";
} else {
const projected = this.tilingScheme.projection.project(
new Cartographic(longitude, latitude, 0.0)
);
horizontal = projected.x;
vertical = projected.y;
sr = "3857";
}

const query = {
f: "json",
geometryType: "esriGeometryPoint",
geometry: `{x: ${horizontal}, y: ${vertical}, spatialReference: {wkid: ${sr}}}`,
// Disable catalog items - as we don't use them
returnCatalogItems: false
};

const resource = this.baseResource.getDerivedResource({
url: "identify",
queryParameters: query
});

const json = (await resource.fetchJson()) as ImageServerIdentifyResult;
const result: ImageryLayerFeatureInfo[] = [];

if (json.value) {
const featureInfo = new ImageryLayerFeatureInfo();
featureInfo.data = json;
featureInfo.name = json.name;
featureInfo.properties = json.properties;
featureInfo.description = json.value;

result.push(featureInfo);
}

// Todo: handle json.catalogItems

return result;
}
}
2 changes: 2 additions & 0 deletions lib/Map/Vector/Proj4Definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const Proj4Definitions: Record<string, string> = {
"EPSG:7844": "+proj=longlat +ellps=GRS80 +no_defs +type=crs",
"EPSG:7855":
"+proj=utm +zone=55 +south +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
"EPSG:7899":
"+proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
"EPSG:8059":
"+proj=lcc +lat_0=-32 +lon_0=135 +lat_1=-28 +lat_2=-36 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs"
};
Expand Down
8 changes: 8 additions & 0 deletions lib/ModelMixins/DiscretelyTimeVaryingMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ function DiscretelyTimeVaryingMixin<
: this.discreteTimesAsSortedJulianDates![index].time;
}

@computed({ equals: JulianDate.equals })
get nextDiscreteJulianDate() {
const index = this.nextDiscreteTimeIndex;
return index === undefined
? undefined
: this.discreteTimesAsSortedJulianDates![index].time;
}

@computed
get currentDiscreteTimeTag() {
const index = this.currentDiscreteTimeIndex;
Expand Down
Loading

0 comments on commit ed76cc1

Please sign in to comment.