@honua/react 0.1.0-beta.0 → 0.1.1-beta.0
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/contract/discovery.d.ts +231 -0
- package/contract/discovery.d.ts.map +1 -0
- package/contract/discovery.js +554 -0
- package/contract/edit-sketch.d.ts +8 -0
- package/contract/edit-sketch.d.ts.map +1 -1
- package/contract/edit-sketch.js +15 -0
- package/contract/edit-snapping.d.ts +160 -0
- package/contract/edit-snapping.d.ts.map +1 -0
- package/contract/edit-snapping.js +675 -0
- package/contract/generated/projjson-v0.7-crs-validator.d.ts +35 -0
- package/contract/generated/projjson-v0.7-crs-validator.d.ts.map +1 -0
- package/contract/generated/projjson-v0.7-crs-validator.js +3941 -0
- package/contract/index.d.ts +11 -2
- package/contract/index.d.ts.map +1 -1
- package/contract/index.js +4 -1
- package/contract/pmtiles.d.ts +31 -0
- package/contract/pmtiles.d.ts.map +1 -1
- package/contract/pmtiles.js +60 -20
- package/contract/protocol-module.d.ts +70 -0
- package/contract/protocol-module.d.ts.map +1 -0
- package/contract/protocol-module.js +1 -0
- package/contract/schema-envelope.d.ts +26 -0
- package/contract/schema-envelope.d.ts.map +1 -0
- package/contract/schema-envelope.js +25 -0
- package/contract/schema.d.ts +582 -0
- package/contract/schema.d.ts.map +1 -0
- package/contract/schema.js +2483 -0
- package/contract/source-capability-support.d.ts +6 -0
- package/contract/source-capability-support.d.ts.map +1 -0
- package/contract/source-capability-support.js +154 -0
- package/contract/source.d.ts +35 -17
- package/contract/source.d.ts.map +1 -1
- package/contract/source.js +267 -71
- package/contract/types.d.ts +140 -4
- package/contract/types.d.ts.map +1 -1
- package/contract/types.js +6 -14
- package/core/bounded-json.d.ts +4 -0
- package/core/bounded-json.d.ts.map +1 -0
- package/core/bounded-json.js +38 -0
- package/core/cache-state.d.ts +9 -0
- package/core/cache-state.d.ts.map +1 -1
- package/core/cache-state.js +10 -0
- package/core/capabilities-xml.d.ts +33 -0
- package/core/capabilities-xml.d.ts.map +1 -0
- package/core/capabilities-xml.js +331 -0
- package/core/client.d.ts +4 -1
- package/core/client.d.ts.map +1 -1
- package/core/client.js +115 -29
- package/core/error-classifications.d.ts +153 -0
- package/core/error-classifications.d.ts.map +1 -0
- package/core/error-classifications.js +140 -0
- package/core/error-code-registry.d.ts +158 -0
- package/core/error-code-registry.d.ts.map +1 -0
- package/core/error-code-registry.js +143 -0
- package/core/error-envelope.d.ts +68 -0
- package/core/error-envelope.d.ts.map +1 -0
- package/core/error-envelope.js +357 -0
- package/core/errors.d.ts +45 -29
- package/core/errors.d.ts.map +1 -1
- package/core/errors.js +97 -50
- package/core/geoparquet-sql.d.ts +30 -2
- package/core/geoparquet-sql.d.ts.map +1 -1
- package/core/geoparquet-sql.js +38 -3
- package/core/odata-key-path.d.ts +12 -0
- package/core/odata-key-path.d.ts.map +1 -0
- package/core/odata-key-path.js +28 -0
- package/core/odata-write-codec.d.ts +66 -0
- package/core/odata-write-codec.d.ts.map +1 -0
- package/core/odata-write-codec.js +1083 -0
- package/core/odata.d.ts +59 -7
- package/core/odata.d.ts.map +1 -1
- package/core/odata.js +431 -99
- package/core/ogc-features.d.ts.map +1 -1
- package/core/ogc-features.js +3 -0
- package/core/ogc-maps.d.ts +7 -0
- package/core/ogc-maps.d.ts.map +1 -1
- package/core/ogc-maps.js +46 -7
- package/core/ogc-processes.d.ts +2 -1
- package/core/ogc-processes.d.ts.map +1 -1
- package/core/ogc-processes.js +33 -5
- package/core/ogc-records.d.ts +9 -0
- package/core/ogc-records.d.ts.map +1 -1
- package/core/ogc-records.js +78 -29
- package/core/ogc-tiles.d.ts +10 -1
- package/core/ogc-tiles.d.ts.map +1 -1
- package/core/ogc-tiles.js +56 -17
- package/core/path-utils.d.ts +9 -0
- package/core/path-utils.d.ts.map +1 -1
- package/core/path-utils.js +19 -0
- package/core/protocol-transport.d.ts +1 -0
- package/core/protocol-transport.d.ts.map +1 -1
- package/core/request-pipeline.d.ts.map +1 -1
- package/core/request-pipeline.js +1 -1
- package/core/spatial-filter.d.ts +8 -4
- package/core/spatial-filter.d.ts.map +1 -1
- package/core/spatial-filter.js +206 -16
- package/core/stac-static.d.ts +6 -2
- package/core/stac-static.d.ts.map +1 -1
- package/core/stac-static.js +260 -69
- package/core/surfaces.d.ts.map +1 -1
- package/core/surfaces.js +69 -5
- package/core/types.d.ts +125 -7
- package/core/types.d.ts.map +1 -1
- package/core/wfs-capabilities.d.ts +2 -0
- package/core/wfs-capabilities.d.ts.map +1 -1
- package/core/wfs-capabilities.js +8 -1
- package/core/wfs.d.ts.map +1 -1
- package/core/wfs.js +10 -0
- package/core/wms-capabilities.d.ts +56 -84
- package/core/wms-capabilities.d.ts.map +1 -1
- package/core/wms-capabilities.js +212 -509
- package/core/wmts-capabilities.d.ts +62 -62
- package/core/wmts-capabilities.d.ts.map +1 -1
- package/core/wmts-capabilities.js +224 -371
- package/esri-compat/editor.d.ts +8 -0
- package/esri-compat/editor.d.ts.map +1 -1
- package/esri-compat/editor.js +13 -0
- package/esri-compat/feature-layer.d.ts +2 -0
- package/esri-compat/feature-layer.d.ts.map +1 -1
- package/esri-compat/feature-layer.js +1 -0
- package/esri-compat/layer-list.d.ts +11 -0
- package/esri-compat/layer-list.d.ts.map +1 -1
- package/esri-compat/layer-list.js +49 -0
- package/esri-compat/legend.d.ts +10 -0
- package/esri-compat/legend.d.ts.map +1 -1
- package/esri-compat/legend.js +27 -0
- package/esri-compat/portal.d.ts +197 -0
- package/esri-compat/portal.d.ts.map +1 -0
- package/esri-compat/portal.js +251 -0
- package/esri-compat/renderer-objects.d.ts +40 -0
- package/esri-compat/renderer-objects.d.ts.map +1 -0
- package/esri-compat/renderer-objects.js +62 -0
- package/esri-compat/sketch.d.ts +43 -0
- package/esri-compat/sketch.d.ts.map +1 -1
- package/esri-compat/sketch.js +46 -0
- package/esri-compat/snapping.d.ts +43 -0
- package/esri-compat/snapping.d.ts.map +1 -0
- package/esri-compat/snapping.js +45 -0
- package/esri-compat/url.d.ts.map +1 -1
- package/esri-compat/url.js +1 -1
- package/esri-compat/widget-host.d.ts +84 -0
- package/esri-compat/widget-host.d.ts.map +1 -0
- package/esri-compat/widget-host.js +159 -0
- package/filter-registry/index.d.ts +145 -0
- package/filter-registry/index.d.ts.map +1 -0
- package/filter-registry/index.js +531 -0
- package/generated-app/errors.d.ts +18 -2
- package/generated-app/errors.d.ts.map +1 -1
- package/generated-app/errors.js +44 -11
- package/map/automatic-mount-integration.d.ts +134 -0
- package/map/automatic-mount-integration.d.ts.map +1 -0
- package/map/automatic-mount-integration.js +305 -0
- package/map/automatic-source-strategy.d.ts +125 -0
- package/map/automatic-source-strategy.d.ts.map +1 -0
- package/map/automatic-source-strategy.js +494 -0
- package/map/data-to-map-bridge.d.ts +293 -0
- package/map/data-to-map-bridge.d.ts.map +1 -0
- package/map/data-to-map-bridge.js +817 -0
- package/map/geojson-projection.d.ts +33 -0
- package/map/geojson-projection.d.ts.map +1 -0
- package/map/geojson-projection.js +118 -0
- package/map/index.d.ts +14 -0
- package/map/index.d.ts.map +1 -1
- package/map/index.js +8 -0
- package/map/raster-source-spec.d.ts +30 -0
- package/map/raster-source-spec.d.ts.map +1 -0
- package/map/raster-source-spec.js +190 -0
- package/map/raster-source-strategy.d.ts +65 -0
- package/map/raster-source-strategy.d.ts.map +1 -0
- package/map/raster-source-strategy.js +232 -0
- package/map/source-to-maplibre.d.ts +91 -0
- package/map/source-to-maplibre.d.ts.map +1 -0
- package/map/source-to-maplibre.js +454 -0
- package/map/temporal-playback.d.ts +139 -0
- package/map/temporal-playback.d.ts.map +1 -0
- package/map/temporal-playback.js +247 -0
- package/package.json +14 -1
- package/query-planner/canonical.d.ts +7 -0
- package/query-planner/canonical.d.ts.map +1 -0
- package/query-planner/canonical.js +145 -0
- package/query-planner/cql2-json.d.ts +17 -0
- package/query-planner/cql2-json.d.ts.map +1 -0
- package/query-planner/cql2-json.js +646 -0
- package/query-planner/diagnostics.d.ts +35 -0
- package/query-planner/diagnostics.d.ts.map +1 -0
- package/query-planner/diagnostics.js +373 -0
- package/query-planner/duckdb.d.ts +79 -0
- package/query-planner/duckdb.d.ts.map +1 -0
- package/query-planner/duckdb.js +758 -0
- package/query-planner/execution-context.d.ts +5 -0
- package/query-planner/execution-context.d.ts.map +1 -0
- package/query-planner/execution-context.js +50 -0
- package/query-planner/executor.d.ts +5 -0
- package/query-planner/executor.d.ts.map +1 -0
- package/query-planner/executor.js +223 -0
- package/query-planner/geoservices.d.ts +91 -0
- package/query-planner/geoservices.d.ts.map +1 -0
- package/query-planner/geoservices.js +667 -0
- package/query-planner/grpc.d.ts +100 -0
- package/query-planner/grpc.d.ts.map +1 -0
- package/query-planner/grpc.js +736 -0
- package/query-planner/index.d.ts +44 -0
- package/query-planner/index.d.ts.map +1 -0
- package/query-planner/index.js +30 -0
- package/query-planner/ir.d.ts +20 -0
- package/query-planner/ir.d.ts.map +1 -0
- package/query-planner/ir.js +417 -0
- package/query-planner/local-aggregate.d.ts +5 -0
- package/query-planner/local-aggregate.d.ts.map +1 -0
- package/query-planner/local-aggregate.js +126 -0
- package/query-planner/odata.d.ts +48 -0
- package/query-planner/odata.d.ts.map +1 -0
- package/query-planner/odata.js +662 -0
- package/query-planner/ogc-compiler.d.ts +55 -0
- package/query-planner/ogc-compiler.d.ts.map +1 -0
- package/query-planner/ogc-compiler.js +480 -0
- package/query-planner/ogc-features.d.ts +53 -0
- package/query-planner/ogc-features.d.ts.map +1 -0
- package/query-planner/ogc-features.js +617 -0
- package/query-planner/planner.d.ts +17 -0
- package/query-planner/planner.d.ts.map +1 -0
- package/query-planner/planner.js +818 -0
- package/query-planner/resource.d.ts +94 -0
- package/query-planner/resource.d.ts.map +1 -0
- package/query-planner/resource.js +526 -0
- package/query-planner/semantic-canonical.d.ts +15 -0
- package/query-planner/semantic-canonical.d.ts.map +1 -0
- package/query-planner/semantic-canonical.js +62 -0
- package/query-planner/semantic-compat.d.ts +10 -0
- package/query-planner/semantic-compat.d.ts.map +1 -0
- package/query-planner/semantic-compat.js +35 -0
- package/query-planner/semantic-compiler.d.ts +64 -0
- package/query-planner/semantic-compiler.d.ts.map +1 -0
- package/query-planner/semantic-compiler.js +81 -0
- package/query-planner/semantic-literals.d.ts +30 -0
- package/query-planner/semantic-literals.d.ts.map +1 -0
- package/query-planner/semantic-literals.js +133 -0
- package/query-planner/semantic-types.d.ts +265 -0
- package/query-planner/semantic-types.d.ts.map +1 -0
- package/query-planner/semantic-types.js +1 -0
- package/query-planner/semantic.d.ts +54 -0
- package/query-planner/semantic.d.ts.map +1 -0
- package/query-planner/semantic.js +1421 -0
- package/query-planner/types.d.ts +560 -0
- package/query-planner/types.d.ts.map +1 -0
- package/query-planner/types.js +49 -0
- package/query-planner/wfs-semantic.d.ts +58 -0
- package/query-planner/wfs-semantic.d.ts.map +1 -0
- package/query-planner/wfs-semantic.js +547 -0
- package/query-planner/wfs.d.ts +6 -0
- package/query-planner/wfs.d.ts.map +1 -0
- package/query-planner/wfs.js +71 -0
- package/react/external-map.d.ts +67 -0
- package/react/external-map.d.ts.map +1 -0
- package/react/external-map.js +63 -0
- package/react/honua-map.d.ts.map +1 -1
- package/react/honua-map.js +3 -2
- package/react/honua-source-layer.d.ts +110 -0
- package/react/honua-source-layer.d.ts.map +1 -0
- package/react/honua-source-layer.js +101 -0
- package/react/hooks.d.ts +28 -0
- package/react/hooks.d.ts.map +1 -1
- package/react/hooks.js +26 -4
- package/react/index.d.ts +8 -0
- package/react/index.d.ts.map +1 -1
- package/react/index.js +5 -0
- package/react/query-cache.d.ts +26 -4
- package/react/query-cache.d.ts.map +1 -1
- package/react/query-cache.js +51 -8
- package/react/selection.d.ts +177 -0
- package/react/selection.d.ts.map +1 -0
- package/react/selection.js +0 -0
- package/react/use-mounted-source.d.ts +69 -0
- package/react/use-mounted-source.d.ts.map +1 -0
- package/react/use-mounted-source.js +346 -0
- package/runtime/edit-snapping.d.ts +107 -0
- package/runtime/edit-snapping.d.ts.map +1 -0
- package/runtime/edit-snapping.js +190 -0
- package/runtime/errors.d.ts +3 -2
- package/runtime/errors.d.ts.map +1 -1
- package/runtime/errors.js +19 -2
- package/runtime/index.d.ts +6 -0
- package/runtime/index.d.ts.map +1 -1
- package/runtime/index.js +3 -0
- package/runtime/maplibre-renderer.d.ts +41 -0
- package/runtime/maplibre-renderer.d.ts.map +1 -0
- package/runtime/maplibre-renderer.js +322 -0
- package/runtime/query-tiles.d.ts +2 -1
- package/runtime/query-tiles.d.ts.map +1 -1
- package/runtime/query-tiles.js +6 -2
- package/runtime/source-bridge.d.ts +4 -4
- package/runtime/source-bridge.d.ts.map +1 -1
- package/runtime/source-bridge.js +9 -56
- package/runtime/style-interactions.d.ts +3 -2
- package/runtime/style-interactions.d.ts.map +1 -1
- package/runtime/style-interactions.js +11 -3
- package/runtime/terra-draw-sketch.d.ts +275 -0
- package/runtime/terra-draw-sketch.d.ts.map +1 -0
- package/runtime/terra-draw-sketch.js +404 -0
- package/source-capability-registry.d.ts +1 -0
- package/source-capability-registry.js +1 -0
- package/source-capability-types.d.ts +186 -0
- package/source-capability-types.js +7 -0
- package/style/index.d.ts +2 -0
- package/style/index.d.ts.map +1 -1
- package/style/index.js +2 -0
- package/style/renderers.d.ts +326 -0
- package/style/renderers.d.ts.map +1 -0
- package/style/renderers.js +517 -0
- package/webmap/convert-renderer.d.ts +24 -3
- package/webmap/convert-renderer.d.ts.map +1 -1
- package/webmap/convert-renderer.js +93 -112
- package/webmap/index.d.ts +1 -1
- package/webmap/index.d.ts.map +1 -1
- package/webmap/index.js +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { HonuaClient } from "../core/client.js";
|
|
2
|
+
import { esriConfig } from "./esri-config.js";
|
|
3
|
+
import { FeatureLayerCompat } from "./feature-layer.js";
|
|
4
|
+
import { identityManager } from "./identity-manager.js";
|
|
5
|
+
import { createArcGisTokenInterceptor } from "./request.js";
|
|
6
|
+
import { parseFeatureLayerUrl, parseImageServiceUrl, parseMapServiceUrl } from "./url.js";
|
|
7
|
+
/** Error thrown when the Portal facade returns an Esri error envelope. */
|
|
8
|
+
export class PortalError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
details;
|
|
11
|
+
constructor(message, code, details) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "PortalError";
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.details = details;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Client for a Honua `/sharing/rest` Portal facade. Lets a repointed ArcGIS app
|
|
20
|
+
* authenticate, search for items, and open an item that resolves to a
|
|
21
|
+
* `/rest/services` URL — carrying the portal token through to the opened layer
|
|
22
|
+
* so the whole flow authenticates end to end.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { PortalCompat } from "@honua/sdk-js/esri-compat";
|
|
27
|
+
*
|
|
28
|
+
* const portal = new PortalCompat({ portalUrl: "https://honua.example" });
|
|
29
|
+
* await portal.generateToken({ username: "u", password: "p" });
|
|
30
|
+
* const { results } = await portal.search({ q: "roads" });
|
|
31
|
+
* const opened = await portal.openFeatureLayer(results[0]);
|
|
32
|
+
* if (opened.type === "feature-service") {
|
|
33
|
+
* await opened.layer.queryFeatures({ where: "1=1" });
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export class PortalCompat {
|
|
38
|
+
/** Normalized `.../sharing/rest` base for this portal. */
|
|
39
|
+
sharingRestBase;
|
|
40
|
+
token;
|
|
41
|
+
tokenExpiresAtMs;
|
|
42
|
+
apiKey;
|
|
43
|
+
fetchFn;
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
this.sharingRestBase = normalizeSharingRestBase(options.portalUrl ?? esriConfig.portalUrl);
|
|
46
|
+
this.token = options.token;
|
|
47
|
+
this.tokenExpiresAtMs = undefined;
|
|
48
|
+
this.apiKey = options.apiKey ?? esriConfig.apiKey;
|
|
49
|
+
// Bind so passing a bare `window.fetch`/`globalThis.fetch` cannot throw an
|
|
50
|
+
// "Illegal invocation" when called as `this.fetchFn(...)`.
|
|
51
|
+
this.fetchFn = (options.fetchFn ?? fetch).bind(globalThis);
|
|
52
|
+
}
|
|
53
|
+
/** The currently held portal token, if any. */
|
|
54
|
+
getToken() {
|
|
55
|
+
return this.token;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Exchange username/password for a portal token via
|
|
59
|
+
* `POST /sharing/rest/generateToken`. Stores the token on this instance and
|
|
60
|
+
* returns the credential (`expires` ms → `expiresAtMs`).
|
|
61
|
+
*/
|
|
62
|
+
async generateToken(options) {
|
|
63
|
+
const referer = options.referer ?? defaultReferer(this.sharingRestBase);
|
|
64
|
+
const body = new URLSearchParams();
|
|
65
|
+
body.set("username", options.username);
|
|
66
|
+
body.set("password", options.password);
|
|
67
|
+
body.set("client", options.client ?? "referer");
|
|
68
|
+
if (referer) {
|
|
69
|
+
body.set("referer", referer);
|
|
70
|
+
}
|
|
71
|
+
if (typeof options.expirationMinutes === "number" && Number.isFinite(options.expirationMinutes)) {
|
|
72
|
+
body.set("expiration", String(options.expirationMinutes));
|
|
73
|
+
}
|
|
74
|
+
body.set("f", "json");
|
|
75
|
+
const response = await this.fetchFn(`${this.sharingRestBase}/generateToken`, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
78
|
+
body: body.toString(),
|
|
79
|
+
});
|
|
80
|
+
const json = (await response.json());
|
|
81
|
+
throwIfEsriError(json, "Portal generateToken failed.");
|
|
82
|
+
if (typeof json.token !== "string" || json.token.length === 0) {
|
|
83
|
+
throw new PortalError("Portal generateToken response did not include a token.");
|
|
84
|
+
}
|
|
85
|
+
const expiresAtMs = typeof json.expires === "number" && Number.isFinite(json.expires) ? json.expires : 0;
|
|
86
|
+
this.token = json.token;
|
|
87
|
+
this.tokenExpiresAtMs = expiresAtMs || undefined;
|
|
88
|
+
return { token: json.token, expiresAtMs, ssl: json.ssl ?? true };
|
|
89
|
+
}
|
|
90
|
+
/** `GET /sharing/rest/info`. */
|
|
91
|
+
async getInfo() {
|
|
92
|
+
return this.getJson("/info", {}, undefined);
|
|
93
|
+
}
|
|
94
|
+
/** `GET /sharing/rest/portals/self` (attaches the token when present). */
|
|
95
|
+
async getPortalSelf() {
|
|
96
|
+
return this.getJson("/portals/self", {}, this.token);
|
|
97
|
+
}
|
|
98
|
+
/** `GET /sharing/rest/search`. */
|
|
99
|
+
async search(options = {}) {
|
|
100
|
+
const params = {
|
|
101
|
+
q: options.q,
|
|
102
|
+
start: options.start,
|
|
103
|
+
num: options.num,
|
|
104
|
+
};
|
|
105
|
+
const raw = await this.getJson("/search", params, options.token ?? this.token);
|
|
106
|
+
return {
|
|
107
|
+
query: raw.query ?? options.q ?? "",
|
|
108
|
+
total: raw.total ?? 0,
|
|
109
|
+
start: raw.start ?? options.start ?? 1,
|
|
110
|
+
num: raw.num ?? options.num ?? 0,
|
|
111
|
+
nextStart: raw.nextStart ?? -1,
|
|
112
|
+
results: Array.isArray(raw.results) ? raw.results : [],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** `GET /sharing/rest/content/items/{id}`. */
|
|
116
|
+
async getItem(itemId, options = {}) {
|
|
117
|
+
return this.getJson(`/content/items/${encodeURIComponent(itemId)}`, {}, options.token ?? this.token);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve an item (fetching it when given an id), parse its service `url`, and
|
|
121
|
+
* return an authenticated handle. Feature Service items yield a
|
|
122
|
+
* {@link FeatureLayerCompat} bound to a client carrying the portal token;
|
|
123
|
+
* Map/Image Service items yield the resolved `{ baseUrl, serviceId, client }`.
|
|
124
|
+
*/
|
|
125
|
+
async openFeatureLayer(itemOrId, options = {}) {
|
|
126
|
+
const item = typeof itemOrId === "string" ? await this.getItem(itemOrId) : itemOrId;
|
|
127
|
+
if (!item.url) {
|
|
128
|
+
throw new PortalError(`Portal item ${item.id} has no service URL to open.`);
|
|
129
|
+
}
|
|
130
|
+
const serviceUrl = trimTrailingSlashes(item.url);
|
|
131
|
+
if (isFeatureService(item.type, serviceUrl)) {
|
|
132
|
+
const hasLayerId = /\/FeatureServer\/\d+$/i.test(serviceUrl);
|
|
133
|
+
const service = parseFeatureLayerUrl(hasLayerId ? serviceUrl : `${serviceUrl}/0`);
|
|
134
|
+
const client = this.buildAuthenticatedClient(service.baseUrl);
|
|
135
|
+
let layerId = service.layerId;
|
|
136
|
+
if (!hasLayerId) {
|
|
137
|
+
if (options.layerId !== undefined) {
|
|
138
|
+
layerId = options.layerId;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const metadata = await client.getFeatureServiceMetadata(service.serviceId);
|
|
142
|
+
const firstLayerId = metadata.layers?.[0]?.id;
|
|
143
|
+
if (typeof firstLayerId !== "number" || !Number.isSafeInteger(firstLayerId)) {
|
|
144
|
+
throw new PortalError(`Portal Feature Service item ${item.id} does not advertise a layer to open.`);
|
|
145
|
+
}
|
|
146
|
+
layerId = firstLayerId;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const layerUrl = hasLayerId ? serviceUrl : `${serviceUrl}/${layerId}`;
|
|
150
|
+
const parsed = parseFeatureLayerUrl(layerUrl);
|
|
151
|
+
const layer = new FeatureLayerCompat({ url: layerUrl, client, title: item.title, id: item.id });
|
|
152
|
+
return {
|
|
153
|
+
type: "feature-service",
|
|
154
|
+
item,
|
|
155
|
+
baseUrl: parsed.baseUrl,
|
|
156
|
+
serviceId: parsed.serviceId,
|
|
157
|
+
layerId: parsed.layerId,
|
|
158
|
+
client,
|
|
159
|
+
layer,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (isImageService(item.type, serviceUrl)) {
|
|
163
|
+
const parsed = parseImageServiceUrl(serviceUrl);
|
|
164
|
+
const client = this.buildAuthenticatedClient(parsed.baseUrl);
|
|
165
|
+
return { type: "image-service", item, baseUrl: parsed.baseUrl, serviceId: parsed.serviceId, client };
|
|
166
|
+
}
|
|
167
|
+
const parsed = parseMapServiceUrl(serviceUrl);
|
|
168
|
+
const client = this.buildAuthenticatedClient(parsed.baseUrl);
|
|
169
|
+
return { type: "map-service", item, baseUrl: parsed.baseUrl, serviceId: parsed.serviceId, client };
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Register the currently held token with the shared {@link identityManager}
|
|
173
|
+
* under this portal's `/sharing/rest` server, so subsequent esri-compat calls
|
|
174
|
+
* resolve the same credential.
|
|
175
|
+
*/
|
|
176
|
+
registerWithIdentityManager() {
|
|
177
|
+
if (!this.token) {
|
|
178
|
+
throw new PortalError("PortalCompat has no token to register; call generateToken first.");
|
|
179
|
+
}
|
|
180
|
+
identityManager.registerToken({
|
|
181
|
+
server: this.sharingRestBase,
|
|
182
|
+
token: this.token,
|
|
183
|
+
...(this.tokenExpiresAtMs !== undefined ? { expires: this.tokenExpiresAtMs } : {}),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
buildAuthenticatedClient(baseUrl) {
|
|
187
|
+
const interceptors = [];
|
|
188
|
+
if (this.token) {
|
|
189
|
+
interceptors.push(createArcGisTokenInterceptor({ getToken: () => this.token }));
|
|
190
|
+
}
|
|
191
|
+
return new HonuaClient({
|
|
192
|
+
baseUrl,
|
|
193
|
+
fetchFn: this.fetchFn,
|
|
194
|
+
...(this.apiKey ? { apiKey: this.apiKey } : {}),
|
|
195
|
+
...(interceptors.length > 0 ? { interceptors } : {}),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
async getJson(path, params, token) {
|
|
199
|
+
const query = new URLSearchParams();
|
|
200
|
+
query.set("f", "json");
|
|
201
|
+
for (const [key, value] of Object.entries(params)) {
|
|
202
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
203
|
+
query.set(key, String(value));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (token) {
|
|
207
|
+
query.set("token", token);
|
|
208
|
+
}
|
|
209
|
+
const response = await this.fetchFn(`${this.sharingRestBase}${path}?${query.toString()}`);
|
|
210
|
+
const json = (await response.json());
|
|
211
|
+
throwIfEsriError(json, `Portal request to ${path} failed.`);
|
|
212
|
+
return json;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function normalizeSharingRestBase(portalUrl) {
|
|
216
|
+
const trimmed = trimTrailingSlashes(portalUrl);
|
|
217
|
+
if (/\/sharing\/rest$/i.test(trimmed)) {
|
|
218
|
+
return trimmed;
|
|
219
|
+
}
|
|
220
|
+
return `${trimmed}/sharing/rest`;
|
|
221
|
+
}
|
|
222
|
+
function trimTrailingSlashes(value) {
|
|
223
|
+
let end = value.length;
|
|
224
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) {
|
|
225
|
+
end--;
|
|
226
|
+
}
|
|
227
|
+
return value.slice(0, end);
|
|
228
|
+
}
|
|
229
|
+
function defaultReferer(sharingRestBase) {
|
|
230
|
+
if (globalThis.location?.origin) {
|
|
231
|
+
return globalThis.location.origin;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
return new URL(sharingRestBase).origin;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function isFeatureService(type, serviceUrl) {
|
|
241
|
+
return /feature\s*service/i.test(type) || /\/FeatureServer(\/\d+)?$/i.test(serviceUrl);
|
|
242
|
+
}
|
|
243
|
+
function isImageService(type, serviceUrl) {
|
|
244
|
+
return /image\s*service/i.test(type) || /\/ImageServer(\/\d+)?$/i.test(serviceUrl);
|
|
245
|
+
}
|
|
246
|
+
function throwIfEsriError(json, fallbackMessage) {
|
|
247
|
+
if (json && typeof json === "object" && json.error) {
|
|
248
|
+
const { code, message, details } = json.error;
|
|
249
|
+
throw new PortalError(message ?? fallbackMessage, code, details);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges the esri-compat renderer shims to the first-class renderer objects
|
|
3
|
+
* in `@honua/sdk-js/style` (issue #497 REQ-002). The compat classes stay
|
|
4
|
+
* pure state holders; these helpers project their state through the same
|
|
5
|
+
* WebMap symbol/renderer conversion path the WebMap converter uses, so the
|
|
6
|
+
* SDK has exactly one class-breaks/unique-value implementation.
|
|
7
|
+
*
|
|
8
|
+
* Kept out of the compat classes themselves so consumers who never emit
|
|
9
|
+
* renderer objects do not pay for the conversion pipeline.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { ClassBreaksRenderer, UniqueValueRenderer } from "../style/renderers.js";
|
|
14
|
+
import type { WarningCollector } from "../webmap/warnings.js";
|
|
15
|
+
import type { ClassBreaksRendererCompat } from "./class-breaks-renderer.js";
|
|
16
|
+
import type { UniqueValueRendererCompat } from "./unique-value-renderer.js";
|
|
17
|
+
/** Result of projecting a compat renderer to a renderer object. @experimental */
|
|
18
|
+
export interface CompatRendererProjection<R> {
|
|
19
|
+
/** The emitted renderer object, or `undefined` when nothing was convertible. */
|
|
20
|
+
readonly renderer: R | undefined;
|
|
21
|
+
/** Symbol/renderer conversion warnings collected during projection. */
|
|
22
|
+
readonly warnings: WarningCollector["warnings"];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Emit a first-class {@link ClassBreaksRenderer} from a
|
|
26
|
+
* `ClassBreaksRendererCompat` shim. Compat `minValue`/`maxValue` map to the
|
|
27
|
+
* WebMap `classMinValue`/`classMaxValue` fields and symbols convert through
|
|
28
|
+
* the shared WebMap symbol pipeline.
|
|
29
|
+
*
|
|
30
|
+
* @experimental
|
|
31
|
+
*/
|
|
32
|
+
export declare function rendererObjectFromClassBreaksCompat(compat: ClassBreaksRendererCompat): CompatRendererProjection<ClassBreaksRenderer>;
|
|
33
|
+
/**
|
|
34
|
+
* Emit a first-class {@link UniqueValueRenderer} from a
|
|
35
|
+
* `UniqueValueRendererCompat` shim.
|
|
36
|
+
*
|
|
37
|
+
* @experimental
|
|
38
|
+
*/
|
|
39
|
+
export declare function rendererObjectFromUniqueValueCompat(compat: UniqueValueRendererCompat): CompatRendererProjection<UniqueValueRenderer>;
|
|
40
|
+
//# sourceMappingURL=renderer-objects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer-objects.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/renderer-objects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAE5E,iFAAiF;AACjF,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAC;IACjC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC;CACjD;AAED;;;;;;;GAOG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,yBAAyB,GAChC,wBAAwB,CAAC,mBAAmB,CAAC,CAmB/C;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,yBAAyB,GAChC,wBAAwB,CAAC,mBAAmB,CAAC,CAmB/C"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges the esri-compat renderer shims to the first-class renderer objects
|
|
3
|
+
* in `@honua/sdk-js/style` (issue #497 REQ-002). The compat classes stay
|
|
4
|
+
* pure state holders; these helpers project their state through the same
|
|
5
|
+
* WebMap symbol/renderer conversion path the WebMap converter uses, so the
|
|
6
|
+
* SDK has exactly one class-breaks/unique-value implementation.
|
|
7
|
+
*
|
|
8
|
+
* Kept out of the compat classes themselves so consumers who never emit
|
|
9
|
+
* renderer objects do not pay for the conversion pipeline.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { classBreaksRendererFromWebMap, uniqueValueRendererFromWebMap } from "../webmap/convert-renderer.js";
|
|
14
|
+
import { createWarningCollector } from "../webmap/warnings.js";
|
|
15
|
+
/**
|
|
16
|
+
* Emit a first-class {@link ClassBreaksRenderer} from a
|
|
17
|
+
* `ClassBreaksRendererCompat` shim. Compat `minValue`/`maxValue` map to the
|
|
18
|
+
* WebMap `classMinValue`/`classMaxValue` fields and symbols convert through
|
|
19
|
+
* the shared WebMap symbol pipeline.
|
|
20
|
+
*
|
|
21
|
+
* @experimental
|
|
22
|
+
*/
|
|
23
|
+
export function rendererObjectFromClassBreaksCompat(compat) {
|
|
24
|
+
const warn = createWarningCollector();
|
|
25
|
+
const renderer = classBreaksRendererFromWebMap({
|
|
26
|
+
type: "classBreaks",
|
|
27
|
+
field: compat.field,
|
|
28
|
+
minValue: compat.minValue,
|
|
29
|
+
defaultSymbol: compat.defaultSymbol,
|
|
30
|
+
defaultLabel: compat.defaultLabel,
|
|
31
|
+
classBreakInfos: compat.classBreakInfos.map((info) => ({
|
|
32
|
+
classMinValue: info.minValue,
|
|
33
|
+
classMaxValue: info.maxValue,
|
|
34
|
+
label: info.label,
|
|
35
|
+
symbol: info.symbol,
|
|
36
|
+
})),
|
|
37
|
+
}, warn);
|
|
38
|
+
return { renderer, warnings: warn.warnings };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Emit a first-class {@link UniqueValueRenderer} from a
|
|
42
|
+
* `UniqueValueRendererCompat` shim.
|
|
43
|
+
*
|
|
44
|
+
* @experimental
|
|
45
|
+
*/
|
|
46
|
+
export function rendererObjectFromUniqueValueCompat(compat) {
|
|
47
|
+
const warn = createWarningCollector();
|
|
48
|
+
const renderer = uniqueValueRendererFromWebMap({
|
|
49
|
+
type: "uniqueValue",
|
|
50
|
+
field1: compat.field,
|
|
51
|
+
field2: compat.field2,
|
|
52
|
+
field3: compat.field3,
|
|
53
|
+
defaultSymbol: compat.defaultSymbol,
|
|
54
|
+
defaultLabel: compat.defaultLabel,
|
|
55
|
+
uniqueValueInfos: compat.uniqueValueInfos.map((info) => ({
|
|
56
|
+
value: info.value,
|
|
57
|
+
label: info.label,
|
|
58
|
+
symbol: info.symbol,
|
|
59
|
+
})),
|
|
60
|
+
}, warn);
|
|
61
|
+
return { renderer, warnings: warn.warnings };
|
|
62
|
+
}
|
package/esri-compat/sketch.d.ts
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
|
+
import type { EditSketchTool } from "../contract/edit-sketch.js";
|
|
2
|
+
import type { SnappingConfig } from "../contract/edit-snapping.js";
|
|
1
3
|
import { CompatEventBus } from "./event-bus.js";
|
|
4
|
+
import { type SnappingOptionsCompat } from "./snapping.js";
|
|
2
5
|
export type SketchCreationModeCompat = "single" | "update" | "continuous";
|
|
3
6
|
export type SketchToolCompat = "point" | "polyline" | "polygon" | "rectangle" | "circle";
|
|
7
|
+
/**
|
|
8
|
+
* Duck-typed subset of the runtime terra-draw sketch handle
|
|
9
|
+
* (`bindTerraDrawSketch` / `createTerraDrawSketch` from
|
|
10
|
+
* `@honua/sdk-js/runtime`) that the Sketch shim can delegate tool modes to.
|
|
11
|
+
* Structural on purpose: the compat layer never imports the optional
|
|
12
|
+
* terra-draw peer, it only feature-detects a binding the app wired up.
|
|
13
|
+
*
|
|
14
|
+
* @experimental
|
|
15
|
+
*/
|
|
16
|
+
export interface SketchToolBindingCompat {
|
|
17
|
+
setTool(tool: EditSketchTool): unknown;
|
|
18
|
+
select?(): void;
|
|
19
|
+
cancel?(): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Feature-detect a terra-draw sketch binding: any object exposing a callable
|
|
23
|
+
* `setTool` qualifies; anything else (including `undefined` when terra-draw
|
|
24
|
+
* is not installed) resolves to `undefined` and the shim keeps its current
|
|
25
|
+
* headless behavior.
|
|
26
|
+
*
|
|
27
|
+
* @experimental
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveSketchToolBindingCompat(value: unknown): SketchToolBindingCompat | undefined;
|
|
4
30
|
export interface SketchCreateOptionsCompat {
|
|
5
31
|
mode?: "click" | "freehand" | "hybrid";
|
|
6
32
|
}
|
|
@@ -20,6 +46,15 @@ export interface SketchCompatOptions {
|
|
|
20
46
|
creationMode?: SketchCreationModeCompat;
|
|
21
47
|
defaultCreateOptions?: Partial<SketchCreateOptionsCompat>;
|
|
22
48
|
defaultUpdateOptions?: Partial<SketchUpdateOptionsCompat>;
|
|
49
|
+
snappingOptions?: SnappingOptionsCompat;
|
|
50
|
+
/**
|
|
51
|
+
* Optional terra-draw sketch binding (see {@link SketchToolBindingCompat}).
|
|
52
|
+
* When present, `create`/`update`/`cancel`/`reset` delegate tool-mode
|
|
53
|
+
* changes to the binding; otherwise the shim keeps its headless behavior.
|
|
54
|
+
*
|
|
55
|
+
* @experimental
|
|
56
|
+
*/
|
|
57
|
+
sketchBinding?: unknown;
|
|
23
58
|
}
|
|
24
59
|
export interface SketchCreateResultCompat {
|
|
25
60
|
state: "complete" | "cancel";
|
|
@@ -46,8 +81,16 @@ export declare class SketchCompat {
|
|
|
46
81
|
activeCreateOptions: Partial<SketchCreateOptionsCompat> | undefined;
|
|
47
82
|
activeUpdateGraphics: Record<string, unknown>[];
|
|
48
83
|
activeUpdateOptions: Partial<SketchUpdateOptionsCompat> | undefined;
|
|
84
|
+
snappingOptions: SnappingOptionsCompat;
|
|
49
85
|
private readonly watchListeners;
|
|
86
|
+
private readonly sketchBinding;
|
|
50
87
|
constructor(options?: SketchCompatOptions);
|
|
88
|
+
/** Whether tool modes delegate to a terra-draw sketch binding. @experimental */
|
|
89
|
+
delegatesToSketchBinding(): boolean;
|
|
90
|
+
/** Replace the ArcGIS-shaped snapping options and notify watchers. */
|
|
91
|
+
setSnappingOptions(options: SnappingOptionsCompat): void;
|
|
92
|
+
/** The snapping options mapped onto the contract `SnappingConfig`. */
|
|
93
|
+
snappingConfig(): SnappingConfig;
|
|
51
94
|
load(): Promise<SketchCompat>;
|
|
52
95
|
when(callback?: (widget: SketchCompat) => void): Promise<SketchCompat>;
|
|
53
96
|
watch(propertyName: string, listener: (value: unknown) => void): SketchHandleCompat;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sketch.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/sketch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmD,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"sketch.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/sketch.ts"],"names":[],"mappings":"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"}
|
package/esri-compat/sketch.js
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import { CompatEventBus, resolveCompatEventBus, safeInvokeCompatListener } from "./event-bus.js";
|
|
2
|
+
import { snappingOptionsToSnappingConfig } from "./snapping.js";
|
|
3
|
+
const SKETCH_COMPAT_TOOL_TO_EDIT_TOOL = Object.freeze({
|
|
4
|
+
point: "point",
|
|
5
|
+
polyline: "line",
|
|
6
|
+
polygon: "polygon",
|
|
7
|
+
rectangle: "rectangle",
|
|
8
|
+
circle: "circle",
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Feature-detect a terra-draw sketch binding: any object exposing a callable
|
|
12
|
+
* `setTool` qualifies; anything else (including `undefined` when terra-draw
|
|
13
|
+
* is not installed) resolves to `undefined` and the shim keeps its current
|
|
14
|
+
* headless behavior.
|
|
15
|
+
*
|
|
16
|
+
* @experimental
|
|
17
|
+
*/
|
|
18
|
+
export function resolveSketchToolBindingCompat(value) {
|
|
19
|
+
if (value !== null && typeof value === "object" && typeof value.setTool === "function") {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
2
24
|
export class SketchCompat {
|
|
3
25
|
view;
|
|
4
26
|
layer;
|
|
@@ -15,7 +37,9 @@ export class SketchCompat {
|
|
|
15
37
|
activeCreateOptions;
|
|
16
38
|
activeUpdateGraphics;
|
|
17
39
|
activeUpdateOptions;
|
|
40
|
+
snappingOptions;
|
|
18
41
|
watchListeners;
|
|
42
|
+
sketchBinding;
|
|
19
43
|
constructor(options = {}) {
|
|
20
44
|
this.view = options.view;
|
|
21
45
|
this.layer = options.layer;
|
|
@@ -32,7 +56,23 @@ export class SketchCompat {
|
|
|
32
56
|
this.activeCreateOptions = undefined;
|
|
33
57
|
this.activeUpdateGraphics = [];
|
|
34
58
|
this.activeUpdateOptions = undefined;
|
|
59
|
+
this.snappingOptions = { ...(options.snappingOptions ?? {}) };
|
|
35
60
|
this.watchListeners = new Map();
|
|
61
|
+
this.sketchBinding = resolveSketchToolBindingCompat(options.sketchBinding);
|
|
62
|
+
}
|
|
63
|
+
/** Whether tool modes delegate to a terra-draw sketch binding. @experimental */
|
|
64
|
+
delegatesToSketchBinding() {
|
|
65
|
+
return this.sketchBinding !== undefined;
|
|
66
|
+
}
|
|
67
|
+
/** Replace the ArcGIS-shaped snapping options and notify watchers. */
|
|
68
|
+
setSnappingOptions(options) {
|
|
69
|
+
this.snappingOptions = { ...options };
|
|
70
|
+
this.notifyWatchers("snappingOptions", this.snappingOptions);
|
|
71
|
+
this.eventBus.emit("sketch.snapping-options-changed", { options: this.snappingOptions }, this);
|
|
72
|
+
}
|
|
73
|
+
/** The snapping options mapped onto the contract `SnappingConfig`. */
|
|
74
|
+
snappingConfig() {
|
|
75
|
+
return snappingOptionsToSnappingConfig(this.snappingOptions);
|
|
36
76
|
}
|
|
37
77
|
async load() {
|
|
38
78
|
if (this.loaded) {
|
|
@@ -83,6 +123,7 @@ export class SketchCompat {
|
|
|
83
123
|
creationMode: this.creationMode,
|
|
84
124
|
options: this.activeCreateOptions,
|
|
85
125
|
}, this);
|
|
126
|
+
this.sketchBinding?.setTool(SKETCH_COMPAT_TOOL_TO_EDIT_TOOL[tool]);
|
|
86
127
|
}
|
|
87
128
|
complete(graphic) {
|
|
88
129
|
if (this.state !== "active" || !this.activeTool) {
|
|
@@ -97,6 +138,8 @@ export class SketchCompat {
|
|
|
97
138
|
graphic: createdGraphic,
|
|
98
139
|
layerGraphicCount: getLayerGraphics(this.layer).length,
|
|
99
140
|
}, this);
|
|
141
|
+
if (this.creationMode !== "continuous")
|
|
142
|
+
this.sketchBinding?.cancel?.();
|
|
100
143
|
return {
|
|
101
144
|
state: "complete",
|
|
102
145
|
tool,
|
|
@@ -110,6 +153,7 @@ export class SketchCompat {
|
|
|
110
153
|
const tool = this.activeTool;
|
|
111
154
|
this.clearActiveState();
|
|
112
155
|
this.eventBus.emit("sketch.create-cancelled", { tool }, this);
|
|
156
|
+
this.sketchBinding?.cancel?.();
|
|
113
157
|
return {
|
|
114
158
|
state: "cancel",
|
|
115
159
|
tool,
|
|
@@ -128,6 +172,7 @@ export class SketchCompat {
|
|
|
128
172
|
count: normalizedGraphics.length,
|
|
129
173
|
options: this.activeUpdateOptions,
|
|
130
174
|
}, this);
|
|
175
|
+
this.sketchBinding?.select?.();
|
|
131
176
|
return this.activeUpdateGraphics;
|
|
132
177
|
}
|
|
133
178
|
delete(graphics) {
|
|
@@ -157,6 +202,7 @@ export class SketchCompat {
|
|
|
157
202
|
this.activeUpdateOptions = undefined;
|
|
158
203
|
this.notifyWatchers("activeUpdateOptions", this.activeUpdateOptions);
|
|
159
204
|
this.eventBus.emit("sketch.reset", undefined, this);
|
|
205
|
+
this.sketchBinding?.cancel?.();
|
|
160
206
|
}
|
|
161
207
|
clearActiveState() {
|
|
162
208
|
this.state = "ready";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArcGIS `SnappingOptions`-shaped compat surface, mapped onto the
|
|
3
|
+
* renderer-neutral `SnappingConfig` from `contract/edit-snapping.ts`.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { type SnappingConfig } from "../contract/edit-snapping.js";
|
|
8
|
+
/** ArcGIS `FeatureSnappingLayerSource`-shaped entry. */
|
|
9
|
+
export interface SnappingFeatureSourceCompat {
|
|
10
|
+
/** Layer (or layer-like object) providing snap features. */
|
|
11
|
+
layer?: unknown;
|
|
12
|
+
/** Explicit Honua source id when no layer id is derivable. */
|
|
13
|
+
id?: string;
|
|
14
|
+
/** @default true */
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** ArcGIS `SnappingOptions`-shaped configuration accepted by the shims. */
|
|
18
|
+
export interface SnappingOptionsCompat {
|
|
19
|
+
/** Master switch. ArcGIS defaults snapping off. @default false */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
/** Snap distance in pixels; maps to `SnappingConfig.tolerance`. */
|
|
22
|
+
distance?: number;
|
|
23
|
+
/** Snap to features from `featureSources`. @default true */
|
|
24
|
+
featureEnabled?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Snap to the geometry being drawn. Stored for parity; self snapping is a
|
|
27
|
+
* runtime concern (index the active sketch source to approximate it).
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
selfEnabled?: boolean;
|
|
31
|
+
featureSources?: readonly SnappingFeatureSourceCompat[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Map ArcGIS-shaped snapping options to the contract `SnappingConfig`.
|
|
35
|
+
*
|
|
36
|
+
* `distance` maps to pixel tolerance; `featureSources` map to per-source
|
|
37
|
+
* enablement keyed by `source.id` (falling back to a duck-typed `layer.id` /
|
|
38
|
+
* `layer.sourceId`); `featureEnabled: false` disables every listed source.
|
|
39
|
+
* Vertex and edge candidates are enabled — matching ArcGIS 2D snapping,
|
|
40
|
+
* which always resolves both.
|
|
41
|
+
*/
|
|
42
|
+
export declare function snappingOptionsToSnappingConfig(options?: SnappingOptionsCompat): SnappingConfig;
|
|
43
|
+
//# sourceMappingURL=snapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapping.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/snapping.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,cAAc,EAEpB,MAAM,8BAA8B,CAAC;AAEtC,wDAAwD;AACxD,MAAM,WAAW,2BAA2B;IAC1C,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,SAAS,2BAA2B,EAAE,CAAC;CACzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAcnG"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArcGIS `SnappingOptions`-shaped compat surface, mapped onto the
|
|
3
|
+
* renderer-neutral `SnappingConfig` from `contract/edit-snapping.ts`.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { DEFAULT_SNAPPING_CONFIG, resolveSnappingConfig, } from "../contract/edit-snapping.js";
|
|
8
|
+
/**
|
|
9
|
+
* Map ArcGIS-shaped snapping options to the contract `SnappingConfig`.
|
|
10
|
+
*
|
|
11
|
+
* `distance` maps to pixel tolerance; `featureSources` map to per-source
|
|
12
|
+
* enablement keyed by `source.id` (falling back to a duck-typed `layer.id` /
|
|
13
|
+
* `layer.sourceId`); `featureEnabled: false` disables every listed source.
|
|
14
|
+
* Vertex and edge candidates are enabled — matching ArcGIS 2D snapping,
|
|
15
|
+
* which always resolves both.
|
|
16
|
+
*/
|
|
17
|
+
export function snappingOptionsToSnappingConfig(options = {}) {
|
|
18
|
+
const kinds = ["vertex", "edge"];
|
|
19
|
+
const sources = {};
|
|
20
|
+
for (const featureSource of options.featureSources ?? []) {
|
|
21
|
+
const sourceId = featureSourceId(featureSource);
|
|
22
|
+
if (sourceId === undefined)
|
|
23
|
+
continue;
|
|
24
|
+
sources[sourceId] = options.featureEnabled === false ? false : (featureSource.enabled ?? true);
|
|
25
|
+
}
|
|
26
|
+
return resolveSnappingConfig({
|
|
27
|
+
enabled: options.enabled ?? false,
|
|
28
|
+
tolerance: options.distance ?? DEFAULT_SNAPPING_CONFIG.tolerance,
|
|
29
|
+
kinds,
|
|
30
|
+
sources,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function featureSourceId(featureSource) {
|
|
34
|
+
if (featureSource.id !== undefined)
|
|
35
|
+
return featureSource.id;
|
|
36
|
+
const layer = featureSource.layer;
|
|
37
|
+
if (layer && typeof layer === "object") {
|
|
38
|
+
const candidate = layer;
|
|
39
|
+
if (typeof candidate.id === "string")
|
|
40
|
+
return candidate.id;
|
|
41
|
+
if (typeof candidate.sourceId === "string")
|
|
42
|
+
return candidate.sourceId;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
package/esri-compat/url.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/url.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/url.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAUD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAkBvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAanE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAarE"}
|
package/esri-compat/url.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const ABSOLUTE_URL_RE = /^[A-Za-z][A-Za-z\d+.-]*:\/\//;
|
|
2
2
|
const PROTOCOL_RELATIVE_URL_RE = /^\/\//;
|
|
3
3
|
const RELATIVE_URL_BASE = "https://honua.invalid";
|
|
4
|
-
const FEATURE_LAYER_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+)\/FeatureServer\/(?<layerId>\d+)\/?$/;
|
|
4
|
+
const FEATURE_LAYER_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+(?:\/[^/]+)*)\/FeatureServer\/(?<layerId>\d+)\/?$/;
|
|
5
5
|
const MAP_SERVICE_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+)\/MapServer(?:\/\d+)?\/?$/;
|
|
6
6
|
const IMAGE_SERVICE_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+)\/ImageServer(?:\/\d+)?\/?$/;
|
|
7
7
|
export function parseFeatureLayerUrl(url) {
|