@itwin/core-frontend 5.14.0-dev.12 → 5.14.0-dev.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/GeoServices.d.ts +3 -0
- package/lib/cjs/GeoServices.d.ts.map +1 -1
- package/lib/cjs/GeoServices.js +8 -1
- package/lib/cjs/GeoServices.js.map +1 -1
- package/lib/cjs/extension/ExtensionAdmin.d.ts +0 -2
- package/lib/cjs/extension/ExtensionAdmin.d.ts.map +1 -1
- package/lib/cjs/extension/ExtensionAdmin.js +44 -22
- package/lib/cjs/extension/ExtensionAdmin.js.map +1 -1
- package/lib/cjs/extension/providers/RemoteExtensionProvider.d.ts +1 -1
- package/lib/cjs/extension/providers/RemoteExtensionProvider.d.ts.map +1 -1
- package/lib/cjs/extension/providers/RemoteExtensionProvider.js +2 -2
- package/lib/cjs/extension/providers/RemoteExtensionProvider.js.map +1 -1
- package/lib/esm/GeoServices.d.ts +3 -0
- package/lib/esm/GeoServices.d.ts.map +1 -1
- package/lib/esm/GeoServices.js +8 -1
- package/lib/esm/GeoServices.js.map +1 -1
- package/lib/esm/extension/ExtensionAdmin.d.ts +0 -2
- package/lib/esm/extension/ExtensionAdmin.d.ts.map +1 -1
- package/lib/esm/extension/ExtensionAdmin.js +44 -22
- package/lib/esm/extension/ExtensionAdmin.js.map +1 -1
- package/lib/esm/extension/providers/RemoteExtensionProvider.d.ts +1 -1
- package/lib/esm/extension/providers/RemoteExtensionProvider.d.ts.map +1 -1
- package/lib/esm/extension/providers/RemoteExtensionProvider.js +2 -2
- package/lib/esm/extension/providers/RemoteExtensionProvider.js.map +1 -1
- package/package.json +20 -20
package/lib/cjs/GeoServices.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { IModelConnection } from "./IModelConnection";
|
|
|
9
9
|
* @internal exported strictly for tests.
|
|
10
10
|
*/
|
|
11
11
|
export interface CoordinateConverterOptions {
|
|
12
|
+
/** Conversion direction included in error diagnostics. */
|
|
13
|
+
direction: "geoToIModel" | "iModelToGeo";
|
|
12
14
|
isIModelClosed: () => boolean;
|
|
13
15
|
/** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */
|
|
14
16
|
requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;
|
|
@@ -43,6 +45,7 @@ export declare class CoordinateConverter {
|
|
|
43
45
|
protected readonly _maxPointsPerRequest: number;
|
|
44
46
|
protected readonly _isIModelClosed: () => boolean;
|
|
45
47
|
protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;
|
|
48
|
+
private readonly _direction;
|
|
46
49
|
protected _redispatchOnCompletion: boolean;
|
|
47
50
|
get isIdle(): boolean;
|
|
48
51
|
protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeoServices.d.ts","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"AAIA;;GAEG;AAGH,OAAO,EACG,OAAO,EAAE,UAAU,EAAU,WAAW,EACjD,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EACL,0BAA0B,EAAE,2BAA2B,EAAkB,kBAAkB,EAAE,6BAA6B,EAAE,8BAA8B,EAClI,eAAe,EACxC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,cAAc,EAAE,MAAM,OAAO,CAAC;IAC9B,2HAA2H;IAC3H,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAChE,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAUD,KAAK,wBAAwB,GAE3B,MAAM,GAEN,WAAW,GAEX,WAAW,CAAC;AAEd;;;;;;;;;;;;GAYG;AACH,qBAAa,mBAAmB;IAC9B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/D,SAAS,CAAC,MAAM,EAAE,wBAAwB,CAAU;IAEpD,SAAS,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAExC,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAEzC,SAAS,CAAC,YAAY,gBAAqB,IAAI,EAAI;IAEnD,SAAS,CAAC,QAAQ,CAAC,WAAW;;;;MAAwB;IACtD,SAAS,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAChD,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"GeoServices.d.ts","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"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"}
|
package/lib/cjs/GeoServices.js
CHANGED
|
@@ -45,6 +45,7 @@ class CoordinateConverter {
|
|
|
45
45
|
_maxPointsPerRequest;
|
|
46
46
|
_isIModelClosed;
|
|
47
47
|
_requestPoints;
|
|
48
|
+
_direction;
|
|
48
49
|
// If true, [[dispatch]] will schedule another dispatch after it receives a response.
|
|
49
50
|
// This is needed when all the points requested after the most recent dispatch were included in the currently-in-flight request -
|
|
50
51
|
// _pending will be empty but new callers will be awaiting the results of the in-flight request.
|
|
@@ -69,6 +70,7 @@ class CoordinateConverter {
|
|
|
69
70
|
this._maxPointsPerRequest = Math.max(1, opts.maxPointsPerRequest ?? 300);
|
|
70
71
|
this._isIModelClosed = opts.isIModelClosed;
|
|
71
72
|
this._requestPoints = opts.requestPoints;
|
|
73
|
+
this._direction = opts.direction;
|
|
72
74
|
this._cache = new core_bentley_1.Dictionary(compareXYAndZ, cloneXYAndZ);
|
|
73
75
|
this._pending = new core_bentley_1.SortedArray(compareXYAndZ, false, cloneXYAndZ);
|
|
74
76
|
this._inflight = new core_bentley_1.SortedArray(compareXYAndZ, false, cloneXYAndZ);
|
|
@@ -103,7 +105,10 @@ class CoordinateConverter {
|
|
|
103
105
|
this._cache.set(requests[j], results[j]);
|
|
104
106
|
}
|
|
105
107
|
}).catch((err) => {
|
|
106
|
-
core_bentley_1.Logger.logError(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.geoservices`, err)
|
|
108
|
+
core_bentley_1.Logger.logError(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.geoservices`, err, () => ({
|
|
109
|
+
direction: this._direction,
|
|
110
|
+
pointCount: requests.length,
|
|
111
|
+
}));
|
|
107
112
|
});
|
|
108
113
|
promises.push(promise);
|
|
109
114
|
}
|
|
@@ -200,10 +205,12 @@ class GeoConverter {
|
|
|
200
205
|
constructor(opts) {
|
|
201
206
|
const isIModelClosed = opts.isIModelClosed;
|
|
202
207
|
this._geoToIModel = new CoordinateConverter({
|
|
208
|
+
direction: "geoToIModel",
|
|
203
209
|
isIModelClosed,
|
|
204
210
|
requestPoints: async (geoCoords) => opts.toIModelCoords({ source: opts.datum, geoCoords }),
|
|
205
211
|
});
|
|
206
212
|
this._iModelToGeo = new CoordinateConverter({
|
|
213
|
+
direction: "iModelToGeo",
|
|
207
214
|
isIModelClosed,
|
|
208
215
|
requestPoints: async (iModelCoords) => opts.fromIModelCoords({ target: opts.datum, iModelCoords }),
|
|
209
216
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeoServices.js","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module IModelConnection\r\n */\r\n\r\n// cspell:ignore GCRS\r\nimport {\r\n assert, BeEvent, Dictionary, Logger, SortedArray,\r\n} from \"@itwin/core-bentley\";\r\nimport { WritableXYAndZ, XYAndZ, XYZProps } from \"@itwin/core-geometry\";\r\nimport {\r\n GeoCoordinatesRequestProps, GeoCoordinatesResponseProps, GeoCoordStatus, GeographicCRSProps, IModelCoordinatesRequestProps, IModelCoordinatesResponseProps,\r\n IModelReadRpcInterface, PointWithStatus,\r\n} from \"@itwin/core-common\";\r\nimport { IModelConnection } from \"./IModelConnection\";\r\nimport { FrontendLoggerCategory } from \"./common/FrontendLoggerCategory\";\r\n\r\n/** Options used to create a [[CoordinateConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface CoordinateConverterOptions {\r\n isIModelClosed: () => boolean;\r\n /** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */\r\n requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n /** Maximum number of points to include in each request. Default: 300. */\r\n maxPointsPerRequest?: number;\r\n}\r\n\r\nfunction compareXYAndZ(lhs: XYAndZ, rhs: XYAndZ): number {\r\n return lhs.x - rhs.x || lhs.y - rhs.y || lhs.z - rhs.z;\r\n}\r\n\r\nfunction cloneXYAndZ(xyz: XYAndZ): XYAndZ {\r\n return { x: xyz.x, y: xyz.y, z: xyz.z };\r\n}\r\n\r\ntype CoordinateConverterState =\r\n // No pending requests.\r\n \"idle\" |\r\n // We have scheduled a requestAnimationFrame to dispatch all pending requests.\r\n \"scheduled\" |\r\n // We have dispatched all requests that were pending at the most recent requestAnimationFrame callback.\r\n \"in-flight\";\r\n\r\n/** Performs conversion of coordinates from one coordinate system to another.\r\n * A [[GeoConverter]] has a pair of these for converting between iModel coordinates and geographic coordinates.\r\n * Uses a cache to avoid repeatedly requesting the same points, and a batching strategy to avoid making frequent small requests.\r\n * The cache stores every point that was ever converted by [[convert]]. It is currently permitted to grow to unbounded size.\r\n * The batching works as follows:\r\n * When a conversion is requested via [[convert]], if all the requested points are in the cache, they are returned immediately.\r\n * Otherwise, any points not in the cache and not in the current in-flight request (if any) are placed onto the queue of pending requests.\r\n * A pending request is scheduled if one hasn't already been scheduled, via requestAnimationFrame.\r\n * In the animation frame callback, the pending requests are split into batches of no more than options.maxPointsPerRequest and dispatched to the backend.\r\n * Once the response is received, the results are loaded into and returned from the cache.\r\n * If more calls to convert occurred while the request was in flight, another request is dispatched.\r\n * @internal exported strictly for tests.\r\n */\r\nexport class CoordinateConverter {\r\n protected readonly _cache: Dictionary<XYAndZ, PointWithStatus>;\r\n protected _state: CoordinateConverterState = \"idle\";\r\n // The accumulated set of points to be converted by the next request.\r\n protected _pending: SortedArray<XYAndZ>;\r\n // The set of points that were included in the current in-flight request, if any.\r\n protected _inflight: SortedArray<XYAndZ>;\r\n // An event fired when the next request completes.\r\n protected _onCompleted = new BeEvent<() => void>();\r\n // Used for creating cache keys (XYAndZ) from XYZProps without having to allocate temporary objects.\r\n protected readonly _scratchXYZ = { x: 0, y: 0, z: 0 };\r\n protected readonly _maxPointsPerRequest: number;\r\n protected readonly _isIModelClosed: () => boolean;\r\n protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n // If true, [[dispatch]] will schedule another dispatch after it receives a response.\r\n // This is needed when all the points requested after the most recent dispatch were included in the currently-in-flight request -\r\n // _pending will be empty but new callers will be awaiting the results of the in-flight request.\r\n protected _redispatchOnCompletion = false;\r\n\r\n public get isIdle(): boolean {\r\n return \"idle\" === this._state;\r\n }\r\n\r\n protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ {\r\n if (Array.isArray(input)) {\r\n output.x = input[0] ?? 0;\r\n output.y = input[1] ?? 0;\r\n output.z = input[2] ?? 0;\r\n } else {\r\n output.x = input.x ?? 0;\r\n output.y = input.y ?? 0;\r\n output.z = input.z ?? 0;\r\n }\r\n\r\n return output;\r\n }\r\n\r\n public constructor(opts: CoordinateConverterOptions) {\r\n this._maxPointsPerRequest = Math.max(1, opts.maxPointsPerRequest ?? 300);\r\n this._isIModelClosed = opts.isIModelClosed;\r\n this._requestPoints = opts.requestPoints;\r\n\r\n this._cache = new Dictionary<XYAndZ, PointWithStatus>(compareXYAndZ, cloneXYAndZ);\r\n this._pending = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n this._inflight = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n }\r\n\r\n protected async dispatch(): Promise<void> {\r\n assert(this._state === \"scheduled\");\r\n if (this._isIModelClosed() || this._pending.isEmpty) {\r\n this._state = \"idle\";\r\n this._onCompleted.raiseEvent();\r\n return;\r\n }\r\n\r\n this._state = \"in-flight\";\r\n\r\n // Ensure subsequently-enqueued requests listen for the *next* response to be received.\r\n const onCompleted = this._onCompleted;\r\n this._onCompleted = new BeEvent<() => void>();\r\n\r\n // Pending requests are now in flight. Start a new list of pending requests. It's cheaper to swap than to allocate new objects.\r\n const inflight = this._pending;\r\n this._pending = this._inflight;\r\n assert(this._pending.isEmpty);\r\n this._inflight = inflight;\r\n\r\n // Split requests if necessary to avoid requesting more than the maximum allowed number of points.\r\n const promises: Array<Promise<void>> = [];\r\n for (let i = 0; i < inflight.length; i += this._maxPointsPerRequest) {\r\n const requests = inflight.slice(i, i + this._maxPointsPerRequest).extractArray();\r\n const promise = this._requestPoints(requests).then((results) => {\r\n if (this._isIModelClosed())\r\n return;\r\n\r\n if (results.length !== requests.length)\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, `requested conversion of ${requests.length} points, but received ${results.length} points`);\r\n\r\n for (let j = 0; j < results.length; j++) {\r\n if (j < requests.length)\r\n this._cache.set(requests[j], results[j]);\r\n }\r\n }).catch((err) => {\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, err);\r\n });\r\n\r\n promises.push(promise);\r\n }\r\n\r\n await Promise.all(promises);\r\n\r\n assert(this._state === \"in-flight\");\r\n this._state = \"idle\";\r\n this._inflight.clear();\r\n\r\n // If any more pending conversions arrived while awaiting this request, schedule another request.\r\n if (!this._pending.isEmpty || this._redispatchOnCompletion) {\r\n this._redispatchOnCompletion = false;\r\n this.scheduleDispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n }\r\n\r\n // Resolve promises of all callers who were awaiting this request.\r\n onCompleted.raiseEvent();\r\n }\r\n\r\n // Add any points not present in cache to pending request list.\r\n // Return the number of points present in cache.\r\n protected enqueue(points: XYZProps[]): number {\r\n let numInCache = 0;\r\n for (const point of points) {\r\n const xyz = this.toXYAndZ(point, this._scratchXYZ);\r\n if (this._cache.get(xyz))\r\n ++numInCache;\r\n else if (this._inflight.contains(xyz))\r\n this._redispatchOnCompletion = true;\r\n else\r\n this._pending.insert(xyz);\r\n }\r\n\r\n return numInCache;\r\n }\r\n\r\n // Obtain converted points from the cache. The assumption is that every point in `inputs` is already present in the cache.\r\n // Any point not present will be returned unconverted with an error status.\r\n protected getFromCache(inputs: XYZProps[]): PointWithStatus[] {\r\n const outputs: PointWithStatus[] = [];\r\n for (const input of inputs) {\r\n const xyz = this.toXYAndZ(input, this._scratchXYZ);\r\n let output = this._cache.get(xyz);\r\n if (!output)\r\n output = { p: { ...xyz }, s: GeoCoordStatus.CSMapError };\r\n\r\n outputs.push(output);\r\n }\r\n\r\n return outputs;\r\n }\r\n\r\n protected async scheduleDispatch(): Promise<void> {\r\n if (\"idle\" === this._state) {\r\n this._state = \"scheduled\";\r\n requestAnimationFrame(() => {\r\n this.dispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n });\r\n }\r\n\r\n return new Promise((resolve) => {\r\n this._onCompleted.addOnce(() => resolve());\r\n });\r\n }\r\n\r\n public async convert(inputs: XYZProps[]): Promise<{ points: PointWithStatus[], fromCache: number }> {\r\n const fromCache = this.enqueue(inputs);\r\n assert(fromCache >= 0);\r\n assert(fromCache <= inputs.length);\r\n\r\n if (fromCache === inputs.length)\r\n return { points: this.getFromCache(inputs), fromCache };\r\n\r\n await this.scheduleDispatch();\r\n\r\n return { points: this.getFromCache(inputs), fromCache };\r\n }\r\n\r\n public findCached(inputs: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n const result: Array<PointWithStatus | undefined> = [];\r\n let missing: XYZProps[] | undefined;\r\n for (const input of inputs) {\r\n const key = this.toXYAndZ(input, this._scratchXYZ);\r\n const output = this._cache.get(key);\r\n result.push(output);\r\n if (!output) {\r\n if (!missing)\r\n missing = [];\r\n\r\n missing.push(input);\r\n }\r\n }\r\n\r\n return { result, missing };\r\n }\r\n}\r\n\r\n/** Response to a request to obtain imodel coordinates from cache.\r\n * @internal\r\n */\r\nexport interface CachedIModelCoordinatesResponseProps {\r\n /** An array of the same length as the input array, with undefined entries at indices corresponding to points not found in cache. */\r\n result: Array<PointWithStatus | undefined>;\r\n /** An array of points in the input array which were not found in the cache, or undefined if all points were found in the cache. */\r\n missing?: XYZProps[];\r\n}\r\n\r\n/** Options used to create a [[GeoConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface GeoConverterOptions {\r\n readonly datum: string;\r\n isIModelClosed: () => boolean;\r\n toIModelCoords: (request: IModelCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n fromIModelCoords: (request: GeoCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n}\r\n\r\n/** An object capable of communicating with the backend to convert between coordinates in a geographic coordinate system and coordinates in an [[IModelConnection]]'s own coordinate system.\r\n * @see [[GeoServices.getConverter]] to obtain a converter.\r\n * @see [GeographicCRS]($common) for more information about geographic coordinate reference systems.\r\n * @public\r\n */\r\nexport class GeoConverter {\r\n private readonly _geoToIModel: CoordinateConverter;\r\n private readonly _iModelToGeo: CoordinateConverter;\r\n /** Used for removing this converter from GeoServices' cache after all requests are completed.\r\n * @internal\r\n */\r\n public readonly onAllRequestsCompleted = new BeEvent<() => void>();\r\n\r\n /** @internal */\r\n constructor(opts: GeoConverterOptions) {\r\n const isIModelClosed = opts.isIModelClosed;\r\n this._geoToIModel = new CoordinateConverter({\r\n isIModelClosed,\r\n requestPoints: async (geoCoords: XYAndZ[]) => opts.toIModelCoords({ source: opts.datum, geoCoords }),\r\n });\r\n\r\n this._iModelToGeo = new CoordinateConverter({\r\n isIModelClosed,\r\n requestPoints: async (iModelCoords: XYAndZ[]) => opts.fromIModelCoords({ target: opts.datum, iModelCoords }),\r\n });\r\n }\r\n\r\n /** Convert the specified geographic coordinates into iModel coordinates. */\r\n public async convertToIModelCoords(geoPoints: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getIModelCoordinatesFromGeoCoordinates(geoPoints);\r\n return result.iModelCoords;\r\n }\r\n\r\n /** Convert the specified iModel coordinates into geographic coordinates. */\r\n public async convertFromIModelCoords(iModelCoords: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getGeoCoordinatesFromIModelCoordinates(iModelCoords);\r\n return result.geoCoords;\r\n }\r\n\r\n /** @internal */\r\n public async getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise<IModelCoordinatesResponseProps> {\r\n const result = await this._geoToIModel.convert(geoPoints);\r\n this.checkCompletion();\r\n return {\r\n iModelCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n /** @internal */\r\n public async getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise<GeoCoordinatesResponseProps> {\r\n const result = await this._iModelToGeo.convert(iModelPoints);\r\n this.checkCompletion();\r\n return {\r\n geoCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n private checkCompletion(): void {\r\n if (this._geoToIModel.isIdle && this._iModelToGeo.isIdle)\r\n this.onAllRequestsCompleted.raiseEvent();\r\n }\r\n\r\n /** @internal */\r\n public getCachedIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n return this._geoToIModel.findCached(geoPoints);\r\n }\r\n}\r\n\r\n/** @internal */\r\nexport type GeoServicesOptions = Omit<GeoConverterOptions, \"datum\">;\r\n\r\n/** The Geographic Services available for an [[IModelConnection]].\r\n * @see [[IModelConnection.geoServices]] to obtain the GeoServices for a specific iModel.\r\n * @public\r\n */\r\nexport class GeoServices {\r\n private readonly _options: GeoServicesOptions;\r\n /** Each GeoConverter has its own independent request queue and cache of previously-converted points.\r\n * Some callers like RealityTileTree obtain a single GeoConverter and reuse it throughout their own lifetime. Therefore they benefit from both batching and caching, and\r\n * the cache gets deleted once the RealityTileTree becomes disused.\r\n *\r\n * Other callers like IModelConnection.spatialToCartographic obtain a new GeoConverter every time they need one, use it to convert a single point(!), and then discard the converter.\r\n * This entirely prevents batching - e.g., calling spatialToCartographic 20 times in one frame results in 20 http requests.\r\n * To address that, we cache each GeoConverter returned by getConverter until it has converted at least one point and has no further outstanding conversion requests.\r\n * In this way, the converter lives for as long as (and no longer than) any caller is awaiting conversion to/from its datum - it and its cache are deleted once it becomes disused.\r\n * This makes the coordinate caching generally less useful, but at least bounded - and maximizes batching of requests.\r\n */\r\n private readonly _cache = new Map<string, GeoConverter>();\r\n\r\n /** @internal */\r\n public constructor(options: GeoServicesOptions) {\r\n this._options = options;\r\n }\r\n\r\n /** @internal */\r\n public static createForIModel(iModel: IModelConnection): GeoServices {\r\n return new GeoServices({\r\n isIModelClosed: () => iModel.isClosed,\r\n toIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getIModelCoordinatesFromGeoCoordinates(iModel.getRpcProps(), request);\r\n return response.iModelCoords;\r\n },\r\n fromIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getGeoCoordinatesFromIModelCoordinates(iModel.getRpcProps(), request);\r\n return response.geoCoords;\r\n },\r\n });\r\n }\r\n\r\n /** Obtain a converter that can convert between a geographic coordinate system and the iModel's own coordinate system.\r\n * @param datumOrGCRS The name or JSON representation of the geographic coordinate system datum - for example, \"WGS84\".\r\n * @returns a converter, or `undefined` if the iModel is not open.\r\n * @note A [[BlankConnection]] has no connection to a backend, so it is never \"open\"; therefore it always returns `undefined`.\r\n */\r\n public getConverter(datumOrGCRS?: string | GeographicCRSProps): GeoConverter | undefined {\r\n if (this._options.isIModelClosed())\r\n return undefined;\r\n\r\n const datum = (typeof datumOrGCRS === \"object\" ? JSON.stringify(datumOrGCRS) : datumOrGCRS) ?? \"\";\r\n\r\n let converter = this._cache.get(datum);\r\n if (!converter) {\r\n converter = new GeoConverter({ ...this._options, datum });\r\n this._cache.set(datum, converter);\r\n\r\n converter.onAllRequestsCompleted.addOnce(() => {\r\n if (converter === this._cache.get(datum))\r\n this._cache.delete(datum);\r\n });\r\n }\r\n\r\n return converter;\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"GeoServices.js","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;AAC/F;;GAEG;;;AAEH,qBAAqB;AACrB,sDAE6B;AAE7B,oDAG4B;AAE5B,4EAAyE;AAezE,SAAS,aAAa,CAAC,GAAW,EAAE,GAAW;IAC7C,OAAO,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;AAC1C,CAAC;AAUD;;;;;;;;;;;;GAYG;AACH,MAAa,mBAAmB;IACX,MAAM,CAAsC;IACrD,MAAM,GAA6B,MAAM,CAAC;IACpD,qEAAqE;IAC3D,QAAQ,CAAsB;IACxC,iFAAiF;IACvE,SAAS,CAAsB;IACzC,kDAAkD;IACxC,YAAY,GAAG,IAAI,sBAAO,EAAc,CAAC;IACnD,oGAAoG;IACjF,WAAW,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IACnC,oBAAoB,CAAS;IAC7B,eAAe,CAAgB;IAC/B,cAAc,CAAmD;IACnE,UAAU,CAA0C;IACrE,qFAAqF;IACrF,iIAAiI;IACjI,gGAAgG;IACtF,uBAAuB,GAAG,KAAK,CAAC;IAE1C,IAAW,MAAM;QACf,OAAO,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC;IAChC,CAAC;IAES,QAAQ,CAAC,KAAe,EAAE,MAAsB;QACxD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzB,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzB,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;YACxB,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;YACxB,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,YAAmB,IAAgC;QACjD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC;QACzE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC;QAC3C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC;QACzC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QAEjC,IAAI,CAAC,MAAM,GAAG,IAAI,yBAAU,CAA0B,aAAa,EAAE,WAAW,CAAC,CAAC;QAClF,IAAI,CAAC,QAAQ,GAAG,IAAI,0BAAW,CAAS,aAAa,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QAC3E,IAAI,CAAC,SAAS,GAAG,IAAI,0BAAW,CAAS,aAAa,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IAC9E,CAAC;IAES,KAAK,CAAC,QAAQ;QACtB,IAAA,qBAAM,EAAC,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,eAAe,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACpD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YACrB,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC;QAE1B,uFAAuF;QACvF,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;QACtC,IAAI,CAAC,YAAY,GAAG,IAAI,sBAAO,EAAc,CAAC;QAE9C,+HAA+H;QAC/H,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAC/B,IAAA,qBAAM,EAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAE1B,kGAAkG;QAClG,MAAM,QAAQ,GAAyB,EAAE,CAAC;QAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACpE,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,oBAAoB,CAAC,CAAC,YAAY,EAAE,CAAC;YACjF,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7D,IAAI,IAAI,CAAC,eAAe,EAAE;oBACxB,OAAO;gBAET,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;oBACpC,qBAAM,CAAC,QAAQ,CAAC,GAAG,+CAAsB,CAAC,OAAO,cAAc,EAAE,2BAA2B,QAAQ,CAAC,MAAM,yBAAyB,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC;gBAE/J,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACxC,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM;wBACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACf,qBAAM,CAAC,QAAQ,CAAC,GAAG,+CAAsB,CAAC,OAAO,cAAc,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;oBAC3E,SAAS,EAAE,IAAI,CAAC,UAAU;oBAC1B,UAAU,EAAE,QAAQ,CAAC,MAAM;iBAC5B,CAAC,CAAC,CAAC;YACN,CAAC,CAAC,CAAC;YAEH,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;QAED,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE5B,IAAA,qBAAM,EAAC,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QAEvB,iGAAiG;QACjG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAC3D,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC;YACrC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,8DAA8D;QACzF,CAAC;QAED,kEAAkE;QAClE,WAAW,CAAC,UAAU,EAAE,CAAC;IAC3B,CAAC;IAED,+DAA+D;IAC/D,gDAAgD;IACtC,OAAO,CAAC,MAAkB;QAClC,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACnD,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;gBACtB,EAAE,UAAU,CAAC;iBACV,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;gBACnC,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;;gBAEpC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,0HAA0H;IAC1H,2EAA2E;IACjE,YAAY,CAAC,MAAkB;QACvC,MAAM,OAAO,GAAsB,EAAE,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACnD,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM;gBACT,MAAM,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,CAAC,EAAE,4BAAc,CAAC,UAAU,EAAE,CAAC;YAE3D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAES,KAAK,CAAC,gBAAgB;QAC9B,IAAI,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC;YAC1B,qBAAqB,CAAC,GAAG,EAAE;gBACzB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,8DAA8D;YACjF,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,MAAkB;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvC,IAAA,qBAAM,EAAC,SAAS,IAAI,CAAC,CAAC,CAAC;QACvB,IAAA,qBAAM,EAAC,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;QAEnC,IAAI,SAAS,KAAK,MAAM,CAAC,MAAM;YAC7B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;QAE1D,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE9B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1D,CAAC;IAEM,UAAU,CAAC,MAAkB;QAClC,MAAM,MAAM,GAAuC,EAAE,CAAC;QACtD,IAAI,OAA+B,CAAC;QACpC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpB,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAI,CAAC,OAAO;oBACV,OAAO,GAAG,EAAE,CAAC;gBAEf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC7B,CAAC;CACF;AA1LD,kDA0LC;AAsBD;;;;GAIG;AACH,MAAa,YAAY;IACN,YAAY,CAAsB;IAClC,YAAY,CAAsB;IACnD;;OAEG;IACa,sBAAsB,GAAG,IAAI,sBAAO,EAAc,CAAC;IAEnE,gBAAgB;IAChB,YAAY,IAAyB;QACnC,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC3C,IAAI,CAAC,YAAY,GAAG,IAAI,mBAAmB,CAAC;YAC1C,SAAS,EAAE,aAAa;YACxB,cAAc;YACd,aAAa,EAAE,KAAK,EAAE,SAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;SACrG,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,GAAG,IAAI,mBAAmB,CAAC;YAC1C,SAAS,EAAE,aAAa;YACxB,cAAc;YACd,aAAa,EAAE,KAAK,EAAE,YAAsB,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC;SAC7G,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IACrE,KAAK,CAAC,qBAAqB,CAAC,SAAqB;QACtD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sCAAsC,CAAC,SAAS,CAAC,CAAC;QAC5E,OAAO,MAAM,CAAC,YAAY,CAAC;IAC7B,CAAC;IAED,4EAA4E;IACrE,KAAK,CAAC,uBAAuB,CAAC,YAAwB;QAC3D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sCAAsC,CAAC,YAAY,CAAC,CAAC;QAC/E,OAAO,MAAM,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,gBAAgB;IACT,KAAK,CAAC,sCAAsC,CAAC,SAAqB;QACvE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,MAAM;YAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC;IACJ,CAAC;IAED,gBAAgB;IACT,KAAK,CAAC,sCAAsC,CAAC,YAAwB;QAC1E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC7D,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,OAAO;YACL,SAAS,EAAE,MAAM,CAAC,MAAM;YACxB,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC;IACJ,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM;YACtD,IAAI,CAAC,sBAAsB,CAAC,UAAU,EAAE,CAAC;IAC7C,CAAC;IAED,gBAAgB;IACT,4CAA4C,CAAC,SAAqB;QACvE,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACjD,CAAC;CACF;AAjED,oCAiEC;AAKD;;;GAGG;AACH,MAAa,WAAW;IACL,QAAQ,CAAqB;IAC9C;;;;;;;;;OASG;IACc,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAE1D,gBAAgB;IAChB,YAAmB,OAA2B;QAC5C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,gBAAgB;IACT,MAAM,CAAC,eAAe,CAAC,MAAwB;QACpD,OAAO,IAAI,WAAW,CAAC;YACrB,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ;YACrC,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAChC,MAAM,GAAG,GAAG,oCAAsB,CAAC,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACpF,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,sCAAsC,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;gBACjG,OAAO,QAAQ,CAAC,YAAY,CAAC;YAC/B,CAAC;YACD,gBAAgB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAClC,MAAM,GAAG,GAAG,oCAAsB,CAAC,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACpF,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,sCAAsC,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;gBACjG,OAAO,QAAQ,CAAC,SAAS,CAAC;YAC5B,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACI,YAAY,CAAC,WAAyC;QAC3D,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE;YAChC,OAAO,SAAS,CAAC;QAEnB,MAAM,KAAK,GAAG,CAAC,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QAElG,IAAI,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,SAAS,GAAG,IAAI,YAAY,CAAC,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YAElC,SAAS,CAAC,sBAAsB,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC5C,IAAI,SAAS,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;oBACtC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AA5DD,kCA4DC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module IModelConnection\r\n */\r\n\r\n// cspell:ignore GCRS\r\nimport {\r\n assert, BeEvent, Dictionary, Logger, SortedArray,\r\n} from \"@itwin/core-bentley\";\r\nimport { WritableXYAndZ, XYAndZ, XYZProps } from \"@itwin/core-geometry\";\r\nimport {\r\n GeoCoordinatesRequestProps, GeoCoordinatesResponseProps, GeoCoordStatus, GeographicCRSProps, IModelCoordinatesRequestProps, IModelCoordinatesResponseProps,\r\n IModelReadRpcInterface, PointWithStatus,\r\n} from \"@itwin/core-common\";\r\nimport { IModelConnection } from \"./IModelConnection\";\r\nimport { FrontendLoggerCategory } from \"./common/FrontendLoggerCategory\";\r\n\r\n/** Options used to create a [[CoordinateConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface CoordinateConverterOptions {\r\n /** Conversion direction included in error diagnostics. */\r\n direction: \"geoToIModel\" | \"iModelToGeo\";\r\n isIModelClosed: () => boolean;\r\n /** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */\r\n requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n /** Maximum number of points to include in each request. Default: 300. */\r\n maxPointsPerRequest?: number;\r\n}\r\n\r\nfunction compareXYAndZ(lhs: XYAndZ, rhs: XYAndZ): number {\r\n return lhs.x - rhs.x || lhs.y - rhs.y || lhs.z - rhs.z;\r\n}\r\n\r\nfunction cloneXYAndZ(xyz: XYAndZ): XYAndZ {\r\n return { x: xyz.x, y: xyz.y, z: xyz.z };\r\n}\r\n\r\ntype CoordinateConverterState =\r\n // No pending requests.\r\n \"idle\" |\r\n // We have scheduled a requestAnimationFrame to dispatch all pending requests.\r\n \"scheduled\" |\r\n // We have dispatched all requests that were pending at the most recent requestAnimationFrame callback.\r\n \"in-flight\";\r\n\r\n/** Performs conversion of coordinates from one coordinate system to another.\r\n * A [[GeoConverter]] has a pair of these for converting between iModel coordinates and geographic coordinates.\r\n * Uses a cache to avoid repeatedly requesting the same points, and a batching strategy to avoid making frequent small requests.\r\n * The cache stores every point that was ever converted by [[convert]]. It is currently permitted to grow to unbounded size.\r\n * The batching works as follows:\r\n * When a conversion is requested via [[convert]], if all the requested points are in the cache, they are returned immediately.\r\n * Otherwise, any points not in the cache and not in the current in-flight request (if any) are placed onto the queue of pending requests.\r\n * A pending request is scheduled if one hasn't already been scheduled, via requestAnimationFrame.\r\n * In the animation frame callback, the pending requests are split into batches of no more than options.maxPointsPerRequest and dispatched to the backend.\r\n * Once the response is received, the results are loaded into and returned from the cache.\r\n * If more calls to convert occurred while the request was in flight, another request is dispatched.\r\n * @internal exported strictly for tests.\r\n */\r\nexport class CoordinateConverter {\r\n protected readonly _cache: Dictionary<XYAndZ, PointWithStatus>;\r\n protected _state: CoordinateConverterState = \"idle\";\r\n // The accumulated set of points to be converted by the next request.\r\n protected _pending: SortedArray<XYAndZ>;\r\n // The set of points that were included in the current in-flight request, if any.\r\n protected _inflight: SortedArray<XYAndZ>;\r\n // An event fired when the next request completes.\r\n protected _onCompleted = new BeEvent<() => void>();\r\n // Used for creating cache keys (XYAndZ) from XYZProps without having to allocate temporary objects.\r\n protected readonly _scratchXYZ = { x: 0, y: 0, z: 0 };\r\n protected readonly _maxPointsPerRequest: number;\r\n protected readonly _isIModelClosed: () => boolean;\r\n protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n private readonly _direction: CoordinateConverterOptions[\"direction\"];\r\n // If true, [[dispatch]] will schedule another dispatch after it receives a response.\r\n // This is needed when all the points requested after the most recent dispatch were included in the currently-in-flight request -\r\n // _pending will be empty but new callers will be awaiting the results of the in-flight request.\r\n protected _redispatchOnCompletion = false;\r\n\r\n public get isIdle(): boolean {\r\n return \"idle\" === this._state;\r\n }\r\n\r\n protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ {\r\n if (Array.isArray(input)) {\r\n output.x = input[0] ?? 0;\r\n output.y = input[1] ?? 0;\r\n output.z = input[2] ?? 0;\r\n } else {\r\n output.x = input.x ?? 0;\r\n output.y = input.y ?? 0;\r\n output.z = input.z ?? 0;\r\n }\r\n\r\n return output;\r\n }\r\n\r\n public constructor(opts: CoordinateConverterOptions) {\r\n this._maxPointsPerRequest = Math.max(1, opts.maxPointsPerRequest ?? 300);\r\n this._isIModelClosed = opts.isIModelClosed;\r\n this._requestPoints = opts.requestPoints;\r\n this._direction = opts.direction;\r\n\r\n this._cache = new Dictionary<XYAndZ, PointWithStatus>(compareXYAndZ, cloneXYAndZ);\r\n this._pending = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n this._inflight = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n }\r\n\r\n protected async dispatch(): Promise<void> {\r\n assert(this._state === \"scheduled\");\r\n if (this._isIModelClosed() || this._pending.isEmpty) {\r\n this._state = \"idle\";\r\n this._onCompleted.raiseEvent();\r\n return;\r\n }\r\n\r\n this._state = \"in-flight\";\r\n\r\n // Ensure subsequently-enqueued requests listen for the *next* response to be received.\r\n const onCompleted = this._onCompleted;\r\n this._onCompleted = new BeEvent<() => void>();\r\n\r\n // Pending requests are now in flight. Start a new list of pending requests. It's cheaper to swap than to allocate new objects.\r\n const inflight = this._pending;\r\n this._pending = this._inflight;\r\n assert(this._pending.isEmpty);\r\n this._inflight = inflight;\r\n\r\n // Split requests if necessary to avoid requesting more than the maximum allowed number of points.\r\n const promises: Array<Promise<void>> = [];\r\n for (let i = 0; i < inflight.length; i += this._maxPointsPerRequest) {\r\n const requests = inflight.slice(i, i + this._maxPointsPerRequest).extractArray();\r\n const promise = this._requestPoints(requests).then((results) => {\r\n if (this._isIModelClosed())\r\n return;\r\n\r\n if (results.length !== requests.length)\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, `requested conversion of ${requests.length} points, but received ${results.length} points`);\r\n\r\n for (let j = 0; j < results.length; j++) {\r\n if (j < requests.length)\r\n this._cache.set(requests[j], results[j]);\r\n }\r\n }).catch((err) => {\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, err, () => ({\r\n direction: this._direction,\r\n pointCount: requests.length,\r\n }));\r\n });\r\n\r\n promises.push(promise);\r\n }\r\n\r\n await Promise.all(promises);\r\n\r\n assert(this._state === \"in-flight\");\r\n this._state = \"idle\";\r\n this._inflight.clear();\r\n\r\n // If any more pending conversions arrived while awaiting this request, schedule another request.\r\n if (!this._pending.isEmpty || this._redispatchOnCompletion) {\r\n this._redispatchOnCompletion = false;\r\n this.scheduleDispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n }\r\n\r\n // Resolve promises of all callers who were awaiting this request.\r\n onCompleted.raiseEvent();\r\n }\r\n\r\n // Add any points not present in cache to pending request list.\r\n // Return the number of points present in cache.\r\n protected enqueue(points: XYZProps[]): number {\r\n let numInCache = 0;\r\n for (const point of points) {\r\n const xyz = this.toXYAndZ(point, this._scratchXYZ);\r\n if (this._cache.get(xyz))\r\n ++numInCache;\r\n else if (this._inflight.contains(xyz))\r\n this._redispatchOnCompletion = true;\r\n else\r\n this._pending.insert(xyz);\r\n }\r\n\r\n return numInCache;\r\n }\r\n\r\n // Obtain converted points from the cache. The assumption is that every point in `inputs` is already present in the cache.\r\n // Any point not present will be returned unconverted with an error status.\r\n protected getFromCache(inputs: XYZProps[]): PointWithStatus[] {\r\n const outputs: PointWithStatus[] = [];\r\n for (const input of inputs) {\r\n const xyz = this.toXYAndZ(input, this._scratchXYZ);\r\n let output = this._cache.get(xyz);\r\n if (!output)\r\n output = { p: { ...xyz }, s: GeoCoordStatus.CSMapError };\r\n\r\n outputs.push(output);\r\n }\r\n\r\n return outputs;\r\n }\r\n\r\n protected async scheduleDispatch(): Promise<void> {\r\n if (\"idle\" === this._state) {\r\n this._state = \"scheduled\";\r\n requestAnimationFrame(() => {\r\n this.dispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n });\r\n }\r\n\r\n return new Promise((resolve) => {\r\n this._onCompleted.addOnce(() => resolve());\r\n });\r\n }\r\n\r\n public async convert(inputs: XYZProps[]): Promise<{ points: PointWithStatus[], fromCache: number }> {\r\n const fromCache = this.enqueue(inputs);\r\n assert(fromCache >= 0);\r\n assert(fromCache <= inputs.length);\r\n\r\n if (fromCache === inputs.length)\r\n return { points: this.getFromCache(inputs), fromCache };\r\n\r\n await this.scheduleDispatch();\r\n\r\n return { points: this.getFromCache(inputs), fromCache };\r\n }\r\n\r\n public findCached(inputs: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n const result: Array<PointWithStatus | undefined> = [];\r\n let missing: XYZProps[] | undefined;\r\n for (const input of inputs) {\r\n const key = this.toXYAndZ(input, this._scratchXYZ);\r\n const output = this._cache.get(key);\r\n result.push(output);\r\n if (!output) {\r\n if (!missing)\r\n missing = [];\r\n\r\n missing.push(input);\r\n }\r\n }\r\n\r\n return { result, missing };\r\n }\r\n}\r\n\r\n/** Response to a request to obtain imodel coordinates from cache.\r\n * @internal\r\n */\r\nexport interface CachedIModelCoordinatesResponseProps {\r\n /** An array of the same length as the input array, with undefined entries at indices corresponding to points not found in cache. */\r\n result: Array<PointWithStatus | undefined>;\r\n /** An array of points in the input array which were not found in the cache, or undefined if all points were found in the cache. */\r\n missing?: XYZProps[];\r\n}\r\n\r\n/** Options used to create a [[GeoConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface GeoConverterOptions {\r\n readonly datum: string;\r\n isIModelClosed: () => boolean;\r\n toIModelCoords: (request: IModelCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n fromIModelCoords: (request: GeoCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n}\r\n\r\n/** An object capable of communicating with the backend to convert between coordinates in a geographic coordinate system and coordinates in an [[IModelConnection]]'s own coordinate system.\r\n * @see [[GeoServices.getConverter]] to obtain a converter.\r\n * @see [GeographicCRS]($common) for more information about geographic coordinate reference systems.\r\n * @public\r\n */\r\nexport class GeoConverter {\r\n private readonly _geoToIModel: CoordinateConverter;\r\n private readonly _iModelToGeo: CoordinateConverter;\r\n /** Used for removing this converter from GeoServices' cache after all requests are completed.\r\n * @internal\r\n */\r\n public readonly onAllRequestsCompleted = new BeEvent<() => void>();\r\n\r\n /** @internal */\r\n constructor(opts: GeoConverterOptions) {\r\n const isIModelClosed = opts.isIModelClosed;\r\n this._geoToIModel = new CoordinateConverter({\r\n direction: \"geoToIModel\",\r\n isIModelClosed,\r\n requestPoints: async (geoCoords: XYAndZ[]) => opts.toIModelCoords({ source: opts.datum, geoCoords }),\r\n });\r\n\r\n this._iModelToGeo = new CoordinateConverter({\r\n direction: \"iModelToGeo\",\r\n isIModelClosed,\r\n requestPoints: async (iModelCoords: XYAndZ[]) => opts.fromIModelCoords({ target: opts.datum, iModelCoords }),\r\n });\r\n }\r\n\r\n /** Convert the specified geographic coordinates into iModel coordinates. */\r\n public async convertToIModelCoords(geoPoints: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getIModelCoordinatesFromGeoCoordinates(geoPoints);\r\n return result.iModelCoords;\r\n }\r\n\r\n /** Convert the specified iModel coordinates into geographic coordinates. */\r\n public async convertFromIModelCoords(iModelCoords: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getGeoCoordinatesFromIModelCoordinates(iModelCoords);\r\n return result.geoCoords;\r\n }\r\n\r\n /** @internal */\r\n public async getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise<IModelCoordinatesResponseProps> {\r\n const result = await this._geoToIModel.convert(geoPoints);\r\n this.checkCompletion();\r\n return {\r\n iModelCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n /** @internal */\r\n public async getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise<GeoCoordinatesResponseProps> {\r\n const result = await this._iModelToGeo.convert(iModelPoints);\r\n this.checkCompletion();\r\n return {\r\n geoCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n private checkCompletion(): void {\r\n if (this._geoToIModel.isIdle && this._iModelToGeo.isIdle)\r\n this.onAllRequestsCompleted.raiseEvent();\r\n }\r\n\r\n /** @internal */\r\n public getCachedIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n return this._geoToIModel.findCached(geoPoints);\r\n }\r\n}\r\n\r\n/** @internal */\r\nexport type GeoServicesOptions = Omit<GeoConverterOptions, \"datum\">;\r\n\r\n/** The Geographic Services available for an [[IModelConnection]].\r\n * @see [[IModelConnection.geoServices]] to obtain the GeoServices for a specific iModel.\r\n * @public\r\n */\r\nexport class GeoServices {\r\n private readonly _options: GeoServicesOptions;\r\n /** Each GeoConverter has its own independent request queue and cache of previously-converted points.\r\n * Some callers like RealityTileTree obtain a single GeoConverter and reuse it throughout their own lifetime. Therefore they benefit from both batching and caching, and\r\n * the cache gets deleted once the RealityTileTree becomes disused.\r\n *\r\n * Other callers like IModelConnection.spatialToCartographic obtain a new GeoConverter every time they need one, use it to convert a single point(!), and then discard the converter.\r\n * This entirely prevents batching - e.g., calling spatialToCartographic 20 times in one frame results in 20 http requests.\r\n * To address that, we cache each GeoConverter returned by getConverter until it has converted at least one point and has no further outstanding conversion requests.\r\n * In this way, the converter lives for as long as (and no longer than) any caller is awaiting conversion to/from its datum - it and its cache are deleted once it becomes disused.\r\n * This makes the coordinate caching generally less useful, but at least bounded - and maximizes batching of requests.\r\n */\r\n private readonly _cache = new Map<string, GeoConverter>();\r\n\r\n /** @internal */\r\n public constructor(options: GeoServicesOptions) {\r\n this._options = options;\r\n }\r\n\r\n /** @internal */\r\n public static createForIModel(iModel: IModelConnection): GeoServices {\r\n return new GeoServices({\r\n isIModelClosed: () => iModel.isClosed,\r\n toIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getIModelCoordinatesFromGeoCoordinates(iModel.getRpcProps(), request);\r\n return response.iModelCoords;\r\n },\r\n fromIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getGeoCoordinatesFromIModelCoordinates(iModel.getRpcProps(), request);\r\n return response.geoCoords;\r\n },\r\n });\r\n }\r\n\r\n /** Obtain a converter that can convert between a geographic coordinate system and the iModel's own coordinate system.\r\n * @param datumOrGCRS The name or JSON representation of the geographic coordinate system datum - for example, \"WGS84\".\r\n * @returns a converter, or `undefined` if the iModel is not open.\r\n * @note A [[BlankConnection]] has no connection to a backend, so it is never \"open\"; therefore it always returns `undefined`.\r\n */\r\n public getConverter(datumOrGCRS?: string | GeographicCRSProps): GeoConverter | undefined {\r\n if (this._options.isIModelClosed())\r\n return undefined;\r\n\r\n const datum = (typeof datumOrGCRS === \"object\" ? JSON.stringify(datumOrGCRS) : datumOrGCRS) ?? \"\";\r\n\r\n let converter = this._cache.get(datum);\r\n if (!converter) {\r\n converter = new GeoConverter({ ...this._options, datum });\r\n this._cache.set(datum, converter);\r\n\r\n converter.onAllRequestsCompleted.addOnce(() => {\r\n if (converter === this._cache.get(datum))\r\n this._cache.delete(datum);\r\n });\r\n }\r\n\r\n return converter;\r\n }\r\n}\r\n"]}
|
|
@@ -34,8 +34,6 @@ export declare class ExtensionAdmin {
|
|
|
34
34
|
* @param hostUrl (string) Accepts both URLs and hostnames (e.g., http://localhost:3000, yourdomain.com, https://www.yourdomain.com, etc.).
|
|
35
35
|
*/
|
|
36
36
|
registerHost(hostUrl: string): void;
|
|
37
|
-
/** Returns the hostname of an input string. Throws an error if input is not a valid hostname (or URL). */
|
|
38
|
-
private getHostName;
|
|
39
37
|
/** Loops over all enabled Extensions and triggers each one if the provided event is defined. */
|
|
40
38
|
private activateExtensionEvents;
|
|
41
39
|
/** Executes the extension. Catches and logs any errors (so that an extension will not crash the main application). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionAdmin.d.ts","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":"AAIA;;GAEG;AAKH,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ExtensionAdmin.d.ts","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":"AAIA;;GAEG;AAKH,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA0DxE;;;GAGG;AACH,qBAAa,cAAc;IACzB,2GAA2G;IAC3G,OAAO,CAAC,WAAW,CAA0E;IAC7F,OAAO,CAAC,MAAM,CAAW;IAEzB;;OAEG;IACI,SAAS,sBAEd;;IAMF;;;;;OAKG;IACU,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBrE;;;;OAIG;IACU,aAAa,CAAC,SAAS,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAM3E;;;;OAIG;IACI,YAAY,CAAC,OAAO,EAAE,MAAM;IAOnC,gGAAgG;YAClF,uBAAuB;IAarC,sHAAsH;YACxG,QAAQ;CAOvB"}
|
|
@@ -10,6 +10,45 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.ExtensionAdmin = void 0;
|
|
11
11
|
const core_bentley_1 = require("@itwin/core-bentley");
|
|
12
12
|
const FrontendLoggerCategory_1 = require("../common/FrontendLoggerCategory");
|
|
13
|
+
/** The Extensions loading system has the following goals:
|
|
14
|
+
* 1. Only fetch what is needed when it is required
|
|
15
|
+
* 1. Load a manifest file
|
|
16
|
+
* 2. Load the the main module when necessary (usually at an activation event)
|
|
17
|
+
* 2. Download the extension's files
|
|
18
|
+
*
|
|
19
|
+
* 3 ways to load an Extension into the system:
|
|
20
|
+
*
|
|
21
|
+
* 1. Load both the Extension Manifest and import the main module of the extension from a local file/package.
|
|
22
|
+
* 2. A minimum set of properties to get the manifest and javascript from a remote server.
|
|
23
|
+
* 3. A minimum set of properties to get the manifest and javascript from Bentley's Extension Service.
|
|
24
|
+
*
|
|
25
|
+
* An Extension must be added to ExtensionAdmin before it can be executed during activation events.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Normalizes hostnames for comparison.
|
|
29
|
+
* @returns The normalized hostname of a URL or bare hostname.
|
|
30
|
+
* @throws Error if the input cannot be parsed, or does not contain a hostname.
|
|
31
|
+
*/
|
|
32
|
+
function normalizeHostname(input) {
|
|
33
|
+
const invalid = () => new Error(`"${input}" is not a valid URL or hostname (i.e. http://localhost:3000, yourdomain.com, etc.).`);
|
|
34
|
+
// inputs without a scheme (e.g., https://) will throw an error in the URL constructor
|
|
35
|
+
const inputWithScheme = /^[a-z][a-z0-9+\-.]*:\/\//i.test(input) ? input : `https://${input}`;
|
|
36
|
+
let hostname;
|
|
37
|
+
try {
|
|
38
|
+
hostname = new URL(inputWithScheme).hostname.toLowerCase();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw invalid();
|
|
42
|
+
}
|
|
43
|
+
// strip only a leading "www." label - removing "www" anywhere would make unrelated
|
|
44
|
+
// hosts like "wwwexample.com" compare equal to "example.com"
|
|
45
|
+
const normalized = hostname.startsWith("www.") ? hostname.substring("www.".length) : hostname;
|
|
46
|
+
// schemes with an optional or opaque host (e.g. file:///, data:) parse successfully but carry no
|
|
47
|
+
// hostname, and an input of just "www." normalizes to nothing. Neither can identify a host.
|
|
48
|
+
if (normalized.length === 0)
|
|
49
|
+
throw invalid();
|
|
50
|
+
return normalized;
|
|
51
|
+
}
|
|
13
52
|
/** The Extension Admin controls the list of currently loaded Extensions.
|
|
14
53
|
*
|
|
15
54
|
* @alpha
|
|
@@ -34,10 +73,10 @@ class ExtensionAdmin {
|
|
|
34
73
|
* @alpha
|
|
35
74
|
*/
|
|
36
75
|
async addExtension(provider) {
|
|
37
|
-
if (provider.hostname) {
|
|
38
|
-
const
|
|
39
|
-
if (this._hosts.
|
|
40
|
-
throw new Error(`Error loading extension: ${
|
|
76
|
+
if (provider.hostname !== undefined && this._hosts.length > 0) {
|
|
77
|
+
const hostname = normalizeHostname(provider.hostname);
|
|
78
|
+
if (this._hosts.indexOf(hostname) < 0) {
|
|
79
|
+
throw new Error(`Error loading extension: ${provider.hostname} was not registered.`);
|
|
41
80
|
}
|
|
42
81
|
}
|
|
43
82
|
try {
|
|
@@ -68,28 +107,11 @@ class ExtensionAdmin {
|
|
|
68
107
|
* @param hostUrl (string) Accepts both URLs and hostnames (e.g., http://localhost:3000, yourdomain.com, https://www.yourdomain.com, etc.).
|
|
69
108
|
*/
|
|
70
109
|
registerHost(hostUrl) {
|
|
71
|
-
const hostname =
|
|
110
|
+
const hostname = normalizeHostname(hostUrl);
|
|
72
111
|
if (this._hosts.indexOf(hostname) < 0) {
|
|
73
112
|
this._hosts.push(hostname);
|
|
74
113
|
}
|
|
75
114
|
}
|
|
76
|
-
/** Returns the hostname of an input string. Throws an error if input is not a valid hostname (or URL). */
|
|
77
|
-
getHostName(inputUrl) {
|
|
78
|
-
// inputs without a protocol (e.g., http://) will throw an error in URL constructor
|
|
79
|
-
const inputWithProtocol = /(http|https):\/\//.test(inputUrl) ?
|
|
80
|
-
inputUrl :
|
|
81
|
-
`https://${inputUrl}`;
|
|
82
|
-
try {
|
|
83
|
-
const hostname = new URL(inputWithProtocol).hostname.replace("www.", "");
|
|
84
|
-
return hostname;
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
if (e instanceof TypeError) {
|
|
88
|
-
throw new Error("Argument hostUrl should be a valid URL or hostname (i.e. http://localhost:3000, yourdomain.com, etc.).");
|
|
89
|
-
}
|
|
90
|
-
throw e;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
115
|
/** Loops over all enabled Extensions and triggers each one if the provided event is defined. */
|
|
94
116
|
async activateExtensionEvents(event) {
|
|
95
117
|
for (const extension of this._extensions.values()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionAdmin.js","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;AAC/F;;GAEG;;;AAEH,sDAA6C;AAE7C,6EAA0E;
|
|
1
|
+
{"version":3,"file":"ExtensionAdmin.js","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Extensions\r\n */\r\n\r\nimport { Logger } from \"@itwin/core-bentley\";\r\n\r\nimport { FrontendLoggerCategory } from \"../common/FrontendLoggerCategory\";\r\nimport type { ExtensionManifest, ExtensionProvider } from \"./Extension\";\r\n\r\n/** The Extensions loading system has the following goals:\r\n * 1. Only fetch what is needed when it is required\r\n * 1. Load a manifest file\r\n * 2. Load the the main module when necessary (usually at an activation event)\r\n * 2. Download the extension's files\r\n *\r\n * 3 ways to load an Extension into the system:\r\n *\r\n * 1. Load both the Extension Manifest and import the main module of the extension from a local file/package.\r\n * 2. A minimum set of properties to get the manifest and javascript from a remote server.\r\n * 3. A minimum set of properties to get the manifest and javascript from Bentley's Extension Service.\r\n *\r\n * An Extension must be added to ExtensionAdmin before it can be executed during activation events.\r\n */\r\n\r\n/**\r\n * Normalizes hostnames for comparison.\r\n * @returns The normalized hostname of a URL or bare hostname.\r\n * @throws Error if the input cannot be parsed, or does not contain a hostname.\r\n */\r\nfunction normalizeHostname(input: string): string {\r\n const invalid = () => new Error(`\"${input}\" is not a valid URL or hostname (i.e. http://localhost:3000, yourdomain.com, etc.).`);\r\n\r\n // inputs without a scheme (e.g., https://) will throw an error in the URL constructor\r\n const inputWithScheme = /^[a-z][a-z0-9+\\-.]*:\\/\\//i.test(input) ? input : `https://${input}`;\r\n\r\n let hostname;\r\n try {\r\n hostname = new URL(inputWithScheme).hostname.toLowerCase();\r\n } catch {\r\n throw invalid();\r\n }\r\n\r\n // strip only a leading \"www.\" label - removing \"www\" anywhere would make unrelated\r\n // hosts like \"wwwexample.com\" compare equal to \"example.com\"\r\n const normalized = hostname.startsWith(\"www.\") ? hostname.substring(\"www.\".length) : hostname;\r\n\r\n // schemes with an optional or opaque host (e.g. file:///, data:) parse successfully but carry no\r\n // hostname, and an input of just \"www.\" normalizes to nothing. Neither can identify a host.\r\n if (normalized.length === 0)\r\n throw invalid();\r\n\r\n return normalized;\r\n}\r\n\r\n/**\r\n * A \"ready to use\" Extension (contains a manifest object and an extension provider to help execute).\r\n * Will be used as the type for in-memory extensions in the ExtensionAdmin\r\n */\r\ninterface InstalledExtension {\r\n /** An extension provider that has been added to ExtensionAdmin */\r\n provider: ExtensionProvider;\r\n /** The manifest (package.json) of the extension */\r\n manifest: ExtensionManifest;\r\n}\r\n\r\n/** The Extension Admin controls the list of currently loaded Extensions.\r\n *\r\n * @alpha\r\n */\r\nexport class ExtensionAdmin {\r\n /** Defines the set of extensions that are currently known and can be invoked during activation events. */\r\n private _extensions: Map<string, InstalledExtension> = new Map<string, InstalledExtension>();\r\n private _hosts: string[];\r\n\r\n /** Fired when an Extension has been added or removed.\r\n * @internal\r\n */\r\n public onStartup = async () => {\r\n await this.activateExtensionEvents(\"onStartup\");\r\n };\r\n\r\n public constructor() {\r\n this._hosts = [];\r\n }\r\n\r\n /**\r\n * Adds an extension.\r\n * The manifest will be fetched and the extension will be activated on an activation event.\r\n * @param provider\r\n * @alpha\r\n */\r\n public async addExtension(provider: ExtensionProvider): Promise<void> {\r\n if (provider.hostname !== undefined && this._hosts.length > 0) {\r\n const hostname = normalizeHostname(provider.hostname);\r\n if (this._hosts.indexOf(hostname) < 0) {\r\n throw new Error(`Error loading extension: ${provider.hostname} was not registered.`);\r\n }\r\n }\r\n try {\r\n const manifest = await provider.getManifest();\r\n this._extensions.set(manifest.name, {\r\n manifest,\r\n provider,\r\n });\r\n // TODO - temporary fix to execute the missed startup event\r\n if (manifest.activationEvents.includes(\"onStartup\"))\r\n provider.execute(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n } catch (e) {\r\n throw new Error(`Failed to get extension manifest ${provider.hostname ? `at ${provider.hostname}` : \"\"}: ${String(e)}`);\r\n }\r\n }\r\n\r\n /**\r\n * Adds a list of extensions\r\n * @param providers\r\n * @alpha\r\n */\r\n public async addExtensions(providers: ExtensionProvider[]): Promise<void[]> {\r\n return Promise.all(\r\n providers.map(async (provider) => this.addExtension(provider)),\r\n );\r\n }\r\n\r\n /**\r\n * Registers a hostname for an extension.\r\n * Once a hostname has been registered, only remote extensions from registered hosts are permitted to be added.\r\n * @param hostUrl (string) Accepts both URLs and hostnames (e.g., http://localhost:3000, yourdomain.com, https://www.yourdomain.com, etc.).\r\n */\r\n public registerHost(hostUrl: string) {\r\n const hostname = normalizeHostname(hostUrl);\r\n if (this._hosts.indexOf(hostname) < 0) {\r\n this._hosts.push(hostname);\r\n }\r\n }\r\n\r\n /** Loops over all enabled Extensions and triggers each one if the provided event is defined. */\r\n private async activateExtensionEvents(event: string) {\r\n for (const extension of this._extensions.values()) {\r\n if (!extension.manifest.activationEvents)\r\n continue;\r\n\r\n for (const activationEvent of extension.manifest.activationEvents) {\r\n if (activationEvent === event) {\r\n this._execute(extension); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** Executes the extension. Catches and logs any errors (so that an extension will not crash the main application). */\r\n private async _execute(extension: InstalledExtension) {\r\n try {\r\n await extension.provider.execute();\r\n } catch (e) {\r\n Logger.logError(FrontendLoggerCategory.Extensions, `Error executing extension ${extension.manifest.name}: ${String(e)}`);\r\n }\r\n }\r\n}\r\n"]}
|
|
@@ -17,7 +17,7 @@ export interface RemoteExtensionProviderProps {
|
|
|
17
17
|
*/
|
|
18
18
|
export declare class RemoteExtensionProvider implements ExtensionProvider {
|
|
19
19
|
private readonly _props;
|
|
20
|
-
/** The
|
|
20
|
+
/** The hostname of the server where the extension is hosted. */
|
|
21
21
|
readonly hostname: string;
|
|
22
22
|
constructor(_props: RemoteExtensionProviderProps);
|
|
23
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteExtensionProvider.d.ts","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAItB;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAInD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,
|
|
1
|
+
{"version":3,"file":"RemoteExtensionProvider.d.ts","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAItB;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAInD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,gEAAgE;IAChE,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAEJ,MAAM,EAAE,4BAA4B;IAIjE;;;OAGG;IACU,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAIvC;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAIvD"}
|
|
@@ -11,11 +11,11 @@ const ExtensionLoadScript_1 = require("./ExtensionLoadScript");
|
|
|
11
11
|
*/
|
|
12
12
|
class RemoteExtensionProvider {
|
|
13
13
|
_props;
|
|
14
|
-
/** The
|
|
14
|
+
/** The hostname of the server where the extension is hosted. */
|
|
15
15
|
hostname;
|
|
16
16
|
constructor(_props) {
|
|
17
17
|
this._props = _props;
|
|
18
|
-
this.hostname = new URL(this._props.jsUrl).hostname
|
|
18
|
+
this.hostname = new URL(this._props.jsUrl).hostname;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Attempts to execute an extension.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteExtensionProvider.js","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":";;;AAQA,mDAAgD;AAChD,+DAAmD;AAanD;;;;;GAKG;AACH,MAAa,uBAAuB;IAIL;IAH7B,
|
|
1
|
+
{"version":3,"file":"RemoteExtensionProvider.js","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":";;;AAQA,mDAAgD;AAChD,+DAAmD;AAanD;;;;;GAKG;AACH,MAAa,uBAAuB;IAIL;IAH7B,gEAAgE;IAChD,QAAQ,CAAS;IAEjC,YAA6B,MAAoC;QAApC,WAAM,GAAN,MAAM,CAA8B;QAC/D,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC;IACtD,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,OAAO;QAClB,OAAO,IAAA,gCAAU,EAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW;QACtB,OAAO,IAAA,iBAAO,EAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAClD,CAAC;CAEF;AAxBD,0DAwBC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\nimport type {\r\n ExtensionManifest,\r\n ExtensionProvider,\r\n} from \"../Extension\";\r\nimport { request } from \"../../request/Request\";\r\nimport { loadScript } from \"./ExtensionLoadScript\";\r\n\r\n/**\r\n * Required props for a remote extension provider\r\n * @alpha\r\n */\r\nexport interface RemoteExtensionProviderProps {\r\n /** URL where the extension entry point can be loaded from */\r\n jsUrl: string;\r\n /** URL where the manifest (package.json) can be loaded from */\r\n manifestUrl: string;\r\n}\r\n\r\n/**\r\n * Implements a \"remote\" extension.\r\n * Remote extensions are hosted on an external server.\r\n * The execute() and getManifest() methods are used by the ExtensionAdmin to load and execute the extension.\r\n * @alpha\r\n */\r\nexport class RemoteExtensionProvider implements ExtensionProvider {\r\n /** The hostname of the server where the extension is hosted. */\r\n public readonly hostname: string;\r\n\r\n constructor(private readonly _props: RemoteExtensionProviderProps) {\r\n this.hostname = new URL(this._props.jsUrl).hostname;\r\n }\r\n\r\n /**\r\n * Attempts to execute an extension.\r\n * Throws an error if the provided jsUrl is not accessible.\r\n */\r\n public async execute(): Promise<string> {\r\n return loadScript(this._props.jsUrl);\r\n }\r\n\r\n /**\r\n * Attempts to fetch an extension's manifest (package.json) file.\r\n * Throws an error if the provided manifestUrl is not accessible.\r\n */\r\n public async getManifest(): Promise<ExtensionManifest> {\r\n return request(this._props.manifestUrl, \"json\");\r\n }\r\n\r\n}\r\n"]}
|
package/lib/esm/GeoServices.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { IModelConnection } from "./IModelConnection";
|
|
|
9
9
|
* @internal exported strictly for tests.
|
|
10
10
|
*/
|
|
11
11
|
export interface CoordinateConverterOptions {
|
|
12
|
+
/** Conversion direction included in error diagnostics. */
|
|
13
|
+
direction: "geoToIModel" | "iModelToGeo";
|
|
12
14
|
isIModelClosed: () => boolean;
|
|
13
15
|
/** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */
|
|
14
16
|
requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;
|
|
@@ -43,6 +45,7 @@ export declare class CoordinateConverter {
|
|
|
43
45
|
protected readonly _maxPointsPerRequest: number;
|
|
44
46
|
protected readonly _isIModelClosed: () => boolean;
|
|
45
47
|
protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;
|
|
48
|
+
private readonly _direction;
|
|
46
49
|
protected _redispatchOnCompletion: boolean;
|
|
47
50
|
get isIdle(): boolean;
|
|
48
51
|
protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeoServices.d.ts","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"AAIA;;GAEG;AAGH,OAAO,EACG,OAAO,EAAE,UAAU,EAAU,WAAW,EACjD,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EACL,0BAA0B,EAAE,2BAA2B,EAAkB,kBAAkB,EAAE,6BAA6B,EAAE,8BAA8B,EAClI,eAAe,EACxC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,cAAc,EAAE,MAAM,OAAO,CAAC;IAC9B,2HAA2H;IAC3H,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAChE,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAUD,KAAK,wBAAwB,GAE3B,MAAM,GAEN,WAAW,GAEX,WAAW,CAAC;AAEd;;;;;;;;;;;;GAYG;AACH,qBAAa,mBAAmB;IAC9B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/D,SAAS,CAAC,MAAM,EAAE,wBAAwB,CAAU;IAEpD,SAAS,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAExC,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAEzC,SAAS,CAAC,YAAY,gBAAqB,IAAI,EAAI;IAEnD,SAAS,CAAC,QAAQ,CAAC,WAAW;;;;MAAwB;IACtD,SAAS,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAChD,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"GeoServices.d.ts","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"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"}
|
package/lib/esm/GeoServices.js
CHANGED
|
@@ -42,6 +42,7 @@ export class CoordinateConverter {
|
|
|
42
42
|
_maxPointsPerRequest;
|
|
43
43
|
_isIModelClosed;
|
|
44
44
|
_requestPoints;
|
|
45
|
+
_direction;
|
|
45
46
|
// If true, [[dispatch]] will schedule another dispatch after it receives a response.
|
|
46
47
|
// This is needed when all the points requested after the most recent dispatch were included in the currently-in-flight request -
|
|
47
48
|
// _pending will be empty but new callers will be awaiting the results of the in-flight request.
|
|
@@ -66,6 +67,7 @@ export class CoordinateConverter {
|
|
|
66
67
|
this._maxPointsPerRequest = Math.max(1, opts.maxPointsPerRequest ?? 300);
|
|
67
68
|
this._isIModelClosed = opts.isIModelClosed;
|
|
68
69
|
this._requestPoints = opts.requestPoints;
|
|
70
|
+
this._direction = opts.direction;
|
|
69
71
|
this._cache = new Dictionary(compareXYAndZ, cloneXYAndZ);
|
|
70
72
|
this._pending = new SortedArray(compareXYAndZ, false, cloneXYAndZ);
|
|
71
73
|
this._inflight = new SortedArray(compareXYAndZ, false, cloneXYAndZ);
|
|
@@ -100,7 +102,10 @@ export class CoordinateConverter {
|
|
|
100
102
|
this._cache.set(requests[j], results[j]);
|
|
101
103
|
}
|
|
102
104
|
}).catch((err) => {
|
|
103
|
-
Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, err)
|
|
105
|
+
Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, err, () => ({
|
|
106
|
+
direction: this._direction,
|
|
107
|
+
pointCount: requests.length,
|
|
108
|
+
}));
|
|
104
109
|
});
|
|
105
110
|
promises.push(promise);
|
|
106
111
|
}
|
|
@@ -196,10 +201,12 @@ export class GeoConverter {
|
|
|
196
201
|
constructor(opts) {
|
|
197
202
|
const isIModelClosed = opts.isIModelClosed;
|
|
198
203
|
this._geoToIModel = new CoordinateConverter({
|
|
204
|
+
direction: "geoToIModel",
|
|
199
205
|
isIModelClosed,
|
|
200
206
|
requestPoints: async (geoCoords) => opts.toIModelCoords({ source: opts.datum, geoCoords }),
|
|
201
207
|
});
|
|
202
208
|
this._iModelToGeo = new CoordinateConverter({
|
|
209
|
+
direction: "iModelToGeo",
|
|
203
210
|
isIModelClosed,
|
|
204
211
|
requestPoints: async (iModelCoords) => opts.fromIModelCoords({ target: opts.datum, iModelCoords }),
|
|
205
212
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeoServices.js","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module IModelConnection\r\n */\r\n\r\n// cspell:ignore GCRS\r\nimport {\r\n assert, BeEvent, Dictionary, Logger, SortedArray,\r\n} from \"@itwin/core-bentley\";\r\nimport { WritableXYAndZ, XYAndZ, XYZProps } from \"@itwin/core-geometry\";\r\nimport {\r\n GeoCoordinatesRequestProps, GeoCoordinatesResponseProps, GeoCoordStatus, GeographicCRSProps, IModelCoordinatesRequestProps, IModelCoordinatesResponseProps,\r\n IModelReadRpcInterface, PointWithStatus,\r\n} from \"@itwin/core-common\";\r\nimport { IModelConnection } from \"./IModelConnection\";\r\nimport { FrontendLoggerCategory } from \"./common/FrontendLoggerCategory\";\r\n\r\n/** Options used to create a [[CoordinateConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface CoordinateConverterOptions {\r\n isIModelClosed: () => boolean;\r\n /** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */\r\n requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n /** Maximum number of points to include in each request. Default: 300. */\r\n maxPointsPerRequest?: number;\r\n}\r\n\r\nfunction compareXYAndZ(lhs: XYAndZ, rhs: XYAndZ): number {\r\n return lhs.x - rhs.x || lhs.y - rhs.y || lhs.z - rhs.z;\r\n}\r\n\r\nfunction cloneXYAndZ(xyz: XYAndZ): XYAndZ {\r\n return { x: xyz.x, y: xyz.y, z: xyz.z };\r\n}\r\n\r\ntype CoordinateConverterState =\r\n // No pending requests.\r\n \"idle\" |\r\n // We have scheduled a requestAnimationFrame to dispatch all pending requests.\r\n \"scheduled\" |\r\n // We have dispatched all requests that were pending at the most recent requestAnimationFrame callback.\r\n \"in-flight\";\r\n\r\n/** Performs conversion of coordinates from one coordinate system to another.\r\n * A [[GeoConverter]] has a pair of these for converting between iModel coordinates and geographic coordinates.\r\n * Uses a cache to avoid repeatedly requesting the same points, and a batching strategy to avoid making frequent small requests.\r\n * The cache stores every point that was ever converted by [[convert]]. It is currently permitted to grow to unbounded size.\r\n * The batching works as follows:\r\n * When a conversion is requested via [[convert]], if all the requested points are in the cache, they are returned immediately.\r\n * Otherwise, any points not in the cache and not in the current in-flight request (if any) are placed onto the queue of pending requests.\r\n * A pending request is scheduled if one hasn't already been scheduled, via requestAnimationFrame.\r\n * In the animation frame callback, the pending requests are split into batches of no more than options.maxPointsPerRequest and dispatched to the backend.\r\n * Once the response is received, the results are loaded into and returned from the cache.\r\n * If more calls to convert occurred while the request was in flight, another request is dispatched.\r\n * @internal exported strictly for tests.\r\n */\r\nexport class CoordinateConverter {\r\n protected readonly _cache: Dictionary<XYAndZ, PointWithStatus>;\r\n protected _state: CoordinateConverterState = \"idle\";\r\n // The accumulated set of points to be converted by the next request.\r\n protected _pending: SortedArray<XYAndZ>;\r\n // The set of points that were included in the current in-flight request, if any.\r\n protected _inflight: SortedArray<XYAndZ>;\r\n // An event fired when the next request completes.\r\n protected _onCompleted = new BeEvent<() => void>();\r\n // Used for creating cache keys (XYAndZ) from XYZProps without having to allocate temporary objects.\r\n protected readonly _scratchXYZ = { x: 0, y: 0, z: 0 };\r\n protected readonly _maxPointsPerRequest: number;\r\n protected readonly _isIModelClosed: () => boolean;\r\n protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n // If true, [[dispatch]] will schedule another dispatch after it receives a response.\r\n // This is needed when all the points requested after the most recent dispatch were included in the currently-in-flight request -\r\n // _pending will be empty but new callers will be awaiting the results of the in-flight request.\r\n protected _redispatchOnCompletion = false;\r\n\r\n public get isIdle(): boolean {\r\n return \"idle\" === this._state;\r\n }\r\n\r\n protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ {\r\n if (Array.isArray(input)) {\r\n output.x = input[0] ?? 0;\r\n output.y = input[1] ?? 0;\r\n output.z = input[2] ?? 0;\r\n } else {\r\n output.x = input.x ?? 0;\r\n output.y = input.y ?? 0;\r\n output.z = input.z ?? 0;\r\n }\r\n\r\n return output;\r\n }\r\n\r\n public constructor(opts: CoordinateConverterOptions) {\r\n this._maxPointsPerRequest = Math.max(1, opts.maxPointsPerRequest ?? 300);\r\n this._isIModelClosed = opts.isIModelClosed;\r\n this._requestPoints = opts.requestPoints;\r\n\r\n this._cache = new Dictionary<XYAndZ, PointWithStatus>(compareXYAndZ, cloneXYAndZ);\r\n this._pending = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n this._inflight = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n }\r\n\r\n protected async dispatch(): Promise<void> {\r\n assert(this._state === \"scheduled\");\r\n if (this._isIModelClosed() || this._pending.isEmpty) {\r\n this._state = \"idle\";\r\n this._onCompleted.raiseEvent();\r\n return;\r\n }\r\n\r\n this._state = \"in-flight\";\r\n\r\n // Ensure subsequently-enqueued requests listen for the *next* response to be received.\r\n const onCompleted = this._onCompleted;\r\n this._onCompleted = new BeEvent<() => void>();\r\n\r\n // Pending requests are now in flight. Start a new list of pending requests. It's cheaper to swap than to allocate new objects.\r\n const inflight = this._pending;\r\n this._pending = this._inflight;\r\n assert(this._pending.isEmpty);\r\n this._inflight = inflight;\r\n\r\n // Split requests if necessary to avoid requesting more than the maximum allowed number of points.\r\n const promises: Array<Promise<void>> = [];\r\n for (let i = 0; i < inflight.length; i += this._maxPointsPerRequest) {\r\n const requests = inflight.slice(i, i + this._maxPointsPerRequest).extractArray();\r\n const promise = this._requestPoints(requests).then((results) => {\r\n if (this._isIModelClosed())\r\n return;\r\n\r\n if (results.length !== requests.length)\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, `requested conversion of ${requests.length} points, but received ${results.length} points`);\r\n\r\n for (let j = 0; j < results.length; j++) {\r\n if (j < requests.length)\r\n this._cache.set(requests[j], results[j]);\r\n }\r\n }).catch((err) => {\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, err);\r\n });\r\n\r\n promises.push(promise);\r\n }\r\n\r\n await Promise.all(promises);\r\n\r\n assert(this._state === \"in-flight\");\r\n this._state = \"idle\";\r\n this._inflight.clear();\r\n\r\n // If any more pending conversions arrived while awaiting this request, schedule another request.\r\n if (!this._pending.isEmpty || this._redispatchOnCompletion) {\r\n this._redispatchOnCompletion = false;\r\n this.scheduleDispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n }\r\n\r\n // Resolve promises of all callers who were awaiting this request.\r\n onCompleted.raiseEvent();\r\n }\r\n\r\n // Add any points not present in cache to pending request list.\r\n // Return the number of points present in cache.\r\n protected enqueue(points: XYZProps[]): number {\r\n let numInCache = 0;\r\n for (const point of points) {\r\n const xyz = this.toXYAndZ(point, this._scratchXYZ);\r\n if (this._cache.get(xyz))\r\n ++numInCache;\r\n else if (this._inflight.contains(xyz))\r\n this._redispatchOnCompletion = true;\r\n else\r\n this._pending.insert(xyz);\r\n }\r\n\r\n return numInCache;\r\n }\r\n\r\n // Obtain converted points from the cache. The assumption is that every point in `inputs` is already present in the cache.\r\n // Any point not present will be returned unconverted with an error status.\r\n protected getFromCache(inputs: XYZProps[]): PointWithStatus[] {\r\n const outputs: PointWithStatus[] = [];\r\n for (const input of inputs) {\r\n const xyz = this.toXYAndZ(input, this._scratchXYZ);\r\n let output = this._cache.get(xyz);\r\n if (!output)\r\n output = { p: { ...xyz }, s: GeoCoordStatus.CSMapError };\r\n\r\n outputs.push(output);\r\n }\r\n\r\n return outputs;\r\n }\r\n\r\n protected async scheduleDispatch(): Promise<void> {\r\n if (\"idle\" === this._state) {\r\n this._state = \"scheduled\";\r\n requestAnimationFrame(() => {\r\n this.dispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n });\r\n }\r\n\r\n return new Promise((resolve) => {\r\n this._onCompleted.addOnce(() => resolve());\r\n });\r\n }\r\n\r\n public async convert(inputs: XYZProps[]): Promise<{ points: PointWithStatus[], fromCache: number }> {\r\n const fromCache = this.enqueue(inputs);\r\n assert(fromCache >= 0);\r\n assert(fromCache <= inputs.length);\r\n\r\n if (fromCache === inputs.length)\r\n return { points: this.getFromCache(inputs), fromCache };\r\n\r\n await this.scheduleDispatch();\r\n\r\n return { points: this.getFromCache(inputs), fromCache };\r\n }\r\n\r\n public findCached(inputs: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n const result: Array<PointWithStatus | undefined> = [];\r\n let missing: XYZProps[] | undefined;\r\n for (const input of inputs) {\r\n const key = this.toXYAndZ(input, this._scratchXYZ);\r\n const output = this._cache.get(key);\r\n result.push(output);\r\n if (!output) {\r\n if (!missing)\r\n missing = [];\r\n\r\n missing.push(input);\r\n }\r\n }\r\n\r\n return { result, missing };\r\n }\r\n}\r\n\r\n/** Response to a request to obtain imodel coordinates from cache.\r\n * @internal\r\n */\r\nexport interface CachedIModelCoordinatesResponseProps {\r\n /** An array of the same length as the input array, with undefined entries at indices corresponding to points not found in cache. */\r\n result: Array<PointWithStatus | undefined>;\r\n /** An array of points in the input array which were not found in the cache, or undefined if all points were found in the cache. */\r\n missing?: XYZProps[];\r\n}\r\n\r\n/** Options used to create a [[GeoConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface GeoConverterOptions {\r\n readonly datum: string;\r\n isIModelClosed: () => boolean;\r\n toIModelCoords: (request: IModelCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n fromIModelCoords: (request: GeoCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n}\r\n\r\n/** An object capable of communicating with the backend to convert between coordinates in a geographic coordinate system and coordinates in an [[IModelConnection]]'s own coordinate system.\r\n * @see [[GeoServices.getConverter]] to obtain a converter.\r\n * @see [GeographicCRS]($common) for more information about geographic coordinate reference systems.\r\n * @public\r\n */\r\nexport class GeoConverter {\r\n private readonly _geoToIModel: CoordinateConverter;\r\n private readonly _iModelToGeo: CoordinateConverter;\r\n /** Used for removing this converter from GeoServices' cache after all requests are completed.\r\n * @internal\r\n */\r\n public readonly onAllRequestsCompleted = new BeEvent<() => void>();\r\n\r\n /** @internal */\r\n constructor(opts: GeoConverterOptions) {\r\n const isIModelClosed = opts.isIModelClosed;\r\n this._geoToIModel = new CoordinateConverter({\r\n isIModelClosed,\r\n requestPoints: async (geoCoords: XYAndZ[]) => opts.toIModelCoords({ source: opts.datum, geoCoords }),\r\n });\r\n\r\n this._iModelToGeo = new CoordinateConverter({\r\n isIModelClosed,\r\n requestPoints: async (iModelCoords: XYAndZ[]) => opts.fromIModelCoords({ target: opts.datum, iModelCoords }),\r\n });\r\n }\r\n\r\n /** Convert the specified geographic coordinates into iModel coordinates. */\r\n public async convertToIModelCoords(geoPoints: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getIModelCoordinatesFromGeoCoordinates(geoPoints);\r\n return result.iModelCoords;\r\n }\r\n\r\n /** Convert the specified iModel coordinates into geographic coordinates. */\r\n public async convertFromIModelCoords(iModelCoords: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getGeoCoordinatesFromIModelCoordinates(iModelCoords);\r\n return result.geoCoords;\r\n }\r\n\r\n /** @internal */\r\n public async getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise<IModelCoordinatesResponseProps> {\r\n const result = await this._geoToIModel.convert(geoPoints);\r\n this.checkCompletion();\r\n return {\r\n iModelCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n /** @internal */\r\n public async getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise<GeoCoordinatesResponseProps> {\r\n const result = await this._iModelToGeo.convert(iModelPoints);\r\n this.checkCompletion();\r\n return {\r\n geoCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n private checkCompletion(): void {\r\n if (this._geoToIModel.isIdle && this._iModelToGeo.isIdle)\r\n this.onAllRequestsCompleted.raiseEvent();\r\n }\r\n\r\n /** @internal */\r\n public getCachedIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n return this._geoToIModel.findCached(geoPoints);\r\n }\r\n}\r\n\r\n/** @internal */\r\nexport type GeoServicesOptions = Omit<GeoConverterOptions, \"datum\">;\r\n\r\n/** The Geographic Services available for an [[IModelConnection]].\r\n * @see [[IModelConnection.geoServices]] to obtain the GeoServices for a specific iModel.\r\n * @public\r\n */\r\nexport class GeoServices {\r\n private readonly _options: GeoServicesOptions;\r\n /** Each GeoConverter has its own independent request queue and cache of previously-converted points.\r\n * Some callers like RealityTileTree obtain a single GeoConverter and reuse it throughout their own lifetime. Therefore they benefit from both batching and caching, and\r\n * the cache gets deleted once the RealityTileTree becomes disused.\r\n *\r\n * Other callers like IModelConnection.spatialToCartographic obtain a new GeoConverter every time they need one, use it to convert a single point(!), and then discard the converter.\r\n * This entirely prevents batching - e.g., calling spatialToCartographic 20 times in one frame results in 20 http requests.\r\n * To address that, we cache each GeoConverter returned by getConverter until it has converted at least one point and has no further outstanding conversion requests.\r\n * In this way, the converter lives for as long as (and no longer than) any caller is awaiting conversion to/from its datum - it and its cache are deleted once it becomes disused.\r\n * This makes the coordinate caching generally less useful, but at least bounded - and maximizes batching of requests.\r\n */\r\n private readonly _cache = new Map<string, GeoConverter>();\r\n\r\n /** @internal */\r\n public constructor(options: GeoServicesOptions) {\r\n this._options = options;\r\n }\r\n\r\n /** @internal */\r\n public static createForIModel(iModel: IModelConnection): GeoServices {\r\n return new GeoServices({\r\n isIModelClosed: () => iModel.isClosed,\r\n toIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getIModelCoordinatesFromGeoCoordinates(iModel.getRpcProps(), request);\r\n return response.iModelCoords;\r\n },\r\n fromIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getGeoCoordinatesFromIModelCoordinates(iModel.getRpcProps(), request);\r\n return response.geoCoords;\r\n },\r\n });\r\n }\r\n\r\n /** Obtain a converter that can convert between a geographic coordinate system and the iModel's own coordinate system.\r\n * @param datumOrGCRS The name or JSON representation of the geographic coordinate system datum - for example, \"WGS84\".\r\n * @returns a converter, or `undefined` if the iModel is not open.\r\n * @note A [[BlankConnection]] has no connection to a backend, so it is never \"open\"; therefore it always returns `undefined`.\r\n */\r\n public getConverter(datumOrGCRS?: string | GeographicCRSProps): GeoConverter | undefined {\r\n if (this._options.isIModelClosed())\r\n return undefined;\r\n\r\n const datum = (typeof datumOrGCRS === \"object\" ? JSON.stringify(datumOrGCRS) : datumOrGCRS) ?? \"\";\r\n\r\n let converter = this._cache.get(datum);\r\n if (!converter) {\r\n converter = new GeoConverter({ ...this._options, datum });\r\n this._cache.set(datum, converter);\r\n\r\n converter.onAllRequestsCompleted.addOnce(() => {\r\n if (converter === this._cache.get(datum))\r\n this._cache.delete(datum);\r\n });\r\n }\r\n\r\n return converter;\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"GeoServices.js","sourceRoot":"","sources":["../../src/GeoServices.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module IModelConnection\r\n */\r\n\r\n// cspell:ignore GCRS\r\nimport {\r\n assert, BeEvent, Dictionary, Logger, SortedArray,\r\n} from \"@itwin/core-bentley\";\r\nimport { WritableXYAndZ, XYAndZ, XYZProps } from \"@itwin/core-geometry\";\r\nimport {\r\n GeoCoordinatesRequestProps, GeoCoordinatesResponseProps, GeoCoordStatus, GeographicCRSProps, IModelCoordinatesRequestProps, IModelCoordinatesResponseProps,\r\n IModelReadRpcInterface, PointWithStatus,\r\n} from \"@itwin/core-common\";\r\nimport { IModelConnection } from \"./IModelConnection\";\r\nimport { FrontendLoggerCategory } from \"./common/FrontendLoggerCategory\";\r\n\r\n/** Options used to create a [[CoordinateConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface CoordinateConverterOptions {\r\n /** Conversion direction included in error diagnostics. */\r\n direction: \"geoToIModel\" | \"iModelToGeo\";\r\n isIModelClosed: () => boolean;\r\n /** Asynchronously convert each point. The resultant array should have the same number and order of points as the input. */\r\n requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n /** Maximum number of points to include in each request. Default: 300. */\r\n maxPointsPerRequest?: number;\r\n}\r\n\r\nfunction compareXYAndZ(lhs: XYAndZ, rhs: XYAndZ): number {\r\n return lhs.x - rhs.x || lhs.y - rhs.y || lhs.z - rhs.z;\r\n}\r\n\r\nfunction cloneXYAndZ(xyz: XYAndZ): XYAndZ {\r\n return { x: xyz.x, y: xyz.y, z: xyz.z };\r\n}\r\n\r\ntype CoordinateConverterState =\r\n // No pending requests.\r\n \"idle\" |\r\n // We have scheduled a requestAnimationFrame to dispatch all pending requests.\r\n \"scheduled\" |\r\n // We have dispatched all requests that were pending at the most recent requestAnimationFrame callback.\r\n \"in-flight\";\r\n\r\n/** Performs conversion of coordinates from one coordinate system to another.\r\n * A [[GeoConverter]] has a pair of these for converting between iModel coordinates and geographic coordinates.\r\n * Uses a cache to avoid repeatedly requesting the same points, and a batching strategy to avoid making frequent small requests.\r\n * The cache stores every point that was ever converted by [[convert]]. It is currently permitted to grow to unbounded size.\r\n * The batching works as follows:\r\n * When a conversion is requested via [[convert]], if all the requested points are in the cache, they are returned immediately.\r\n * Otherwise, any points not in the cache and not in the current in-flight request (if any) are placed onto the queue of pending requests.\r\n * A pending request is scheduled if one hasn't already been scheduled, via requestAnimationFrame.\r\n * In the animation frame callback, the pending requests are split into batches of no more than options.maxPointsPerRequest and dispatched to the backend.\r\n * Once the response is received, the results are loaded into and returned from the cache.\r\n * If more calls to convert occurred while the request was in flight, another request is dispatched.\r\n * @internal exported strictly for tests.\r\n */\r\nexport class CoordinateConverter {\r\n protected readonly _cache: Dictionary<XYAndZ, PointWithStatus>;\r\n protected _state: CoordinateConverterState = \"idle\";\r\n // The accumulated set of points to be converted by the next request.\r\n protected _pending: SortedArray<XYAndZ>;\r\n // The set of points that were included in the current in-flight request, if any.\r\n protected _inflight: SortedArray<XYAndZ>;\r\n // An event fired when the next request completes.\r\n protected _onCompleted = new BeEvent<() => void>();\r\n // Used for creating cache keys (XYAndZ) from XYZProps without having to allocate temporary objects.\r\n protected readonly _scratchXYZ = { x: 0, y: 0, z: 0 };\r\n protected readonly _maxPointsPerRequest: number;\r\n protected readonly _isIModelClosed: () => boolean;\r\n protected readonly _requestPoints: (points: XYAndZ[]) => Promise<PointWithStatus[]>;\r\n private readonly _direction: CoordinateConverterOptions[\"direction\"];\r\n // If true, [[dispatch]] will schedule another dispatch after it receives a response.\r\n // This is needed when all the points requested after the most recent dispatch were included in the currently-in-flight request -\r\n // _pending will be empty but new callers will be awaiting the results of the in-flight request.\r\n protected _redispatchOnCompletion = false;\r\n\r\n public get isIdle(): boolean {\r\n return \"idle\" === this._state;\r\n }\r\n\r\n protected toXYAndZ(input: XYZProps, output: WritableXYAndZ): XYAndZ {\r\n if (Array.isArray(input)) {\r\n output.x = input[0] ?? 0;\r\n output.y = input[1] ?? 0;\r\n output.z = input[2] ?? 0;\r\n } else {\r\n output.x = input.x ?? 0;\r\n output.y = input.y ?? 0;\r\n output.z = input.z ?? 0;\r\n }\r\n\r\n return output;\r\n }\r\n\r\n public constructor(opts: CoordinateConverterOptions) {\r\n this._maxPointsPerRequest = Math.max(1, opts.maxPointsPerRequest ?? 300);\r\n this._isIModelClosed = opts.isIModelClosed;\r\n this._requestPoints = opts.requestPoints;\r\n this._direction = opts.direction;\r\n\r\n this._cache = new Dictionary<XYAndZ, PointWithStatus>(compareXYAndZ, cloneXYAndZ);\r\n this._pending = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n this._inflight = new SortedArray<XYAndZ>(compareXYAndZ, false, cloneXYAndZ);\r\n }\r\n\r\n protected async dispatch(): Promise<void> {\r\n assert(this._state === \"scheduled\");\r\n if (this._isIModelClosed() || this._pending.isEmpty) {\r\n this._state = \"idle\";\r\n this._onCompleted.raiseEvent();\r\n return;\r\n }\r\n\r\n this._state = \"in-flight\";\r\n\r\n // Ensure subsequently-enqueued requests listen for the *next* response to be received.\r\n const onCompleted = this._onCompleted;\r\n this._onCompleted = new BeEvent<() => void>();\r\n\r\n // Pending requests are now in flight. Start a new list of pending requests. It's cheaper to swap than to allocate new objects.\r\n const inflight = this._pending;\r\n this._pending = this._inflight;\r\n assert(this._pending.isEmpty);\r\n this._inflight = inflight;\r\n\r\n // Split requests if necessary to avoid requesting more than the maximum allowed number of points.\r\n const promises: Array<Promise<void>> = [];\r\n for (let i = 0; i < inflight.length; i += this._maxPointsPerRequest) {\r\n const requests = inflight.slice(i, i + this._maxPointsPerRequest).extractArray();\r\n const promise = this._requestPoints(requests).then((results) => {\r\n if (this._isIModelClosed())\r\n return;\r\n\r\n if (results.length !== requests.length)\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, `requested conversion of ${requests.length} points, but received ${results.length} points`);\r\n\r\n for (let j = 0; j < results.length; j++) {\r\n if (j < requests.length)\r\n this._cache.set(requests[j], results[j]);\r\n }\r\n }).catch((err) => {\r\n Logger.logError(`${FrontendLoggerCategory.Package}.geoservices`, err, () => ({\r\n direction: this._direction,\r\n pointCount: requests.length,\r\n }));\r\n });\r\n\r\n promises.push(promise);\r\n }\r\n\r\n await Promise.all(promises);\r\n\r\n assert(this._state === \"in-flight\");\r\n this._state = \"idle\";\r\n this._inflight.clear();\r\n\r\n // If any more pending conversions arrived while awaiting this request, schedule another request.\r\n if (!this._pending.isEmpty || this._redispatchOnCompletion) {\r\n this._redispatchOnCompletion = false;\r\n this.scheduleDispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n }\r\n\r\n // Resolve promises of all callers who were awaiting this request.\r\n onCompleted.raiseEvent();\r\n }\r\n\r\n // Add any points not present in cache to pending request list.\r\n // Return the number of points present in cache.\r\n protected enqueue(points: XYZProps[]): number {\r\n let numInCache = 0;\r\n for (const point of points) {\r\n const xyz = this.toXYAndZ(point, this._scratchXYZ);\r\n if (this._cache.get(xyz))\r\n ++numInCache;\r\n else if (this._inflight.contains(xyz))\r\n this._redispatchOnCompletion = true;\r\n else\r\n this._pending.insert(xyz);\r\n }\r\n\r\n return numInCache;\r\n }\r\n\r\n // Obtain converted points from the cache. The assumption is that every point in `inputs` is already present in the cache.\r\n // Any point not present will be returned unconverted with an error status.\r\n protected getFromCache(inputs: XYZProps[]): PointWithStatus[] {\r\n const outputs: PointWithStatus[] = [];\r\n for (const input of inputs) {\r\n const xyz = this.toXYAndZ(input, this._scratchXYZ);\r\n let output = this._cache.get(xyz);\r\n if (!output)\r\n output = { p: { ...xyz }, s: GeoCoordStatus.CSMapError };\r\n\r\n outputs.push(output);\r\n }\r\n\r\n return outputs;\r\n }\r\n\r\n protected async scheduleDispatch(): Promise<void> {\r\n if (\"idle\" === this._state) {\r\n this._state = \"scheduled\";\r\n requestAnimationFrame(() => {\r\n this.dispatch(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n });\r\n }\r\n\r\n return new Promise((resolve) => {\r\n this._onCompleted.addOnce(() => resolve());\r\n });\r\n }\r\n\r\n public async convert(inputs: XYZProps[]): Promise<{ points: PointWithStatus[], fromCache: number }> {\r\n const fromCache = this.enqueue(inputs);\r\n assert(fromCache >= 0);\r\n assert(fromCache <= inputs.length);\r\n\r\n if (fromCache === inputs.length)\r\n return { points: this.getFromCache(inputs), fromCache };\r\n\r\n await this.scheduleDispatch();\r\n\r\n return { points: this.getFromCache(inputs), fromCache };\r\n }\r\n\r\n public findCached(inputs: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n const result: Array<PointWithStatus | undefined> = [];\r\n let missing: XYZProps[] | undefined;\r\n for (const input of inputs) {\r\n const key = this.toXYAndZ(input, this._scratchXYZ);\r\n const output = this._cache.get(key);\r\n result.push(output);\r\n if (!output) {\r\n if (!missing)\r\n missing = [];\r\n\r\n missing.push(input);\r\n }\r\n }\r\n\r\n return { result, missing };\r\n }\r\n}\r\n\r\n/** Response to a request to obtain imodel coordinates from cache.\r\n * @internal\r\n */\r\nexport interface CachedIModelCoordinatesResponseProps {\r\n /** An array of the same length as the input array, with undefined entries at indices corresponding to points not found in cache. */\r\n result: Array<PointWithStatus | undefined>;\r\n /** An array of points in the input array which were not found in the cache, or undefined if all points were found in the cache. */\r\n missing?: XYZProps[];\r\n}\r\n\r\n/** Options used to create a [[GeoConverter]].\r\n * @internal exported strictly for tests.\r\n */\r\nexport interface GeoConverterOptions {\r\n readonly datum: string;\r\n isIModelClosed: () => boolean;\r\n toIModelCoords: (request: IModelCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n fromIModelCoords: (request: GeoCoordinatesRequestProps) => Promise<PointWithStatus[]>;\r\n}\r\n\r\n/** An object capable of communicating with the backend to convert between coordinates in a geographic coordinate system and coordinates in an [[IModelConnection]]'s own coordinate system.\r\n * @see [[GeoServices.getConverter]] to obtain a converter.\r\n * @see [GeographicCRS]($common) for more information about geographic coordinate reference systems.\r\n * @public\r\n */\r\nexport class GeoConverter {\r\n private readonly _geoToIModel: CoordinateConverter;\r\n private readonly _iModelToGeo: CoordinateConverter;\r\n /** Used for removing this converter from GeoServices' cache after all requests are completed.\r\n * @internal\r\n */\r\n public readonly onAllRequestsCompleted = new BeEvent<() => void>();\r\n\r\n /** @internal */\r\n constructor(opts: GeoConverterOptions) {\r\n const isIModelClosed = opts.isIModelClosed;\r\n this._geoToIModel = new CoordinateConverter({\r\n direction: \"geoToIModel\",\r\n isIModelClosed,\r\n requestPoints: async (geoCoords: XYAndZ[]) => opts.toIModelCoords({ source: opts.datum, geoCoords }),\r\n });\r\n\r\n this._iModelToGeo = new CoordinateConverter({\r\n direction: \"iModelToGeo\",\r\n isIModelClosed,\r\n requestPoints: async (iModelCoords: XYAndZ[]) => opts.fromIModelCoords({ target: opts.datum, iModelCoords }),\r\n });\r\n }\r\n\r\n /** Convert the specified geographic coordinates into iModel coordinates. */\r\n public async convertToIModelCoords(geoPoints: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getIModelCoordinatesFromGeoCoordinates(geoPoints);\r\n return result.iModelCoords;\r\n }\r\n\r\n /** Convert the specified iModel coordinates into geographic coordinates. */\r\n public async convertFromIModelCoords(iModelCoords: XYZProps[]): Promise<PointWithStatus[]> {\r\n const result = await this.getGeoCoordinatesFromIModelCoordinates(iModelCoords);\r\n return result.geoCoords;\r\n }\r\n\r\n /** @internal */\r\n public async getIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): Promise<IModelCoordinatesResponseProps> {\r\n const result = await this._geoToIModel.convert(geoPoints);\r\n this.checkCompletion();\r\n return {\r\n iModelCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n /** @internal */\r\n public async getGeoCoordinatesFromIModelCoordinates(iModelPoints: XYZProps[]): Promise<GeoCoordinatesResponseProps> {\r\n const result = await this._iModelToGeo.convert(iModelPoints);\r\n this.checkCompletion();\r\n return {\r\n geoCoords: result.points,\r\n fromCache: result.fromCache,\r\n };\r\n }\r\n\r\n private checkCompletion(): void {\r\n if (this._geoToIModel.isIdle && this._iModelToGeo.isIdle)\r\n this.onAllRequestsCompleted.raiseEvent();\r\n }\r\n\r\n /** @internal */\r\n public getCachedIModelCoordinatesFromGeoCoordinates(geoPoints: XYZProps[]): CachedIModelCoordinatesResponseProps {\r\n return this._geoToIModel.findCached(geoPoints);\r\n }\r\n}\r\n\r\n/** @internal */\r\nexport type GeoServicesOptions = Omit<GeoConverterOptions, \"datum\">;\r\n\r\n/** The Geographic Services available for an [[IModelConnection]].\r\n * @see [[IModelConnection.geoServices]] to obtain the GeoServices for a specific iModel.\r\n * @public\r\n */\r\nexport class GeoServices {\r\n private readonly _options: GeoServicesOptions;\r\n /** Each GeoConverter has its own independent request queue and cache of previously-converted points.\r\n * Some callers like RealityTileTree obtain a single GeoConverter and reuse it throughout their own lifetime. Therefore they benefit from both batching and caching, and\r\n * the cache gets deleted once the RealityTileTree becomes disused.\r\n *\r\n * Other callers like IModelConnection.spatialToCartographic obtain a new GeoConverter every time they need one, use it to convert a single point(!), and then discard the converter.\r\n * This entirely prevents batching - e.g., calling spatialToCartographic 20 times in one frame results in 20 http requests.\r\n * To address that, we cache each GeoConverter returned by getConverter until it has converted at least one point and has no further outstanding conversion requests.\r\n * In this way, the converter lives for as long as (and no longer than) any caller is awaiting conversion to/from its datum - it and its cache are deleted once it becomes disused.\r\n * This makes the coordinate caching generally less useful, but at least bounded - and maximizes batching of requests.\r\n */\r\n private readonly _cache = new Map<string, GeoConverter>();\r\n\r\n /** @internal */\r\n public constructor(options: GeoServicesOptions) {\r\n this._options = options;\r\n }\r\n\r\n /** @internal */\r\n public static createForIModel(iModel: IModelConnection): GeoServices {\r\n return new GeoServices({\r\n isIModelClosed: () => iModel.isClosed,\r\n toIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getIModelCoordinatesFromGeoCoordinates(iModel.getRpcProps(), request);\r\n return response.iModelCoords;\r\n },\r\n fromIModelCoords: async (request) => {\r\n const rpc = IModelReadRpcInterface.getClientForRouting(iModel.routingContext.token);\r\n const response = await rpc.getGeoCoordinatesFromIModelCoordinates(iModel.getRpcProps(), request);\r\n return response.geoCoords;\r\n },\r\n });\r\n }\r\n\r\n /** Obtain a converter that can convert between a geographic coordinate system and the iModel's own coordinate system.\r\n * @param datumOrGCRS The name or JSON representation of the geographic coordinate system datum - for example, \"WGS84\".\r\n * @returns a converter, or `undefined` if the iModel is not open.\r\n * @note A [[BlankConnection]] has no connection to a backend, so it is never \"open\"; therefore it always returns `undefined`.\r\n */\r\n public getConverter(datumOrGCRS?: string | GeographicCRSProps): GeoConverter | undefined {\r\n if (this._options.isIModelClosed())\r\n return undefined;\r\n\r\n const datum = (typeof datumOrGCRS === \"object\" ? JSON.stringify(datumOrGCRS) : datumOrGCRS) ?? \"\";\r\n\r\n let converter = this._cache.get(datum);\r\n if (!converter) {\r\n converter = new GeoConverter({ ...this._options, datum });\r\n this._cache.set(datum, converter);\r\n\r\n converter.onAllRequestsCompleted.addOnce(() => {\r\n if (converter === this._cache.get(datum))\r\n this._cache.delete(datum);\r\n });\r\n }\r\n\r\n return converter;\r\n }\r\n}\r\n"]}
|
|
@@ -34,8 +34,6 @@ export declare class ExtensionAdmin {
|
|
|
34
34
|
* @param hostUrl (string) Accepts both URLs and hostnames (e.g., http://localhost:3000, yourdomain.com, https://www.yourdomain.com, etc.).
|
|
35
35
|
*/
|
|
36
36
|
registerHost(hostUrl: string): void;
|
|
37
|
-
/** Returns the hostname of an input string. Throws an error if input is not a valid hostname (or URL). */
|
|
38
|
-
private getHostName;
|
|
39
37
|
/** Loops over all enabled Extensions and triggers each one if the provided event is defined. */
|
|
40
38
|
private activateExtensionEvents;
|
|
41
39
|
/** Executes the extension. Catches and logs any errors (so that an extension will not crash the main application). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionAdmin.d.ts","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":"AAIA;;GAEG;AAKH,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ExtensionAdmin.d.ts","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":"AAIA;;GAEG;AAKH,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA0DxE;;;GAGG;AACH,qBAAa,cAAc;IACzB,2GAA2G;IAC3G,OAAO,CAAC,WAAW,CAA0E;IAC7F,OAAO,CAAC,MAAM,CAAW;IAEzB;;OAEG;IACI,SAAS,sBAEd;;IAMF;;;;;OAKG;IACU,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBrE;;;;OAIG;IACU,aAAa,CAAC,SAAS,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAM3E;;;;OAIG;IACI,YAAY,CAAC,OAAO,EAAE,MAAM;IAOnC,gGAAgG;YAClF,uBAAuB;IAarC,sHAAsH;YACxG,QAAQ;CAOvB"}
|
|
@@ -7,6 +7,45 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Logger } from "@itwin/core-bentley";
|
|
9
9
|
import { FrontendLoggerCategory } from "../common/FrontendLoggerCategory";
|
|
10
|
+
/** The Extensions loading system has the following goals:
|
|
11
|
+
* 1. Only fetch what is needed when it is required
|
|
12
|
+
* 1. Load a manifest file
|
|
13
|
+
* 2. Load the the main module when necessary (usually at an activation event)
|
|
14
|
+
* 2. Download the extension's files
|
|
15
|
+
*
|
|
16
|
+
* 3 ways to load an Extension into the system:
|
|
17
|
+
*
|
|
18
|
+
* 1. Load both the Extension Manifest and import the main module of the extension from a local file/package.
|
|
19
|
+
* 2. A minimum set of properties to get the manifest and javascript from a remote server.
|
|
20
|
+
* 3. A minimum set of properties to get the manifest and javascript from Bentley's Extension Service.
|
|
21
|
+
*
|
|
22
|
+
* An Extension must be added to ExtensionAdmin before it can be executed during activation events.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes hostnames for comparison.
|
|
26
|
+
* @returns The normalized hostname of a URL or bare hostname.
|
|
27
|
+
* @throws Error if the input cannot be parsed, or does not contain a hostname.
|
|
28
|
+
*/
|
|
29
|
+
function normalizeHostname(input) {
|
|
30
|
+
const invalid = () => new Error(`"${input}" is not a valid URL or hostname (i.e. http://localhost:3000, yourdomain.com, etc.).`);
|
|
31
|
+
// inputs without a scheme (e.g., https://) will throw an error in the URL constructor
|
|
32
|
+
const inputWithScheme = /^[a-z][a-z0-9+\-.]*:\/\//i.test(input) ? input : `https://${input}`;
|
|
33
|
+
let hostname;
|
|
34
|
+
try {
|
|
35
|
+
hostname = new URL(inputWithScheme).hostname.toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw invalid();
|
|
39
|
+
}
|
|
40
|
+
// strip only a leading "www." label - removing "www" anywhere would make unrelated
|
|
41
|
+
// hosts like "wwwexample.com" compare equal to "example.com"
|
|
42
|
+
const normalized = hostname.startsWith("www.") ? hostname.substring("www.".length) : hostname;
|
|
43
|
+
// schemes with an optional or opaque host (e.g. file:///, data:) parse successfully but carry no
|
|
44
|
+
// hostname, and an input of just "www." normalizes to nothing. Neither can identify a host.
|
|
45
|
+
if (normalized.length === 0)
|
|
46
|
+
throw invalid();
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
10
49
|
/** The Extension Admin controls the list of currently loaded Extensions.
|
|
11
50
|
*
|
|
12
51
|
* @alpha
|
|
@@ -31,10 +70,10 @@ export class ExtensionAdmin {
|
|
|
31
70
|
* @alpha
|
|
32
71
|
*/
|
|
33
72
|
async addExtension(provider) {
|
|
34
|
-
if (provider.hostname) {
|
|
35
|
-
const
|
|
36
|
-
if (this._hosts.
|
|
37
|
-
throw new Error(`Error loading extension: ${
|
|
73
|
+
if (provider.hostname !== undefined && this._hosts.length > 0) {
|
|
74
|
+
const hostname = normalizeHostname(provider.hostname);
|
|
75
|
+
if (this._hosts.indexOf(hostname) < 0) {
|
|
76
|
+
throw new Error(`Error loading extension: ${provider.hostname} was not registered.`);
|
|
38
77
|
}
|
|
39
78
|
}
|
|
40
79
|
try {
|
|
@@ -65,28 +104,11 @@ export class ExtensionAdmin {
|
|
|
65
104
|
* @param hostUrl (string) Accepts both URLs and hostnames (e.g., http://localhost:3000, yourdomain.com, https://www.yourdomain.com, etc.).
|
|
66
105
|
*/
|
|
67
106
|
registerHost(hostUrl) {
|
|
68
|
-
const hostname =
|
|
107
|
+
const hostname = normalizeHostname(hostUrl);
|
|
69
108
|
if (this._hosts.indexOf(hostname) < 0) {
|
|
70
109
|
this._hosts.push(hostname);
|
|
71
110
|
}
|
|
72
111
|
}
|
|
73
|
-
/** Returns the hostname of an input string. Throws an error if input is not a valid hostname (or URL). */
|
|
74
|
-
getHostName(inputUrl) {
|
|
75
|
-
// inputs without a protocol (e.g., http://) will throw an error in URL constructor
|
|
76
|
-
const inputWithProtocol = /(http|https):\/\//.test(inputUrl) ?
|
|
77
|
-
inputUrl :
|
|
78
|
-
`https://${inputUrl}`;
|
|
79
|
-
try {
|
|
80
|
-
const hostname = new URL(inputWithProtocol).hostname.replace("www.", "");
|
|
81
|
-
return hostname;
|
|
82
|
-
}
|
|
83
|
-
catch (e) {
|
|
84
|
-
if (e instanceof TypeError) {
|
|
85
|
-
throw new Error("Argument hostUrl should be a valid URL or hostname (i.e. http://localhost:3000, yourdomain.com, etc.).");
|
|
86
|
-
}
|
|
87
|
-
throw e;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
112
|
/** Loops over all enabled Extensions and triggers each one if the provided event is defined. */
|
|
91
113
|
async activateExtensionEvents(event) {
|
|
92
114
|
for (const extension of this._extensions.values()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionAdmin.js","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":"AAAA;;;+FAG+F;AAC/F;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"ExtensionAdmin.js","sourceRoot":"","sources":["../../../src/extension/ExtensionAdmin.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module Extensions\r\n */\r\n\r\nimport { Logger } from \"@itwin/core-bentley\";\r\n\r\nimport { FrontendLoggerCategory } from \"../common/FrontendLoggerCategory\";\r\nimport type { ExtensionManifest, ExtensionProvider } from \"./Extension\";\r\n\r\n/** The Extensions loading system has the following goals:\r\n * 1. Only fetch what is needed when it is required\r\n * 1. Load a manifest file\r\n * 2. Load the the main module when necessary (usually at an activation event)\r\n * 2. Download the extension's files\r\n *\r\n * 3 ways to load an Extension into the system:\r\n *\r\n * 1. Load both the Extension Manifest and import the main module of the extension from a local file/package.\r\n * 2. A minimum set of properties to get the manifest and javascript from a remote server.\r\n * 3. A minimum set of properties to get the manifest and javascript from Bentley's Extension Service.\r\n *\r\n * An Extension must be added to ExtensionAdmin before it can be executed during activation events.\r\n */\r\n\r\n/**\r\n * Normalizes hostnames for comparison.\r\n * @returns The normalized hostname of a URL or bare hostname.\r\n * @throws Error if the input cannot be parsed, or does not contain a hostname.\r\n */\r\nfunction normalizeHostname(input: string): string {\r\n const invalid = () => new Error(`\"${input}\" is not a valid URL or hostname (i.e. http://localhost:3000, yourdomain.com, etc.).`);\r\n\r\n // inputs without a scheme (e.g., https://) will throw an error in the URL constructor\r\n const inputWithScheme = /^[a-z][a-z0-9+\\-.]*:\\/\\//i.test(input) ? input : `https://${input}`;\r\n\r\n let hostname;\r\n try {\r\n hostname = new URL(inputWithScheme).hostname.toLowerCase();\r\n } catch {\r\n throw invalid();\r\n }\r\n\r\n // strip only a leading \"www.\" label - removing \"www\" anywhere would make unrelated\r\n // hosts like \"wwwexample.com\" compare equal to \"example.com\"\r\n const normalized = hostname.startsWith(\"www.\") ? hostname.substring(\"www.\".length) : hostname;\r\n\r\n // schemes with an optional or opaque host (e.g. file:///, data:) parse successfully but carry no\r\n // hostname, and an input of just \"www.\" normalizes to nothing. Neither can identify a host.\r\n if (normalized.length === 0)\r\n throw invalid();\r\n\r\n return normalized;\r\n}\r\n\r\n/**\r\n * A \"ready to use\" Extension (contains a manifest object and an extension provider to help execute).\r\n * Will be used as the type for in-memory extensions in the ExtensionAdmin\r\n */\r\ninterface InstalledExtension {\r\n /** An extension provider that has been added to ExtensionAdmin */\r\n provider: ExtensionProvider;\r\n /** The manifest (package.json) of the extension */\r\n manifest: ExtensionManifest;\r\n}\r\n\r\n/** The Extension Admin controls the list of currently loaded Extensions.\r\n *\r\n * @alpha\r\n */\r\nexport class ExtensionAdmin {\r\n /** Defines the set of extensions that are currently known and can be invoked during activation events. */\r\n private _extensions: Map<string, InstalledExtension> = new Map<string, InstalledExtension>();\r\n private _hosts: string[];\r\n\r\n /** Fired when an Extension has been added or removed.\r\n * @internal\r\n */\r\n public onStartup = async () => {\r\n await this.activateExtensionEvents(\"onStartup\");\r\n };\r\n\r\n public constructor() {\r\n this._hosts = [];\r\n }\r\n\r\n /**\r\n * Adds an extension.\r\n * The manifest will be fetched and the extension will be activated on an activation event.\r\n * @param provider\r\n * @alpha\r\n */\r\n public async addExtension(provider: ExtensionProvider): Promise<void> {\r\n if (provider.hostname !== undefined && this._hosts.length > 0) {\r\n const hostname = normalizeHostname(provider.hostname);\r\n if (this._hosts.indexOf(hostname) < 0) {\r\n throw new Error(`Error loading extension: ${provider.hostname} was not registered.`);\r\n }\r\n }\r\n try {\r\n const manifest = await provider.getManifest();\r\n this._extensions.set(manifest.name, {\r\n manifest,\r\n provider,\r\n });\r\n // TODO - temporary fix to execute the missed startup event\r\n if (manifest.activationEvents.includes(\"onStartup\"))\r\n provider.execute(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n } catch (e) {\r\n throw new Error(`Failed to get extension manifest ${provider.hostname ? `at ${provider.hostname}` : \"\"}: ${String(e)}`);\r\n }\r\n }\r\n\r\n /**\r\n * Adds a list of extensions\r\n * @param providers\r\n * @alpha\r\n */\r\n public async addExtensions(providers: ExtensionProvider[]): Promise<void[]> {\r\n return Promise.all(\r\n providers.map(async (provider) => this.addExtension(provider)),\r\n );\r\n }\r\n\r\n /**\r\n * Registers a hostname for an extension.\r\n * Once a hostname has been registered, only remote extensions from registered hosts are permitted to be added.\r\n * @param hostUrl (string) Accepts both URLs and hostnames (e.g., http://localhost:3000, yourdomain.com, https://www.yourdomain.com, etc.).\r\n */\r\n public registerHost(hostUrl: string) {\r\n const hostname = normalizeHostname(hostUrl);\r\n if (this._hosts.indexOf(hostname) < 0) {\r\n this._hosts.push(hostname);\r\n }\r\n }\r\n\r\n /** Loops over all enabled Extensions and triggers each one if the provided event is defined. */\r\n private async activateExtensionEvents(event: string) {\r\n for (const extension of this._extensions.values()) {\r\n if (!extension.manifest.activationEvents)\r\n continue;\r\n\r\n for (const activationEvent of extension.manifest.activationEvents) {\r\n if (activationEvent === event) {\r\n this._execute(extension); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** Executes the extension. Catches and logs any errors (so that an extension will not crash the main application). */\r\n private async _execute(extension: InstalledExtension) {\r\n try {\r\n await extension.provider.execute();\r\n } catch (e) {\r\n Logger.logError(FrontendLoggerCategory.Extensions, `Error executing extension ${extension.manifest.name}: ${String(e)}`);\r\n }\r\n }\r\n}\r\n"]}
|
|
@@ -17,7 +17,7 @@ export interface RemoteExtensionProviderProps {
|
|
|
17
17
|
*/
|
|
18
18
|
export declare class RemoteExtensionProvider implements ExtensionProvider {
|
|
19
19
|
private readonly _props;
|
|
20
|
-
/** The
|
|
20
|
+
/** The hostname of the server where the extension is hosted. */
|
|
21
21
|
readonly hostname: string;
|
|
22
22
|
constructor(_props: RemoteExtensionProviderProps);
|
|
23
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteExtensionProvider.d.ts","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAItB;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAInD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,
|
|
1
|
+
{"version":3,"file":"RemoteExtensionProvider.d.ts","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAItB;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAInD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,gEAAgE;IAChE,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAEJ,MAAM,EAAE,4BAA4B;IAIjE;;;OAGG;IACU,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAIvC;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAIvD"}
|
|
@@ -8,11 +8,11 @@ import { loadScript } from "./ExtensionLoadScript";
|
|
|
8
8
|
*/
|
|
9
9
|
export class RemoteExtensionProvider {
|
|
10
10
|
_props;
|
|
11
|
-
/** The
|
|
11
|
+
/** The hostname of the server where the extension is hosted. */
|
|
12
12
|
hostname;
|
|
13
13
|
constructor(_props) {
|
|
14
14
|
this._props = _props;
|
|
15
|
-
this.hostname = new URL(this._props.jsUrl).hostname
|
|
15
|
+
this.hostname = new URL(this._props.jsUrl).hostname;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Attempts to execute an extension.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteExtensionProvider.js","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAanD;;;;;GAKG;AACH,MAAM,OAAO,uBAAuB;IAIL;IAH7B,
|
|
1
|
+
{"version":3,"file":"RemoteExtensionProvider.js","sourceRoot":"","sources":["../../../../src/extension/providers/RemoteExtensionProvider.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAanD;;;;;GAKG;AACH,MAAM,OAAO,uBAAuB;IAIL;IAH7B,gEAAgE;IAChD,QAAQ,CAAS;IAEjC,YAA6B,MAAoC;QAApC,WAAM,GAAN,MAAM,CAA8B;QAC/D,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC;IACtD,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,OAAO;QAClB,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW;QACtB,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAClD,CAAC;CAEF","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\nimport type {\r\n ExtensionManifest,\r\n ExtensionProvider,\r\n} from \"../Extension\";\r\nimport { request } from \"../../request/Request\";\r\nimport { loadScript } from \"./ExtensionLoadScript\";\r\n\r\n/**\r\n * Required props for a remote extension provider\r\n * @alpha\r\n */\r\nexport interface RemoteExtensionProviderProps {\r\n /** URL where the extension entry point can be loaded from */\r\n jsUrl: string;\r\n /** URL where the manifest (package.json) can be loaded from */\r\n manifestUrl: string;\r\n}\r\n\r\n/**\r\n * Implements a \"remote\" extension.\r\n * Remote extensions are hosted on an external server.\r\n * The execute() and getManifest() methods are used by the ExtensionAdmin to load and execute the extension.\r\n * @alpha\r\n */\r\nexport class RemoteExtensionProvider implements ExtensionProvider {\r\n /** The hostname of the server where the extension is hosted. */\r\n public readonly hostname: string;\r\n\r\n constructor(private readonly _props: RemoteExtensionProviderProps) {\r\n this.hostname = new URL(this._props.jsUrl).hostname;\r\n }\r\n\r\n /**\r\n * Attempts to execute an extension.\r\n * Throws an error if the provided jsUrl is not accessible.\r\n */\r\n public async execute(): Promise<string> {\r\n return loadScript(this._props.jsUrl);\r\n }\r\n\r\n /**\r\n * Attempts to fetch an extension's manifest (package.json) file.\r\n * Throws an error if the provided manifestUrl is not accessible.\r\n */\r\n public async getManifest(): Promise<ExtensionManifest> {\r\n return request(this._props.manifestUrl, \"json\");\r\n }\r\n\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itwin/core-frontend",
|
|
3
|
-
"version": "5.14.0-dev.
|
|
3
|
+
"version": "5.14.0-dev.14",
|
|
4
4
|
"description": "iTwin.js frontend components",
|
|
5
5
|
"main": "lib/cjs/core-frontend.js",
|
|
6
6
|
"module": "lib/esm/core-frontend.js",
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
"url": "http://www.bentley.com"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@itwin/
|
|
27
|
-
"@itwin/core-
|
|
28
|
-
"@itwin/
|
|
29
|
-
"@itwin/core-
|
|
30
|
-
"@itwin/core-
|
|
31
|
-
"@itwin/core-
|
|
32
|
-
"@itwin/ecschema-metadata": "5.14.0-dev.
|
|
33
|
-
"@itwin/ecschema-rpcinterface-common": "5.14.0-dev.
|
|
26
|
+
"@itwin/appui-abstract": "5.14.0-dev.14",
|
|
27
|
+
"@itwin/core-common": "5.14.0-dev.14",
|
|
28
|
+
"@itwin/core-orbitgt": "5.14.0-dev.14",
|
|
29
|
+
"@itwin/core-quantity": "5.14.0-dev.14",
|
|
30
|
+
"@itwin/core-bentley": "5.14.0-dev.14",
|
|
31
|
+
"@itwin/core-geometry": "5.14.0-dev.14",
|
|
32
|
+
"@itwin/ecschema-metadata": "5.14.0-dev.14",
|
|
33
|
+
"@itwin/ecschema-rpcinterface-common": "5.14.0-dev.14"
|
|
34
34
|
},
|
|
35
35
|
"//devDependencies": [
|
|
36
36
|
"NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install",
|
|
@@ -55,15 +55,15 @@
|
|
|
55
55
|
"vite": "^6.4.3",
|
|
56
56
|
"vitest": "^4.1.10",
|
|
57
57
|
"vite-plugin-static-copy": "2.2.0",
|
|
58
|
-
"@itwin/
|
|
59
|
-
"@itwin/build-tools": "5.14.0-dev.
|
|
60
|
-
"@itwin/
|
|
61
|
-
"@itwin/core-
|
|
62
|
-
"@itwin/core-orbitgt": "5.14.0-dev.
|
|
63
|
-
"@itwin/
|
|
64
|
-
"@itwin/ecschema-
|
|
65
|
-
"@itwin/
|
|
66
|
-
"@itwin/core-
|
|
58
|
+
"@itwin/appui-abstract": "5.14.0-dev.14",
|
|
59
|
+
"@itwin/build-tools": "5.14.0-dev.14",
|
|
60
|
+
"@itwin/core-geometry": "5.14.0-dev.14",
|
|
61
|
+
"@itwin/core-bentley": "5.14.0-dev.14",
|
|
62
|
+
"@itwin/core-orbitgt": "5.14.0-dev.14",
|
|
63
|
+
"@itwin/core-quantity": "5.14.0-dev.14",
|
|
64
|
+
"@itwin/ecschema-metadata": "5.14.0-dev.14",
|
|
65
|
+
"@itwin/ecschema-rpcinterface-common": "5.14.0-dev.14",
|
|
66
|
+
"@itwin/core-common": "5.14.0-dev.14"
|
|
67
67
|
},
|
|
68
68
|
"//dependencies": [
|
|
69
69
|
"NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"@loaders.gl/draco": "^4.4.5",
|
|
75
75
|
"fuse.js": "^3.3.0",
|
|
76
76
|
"wms-capabilities": "0.6.0",
|
|
77
|
-
"@itwin/core-i18n": "5.14.0-dev.
|
|
78
|
-
"@itwin/webgl-compatibility": "5.14.0-dev.
|
|
77
|
+
"@itwin/core-i18n": "5.14.0-dev.14",
|
|
78
|
+
"@itwin/webgl-compatibility": "5.14.0-dev.14"
|
|
79
79
|
},
|
|
80
80
|
"scripts": {
|
|
81
81
|
"build": "npm run -s copy:public && npm run -s build:cjs && npm run -s build:esm && npm run -s build:workers && npm run -s copy:draco",
|