@honua/app-platform 0.1.9-beta.0 → 0.1.11-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/control-plane/admin-client.d.ts +2 -0
- package/control-plane/admin-client.d.ts.map +1 -1
- package/control-plane/admin-client.js +32 -1
- package/control-plane/client.d.ts +1 -1
- package/control-plane/client.d.ts.map +1 -1
- package/control-plane/client.js +124 -8
- package/control-plane/commands/catalog.d.ts +7 -7
- package/control-plane/commands/catalog.d.ts.map +1 -1
- package/control-plane/commands/catalog.js +27 -15
- package/control-plane/commands/runtime.d.ts +6 -6
- package/control-plane/commands/runtime.d.ts.map +1 -1
- package/control-plane/commands/runtime.js +26 -14
- package/control-plane/commands/types.d.ts +4 -2
- package/control-plane/commands/types.d.ts.map +1 -1
- package/control-plane/generated/admin-api.d.ts +6 -4
- package/control-plane/generated/admin-api.d.ts.map +1 -1
- package/control-plane/generated/admin-api.js +2 -2
- package/control-plane/generated/admin-operations.d.ts +7 -7
- package/control-plane/generated/admin-operations.d.ts.map +1 -1
- package/control-plane/generated/admin-operations.js +9 -9
- package/control-plane/index.d.ts +1 -1
- package/control-plane/index.d.ts.map +1 -1
- package/control-plane/index.js +1 -1
- package/controls/qualification.d.ts.map +1 -1
- package/controls/qualification.js +10 -6
- package/core/client.d.ts.map +1 -1
- package/core/client.js +28 -30
- package/core/error-envelope.d.ts +31 -0
- package/core/error-envelope.d.ts.map +1 -1
- package/core/error-envelope.js +11 -0
- package/core/errors.d.ts +15 -3
- package/core/errors.d.ts.map +1 -1
- package/core/errors.js +254 -2
- package/core/geometry-input-sr.d.ts +3 -0
- package/core/geometry-input-sr.d.ts.map +1 -0
- package/core/geometry-input-sr.js +28 -0
- package/core/geoservices.d.ts.map +1 -1
- package/core/geoservices.js +33 -6
- package/core/grpc-adapter.d.ts.map +1 -1
- package/core/grpc-adapter.js +11 -3
- package/core/pbf-decoder.d.ts.map +1 -1
- package/core/pbf-decoder.js +16 -4
- package/core/protocol-transport.d.ts +6 -1
- package/core/protocol-transport.d.ts.map +1 -1
- package/core/request-pipeline.d.ts +6 -3
- package/core/request-pipeline.d.ts.map +1 -1
- package/core/request-pipeline.js +28 -11
- package/core/spatial-filter.d.ts +4 -4
- package/core/spatial-filter.d.ts.map +1 -1
- package/core/spatial-filter.js +7 -7
- package/core/types.d.ts +14 -3
- package/core/types.d.ts.map +1 -1
- package/custom-elements.json +341 -0
- package/esri-compat/class-breaks-renderer.d.ts +2 -0
- package/esri-compat/class-breaks-renderer.d.ts.map +1 -1
- package/esri-compat/class-breaks-renderer.js +7 -0
- package/esri-compat/controls.d.ts +10 -1
- package/esri-compat/controls.d.ts.map +1 -1
- package/esri-compat/controls.js +86 -2
- package/esri-compat/directions.d.ts +35 -0
- package/esri-compat/directions.d.ts.map +1 -1
- package/esri-compat/directions.js +153 -2
- package/esri-compat/expand.d.ts +1 -0
- package/esri-compat/expand.d.ts.map +1 -1
- package/esri-compat/expand.js +61 -0
- package/esri-compat/feature-layer.d.ts +29 -4
- package/esri-compat/feature-layer.d.ts.map +1 -1
- package/esri-compat/feature-layer.js +335 -13
- package/esri-compat/geometry-engine.d.ts +4 -0
- package/esri-compat/geometry-engine.d.ts.map +1 -1
- package/esri-compat/geometry-engine.js +8 -1
- package/esri-compat/graphic.d.ts +7 -4
- package/esri-compat/graphic.d.ts.map +1 -1
- package/esri-compat/legend.d.ts +2 -1
- package/esri-compat/legend.d.ts.map +1 -1
- package/esri-compat/legend.js +15 -0
- package/esri-compat/map-view-mount.d.ts +48 -0
- package/esri-compat/map-view-mount.d.ts.map +1 -0
- package/esri-compat/map-view-mount.js +344 -0
- package/esri-compat/map-view.d.ts +38 -6
- package/esri-compat/map-view.d.ts.map +1 -1
- package/esri-compat/map-view.js +471 -33
- package/esri-compat/portal.d.ts +2 -0
- package/esri-compat/portal.d.ts.map +1 -1
- package/esri-compat/portal.js +7 -2
- package/esri-compat/renderer-objects.d.ts +7 -2
- package/esri-compat/renderer-objects.d.ts.map +1 -1
- package/esri-compat/renderer-objects.js +16 -5
- package/esri-compat/route-layer.d.ts +8 -0
- package/esri-compat/route-layer.d.ts.map +1 -1
- package/esri-compat/route-layer.js +24 -4
- package/esri-compat/route-task.d.ts +5 -1
- package/esri-compat/route-task.d.ts.map +1 -1
- package/esri-compat/route-task.js +119 -7
- package/esri-compat/scene-view.d.ts.map +1 -1
- package/esri-compat/scene-view.js +1 -0
- package/esri-compat/search.d.ts +2 -1
- package/esri-compat/search.d.ts.map +1 -1
- package/esri-compat/search.js +52 -2
- package/esri-compat/unique-value-renderer.d.ts +2 -0
- package/esri-compat/unique-value-renderer.d.ts.map +1 -1
- package/esri-compat/unique-value-renderer.js +7 -0
- package/esri-compat/web-map.d.ts +10 -0
- package/esri-compat/web-map.d.ts.map +1 -1
- package/esri-compat/web-map.js +102 -0
- package/geocoding/index.d.ts +1 -0
- package/geocoding/index.d.ts.map +1 -1
- package/geocoding/index.js +18 -8
- package/geocoding/provider-http.d.ts.map +1 -1
- package/geocoding/provider-http.js +3 -1
- package/map/webmap-maplibre.d.ts +2 -1
- package/map/webmap-maplibre.d.ts.map +1 -1
- package/map/webmap-maplibre.js +3 -0
- package/package.json +6 -4
- package/query-planner/geoservices.d.ts.map +1 -1
- package/query-planner/geoservices.js +2 -0
- package/query-planner/types.d.ts +2 -0
- package/query-planner/types.d.ts.map +1 -1
- package/realtime/honua-server.d.ts +4 -0
- package/realtime/honua-server.d.ts.map +1 -1
- package/realtime/honua-server.js +10 -0
- package/realtime/resumable.js +9 -0
- package/realtime/types.d.ts +8 -0
- package/realtime/types.d.ts.map +1 -1
- package/scene-workspace/index.d.ts +2 -2
- package/scene-workspace/index.d.ts.map +1 -1
- package/scene-workspace/index.js +1 -1
- package/scene-workspace/maplibre-state-sync.d.ts.map +1 -1
- package/scene-workspace/maplibre-state-sync.js +35 -3
- package/scene-workspace/primitives.d.ts +37 -5
- package/scene-workspace/primitives.d.ts.map +1 -1
- package/scene-workspace/primitives.js +46 -12
- package/scene-workspace/state-sync-port-kit.d.ts +1 -1
- package/scene-workspace/state-sync-port-kit.d.ts.map +1 -1
- package/studio/capability-manifest.d.ts +10 -0
- package/studio/capability-manifest.d.ts.map +1 -1
- package/studio/capability-manifest.js +38 -0
- package/studio/command-adapter.d.ts +35 -0
- package/studio/command-adapter.d.ts.map +1 -0
- package/studio/command-adapter.js +36 -0
- package/studio/generated/studio-api.d.ts +1062 -0
- package/studio/generated/studio-api.d.ts.map +1 -0
- package/studio/generated/studio-api.js +4 -0
- package/studio/index.d.ts +8 -6
- package/studio/index.d.ts.map +1 -1
- package/studio/index.js +6 -5
- package/studio/lifecycle-client.d.ts +3 -3
- package/studio/lifecycle-client.d.ts.map +1 -1
- package/studio/lifecycle-client.js +41 -22
- package/studio/lifecycle-types.d.ts +56 -77
- package/studio/lifecycle-types.d.ts.map +1 -1
- package/studio/lifecycle-types.js +5 -12
- package/studio/types.d.ts +37 -7
- package/studio/types.d.ts.map +1 -1
- package/studio/types.js +5 -3
- package/studio/validate.d.ts.map +1 -1
- package/studio/validate.js +104 -7
- package/style/renderers.d.ts +6 -1
- package/style/renderers.d.ts.map +1 -1
- package/style/renderers.js +11 -6
- package/style/visual-variables.d.ts +43 -0
- package/style/visual-variables.d.ts.map +1 -0
- package/style/visual-variables.js +183 -0
- package/web-components/index.d.ts +2 -1
- package/web-components/index.d.ts.map +1 -1
- package/web-components/index.js +1 -0
- package/web-components/map-pointer-claim.d.ts +24 -0
- package/web-components/map-pointer-claim.d.ts.map +1 -0
- package/web-components/map-pointer-claim.js +40 -0
- package/web-components/maplibre-renderer.d.ts.map +1 -1
- package/web-components/maplibre-renderer.js +5 -1
- package/web-components/measurement-units.d.ts +88 -0
- package/web-components/measurement-units.d.ts.map +1 -0
- package/web-components/measurement-units.js +278 -0
- package/web-components/measurement.d.ts +75 -4
- package/web-components/measurement.d.ts.map +1 -1
- package/web-components/measurement.js +227 -28
- package/web-components/types.d.ts +58 -3
- package/web-components/types.d.ts.map +1 -1
- package/webmap/convert-layer.d.ts +2 -1
- package/webmap/convert-layer.d.ts.map +1 -1
- package/webmap/convert-layer.js +20 -3
- package/webmap/convert-renderer.d.ts +10 -3
- package/webmap/convert-renderer.d.ts.map +1 -1
- package/webmap/convert-renderer.js +190 -36
- package/webmap/index.d.ts +1 -0
- package/webmap/index.d.ts.map +1 -1
- package/webmap/parse.d.ts +2 -1
- package/webmap/parse.d.ts.map +1 -1
- package/webmap/parse.js +1 -1
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit conversion, display formatting, and planar (Euclidean, flat-earth)
|
|
3
|
+
* math for `<honua-measurement>` (issue #1419).
|
|
4
|
+
*
|
|
5
|
+
* All conversion and formatting here operates on the canonical SI value
|
|
6
|
+
* already stored on `HonuaMeasureResult` (meters / square meters) — never on
|
|
7
|
+
* a previously rounded display string — so switching `unit`/`areaUnit`/
|
|
8
|
+
* `precision` at runtime always reformats the same full-precision number.
|
|
9
|
+
*
|
|
10
|
+
* `planarLength` / `planarArea` intentionally do not depend on `proj4`
|
|
11
|
+
* (`@honua/geometry`'s `project`): pulling that in would add proj4 as a
|
|
12
|
+
* static import of every `<honua-measurement>` consumer's bundle (the
|
|
13
|
+
* `/web-components` and `/controls` entrypoints), well past their gzip
|
|
14
|
+
* budgets, for a fidelity mode most apps never enable. Instead this uses the
|
|
15
|
+
* same equirectangular (meters-per-degree-of-latitude/longitude) flat-earth
|
|
16
|
+
* approximation `MeasurementCompat` already ships in `src/esri-compat/
|
|
17
|
+
* measurement.ts` — accurate for the local-extent sketches this widget draws,
|
|
18
|
+
* and proven parity math rather than a new approach.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
const METERS_PER_DISTANCE_UNIT = {
|
|
23
|
+
meters: 1,
|
|
24
|
+
kilometers: 1000,
|
|
25
|
+
feet: 0.3048,
|
|
26
|
+
yards: 0.9144,
|
|
27
|
+
miles: 1609.344,
|
|
28
|
+
nauticalmiles: 1852,
|
|
29
|
+
};
|
|
30
|
+
const SQUARE_METERS_PER_AREA_UNIT = {
|
|
31
|
+
"square-meters": 1,
|
|
32
|
+
hectares: 10_000,
|
|
33
|
+
"square-kilometers": 1_000_000,
|
|
34
|
+
"square-feet": 0.09290304,
|
|
35
|
+
acres: 4046.8564224,
|
|
36
|
+
"square-miles": 2_589_988.110336,
|
|
37
|
+
};
|
|
38
|
+
const DISTANCE_UNIT_SUFFIX = {
|
|
39
|
+
meters: "m",
|
|
40
|
+
kilometers: "km",
|
|
41
|
+
feet: "ft",
|
|
42
|
+
yards: "yd",
|
|
43
|
+
miles: "mi",
|
|
44
|
+
nauticalmiles: "nmi",
|
|
45
|
+
};
|
|
46
|
+
const AREA_UNIT_SUFFIX = {
|
|
47
|
+
"square-meters": "m²",
|
|
48
|
+
hectares: "ha",
|
|
49
|
+
"square-kilometers": "km²",
|
|
50
|
+
"square-feet": "ft²",
|
|
51
|
+
acres: "ac",
|
|
52
|
+
"square-miles": "mi²",
|
|
53
|
+
};
|
|
54
|
+
/** Converts a distance already in meters into `unit`. */
|
|
55
|
+
export function convertDistance(meters, unit) {
|
|
56
|
+
return meters / METERS_PER_DISTANCE_UNIT[unit];
|
|
57
|
+
}
|
|
58
|
+
/** Converts an area already in square meters into `unit`. */
|
|
59
|
+
export function convertArea(squareMeters, unit) {
|
|
60
|
+
return squareMeters / SQUARE_METERS_PER_AREA_UNIT[unit];
|
|
61
|
+
}
|
|
62
|
+
/** Resolves `"auto"` to a concrete unit given the magnitude, matching the pre-#1419 auto-scaling. */
|
|
63
|
+
function resolveDistanceUnit(meters, unit) {
|
|
64
|
+
if (unit !== "auto")
|
|
65
|
+
return unit;
|
|
66
|
+
return meters >= 1000 ? "kilometers" : "meters";
|
|
67
|
+
}
|
|
68
|
+
/** Resolves `"auto"` to a concrete unit given the magnitude, matching the pre-#1419 auto-scaling. */
|
|
69
|
+
function resolveAreaUnit(squareMeters, unit) {
|
|
70
|
+
if (unit !== "auto")
|
|
71
|
+
return unit;
|
|
72
|
+
if (squareMeters >= 1_000_000)
|
|
73
|
+
return "square-kilometers";
|
|
74
|
+
if (squareMeters >= 10_000)
|
|
75
|
+
return "hectares";
|
|
76
|
+
return "square-meters";
|
|
77
|
+
}
|
|
78
|
+
/** Default decimal digits per resolved unit, matching the pre-#1419 formatting. */
|
|
79
|
+
function defaultDistancePrecision(unit) {
|
|
80
|
+
return unit === "meters" ? 1 : 2;
|
|
81
|
+
}
|
|
82
|
+
/** Default decimal digits per resolved unit, matching the pre-#1419 formatting. */
|
|
83
|
+
function defaultAreaPrecision(unit) {
|
|
84
|
+
return unit === "square-meters" ? 1 : 2;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Formats a canonical meters value for display. `precision` overrides the
|
|
88
|
+
* unit's default decimal digits; `unit` overrides magnitude-based auto-scaling.
|
|
89
|
+
*/
|
|
90
|
+
export function formatDistanceValue(meters, unit, precision) {
|
|
91
|
+
const resolved = resolveDistanceUnit(meters, unit);
|
|
92
|
+
const value = convertDistance(meters, resolved);
|
|
93
|
+
const digits = precision ?? defaultDistancePrecision(resolved);
|
|
94
|
+
return `${value.toFixed(digits)} ${DISTANCE_UNIT_SUFFIX[resolved]}`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Formats a canonical square-meters value for display. `precision` overrides
|
|
98
|
+
* the unit's default decimal digits; `unit` overrides magnitude-based
|
|
99
|
+
* auto-scaling.
|
|
100
|
+
*/
|
|
101
|
+
export function formatAreaValue(squareMeters, unit, precision) {
|
|
102
|
+
const resolved = resolveAreaUnit(squareMeters, unit);
|
|
103
|
+
const value = convertArea(squareMeters, resolved);
|
|
104
|
+
const digits = precision ?? defaultAreaPrecision(resolved);
|
|
105
|
+
return `${value.toFixed(digits)} ${AREA_UNIT_SUFFIX[resolved]}`;
|
|
106
|
+
}
|
|
107
|
+
/** Distance units `<honua-measurement>` accepts, including `"auto"`. */
|
|
108
|
+
export const HONUA_MEASURE_DISTANCE_UNITS = [
|
|
109
|
+
"auto",
|
|
110
|
+
...Object.keys(METERS_PER_DISTANCE_UNIT),
|
|
111
|
+
];
|
|
112
|
+
/** Area units `<honua-measurement>` accepts, including `"auto"`. */
|
|
113
|
+
export const HONUA_MEASURE_AREA_UNITS = [
|
|
114
|
+
"auto",
|
|
115
|
+
...Object.keys(SQUARE_METERS_PER_AREA_UNIT),
|
|
116
|
+
];
|
|
117
|
+
/** Planar frames `<honua-measurement>` accepts for `fidelity: "planar"`. */
|
|
118
|
+
export const HONUA_MEASURE_PLANAR_CRS = ["local", "EPSG:3857"];
|
|
119
|
+
/** Meters per degree of latitude (WGS84, constant to within centimeters at any latitude). */
|
|
120
|
+
const METERS_PER_DEGREE_LATITUDE = 111_132.92;
|
|
121
|
+
/** Semi-major axis EPSG:3857 (spherical Web Mercator) projects onto. */
|
|
122
|
+
const WEB_MERCATOR_RADIUS = 6_378_137;
|
|
123
|
+
/** Latitude beyond which EPSG:3857 is undefined (the square-world clamp). */
|
|
124
|
+
const WEB_MERCATOR_MAX_LATITUDE = 85.051_128_779_806_59;
|
|
125
|
+
/** Meters per degree of longitude at WGS84 latitude `latDeg`. */
|
|
126
|
+
function metersPerDegreeLongitude(latDeg) {
|
|
127
|
+
return 111_412.84 * Math.cos(toRadians(latDeg));
|
|
128
|
+
}
|
|
129
|
+
function toRadians(degrees) {
|
|
130
|
+
return (degrees * Math.PI) / 180;
|
|
131
|
+
}
|
|
132
|
+
function meanLatitude(positions) {
|
|
133
|
+
return positions.reduce((sum, [, lat]) => sum + lat, 0) / positions.length;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Whether a position is a usable WGS84 vertex: finite, with latitude inside
|
|
137
|
+
* `[-90, 90]`. Longitude may exceed ±180 — MapLibre reports clicks on a world
|
|
138
|
+
* copy that way — and is unwrapped before any planar math.
|
|
139
|
+
*/
|
|
140
|
+
export function isValidMeasureVertex(position) {
|
|
141
|
+
const [lng, lat] = position;
|
|
142
|
+
return (typeof lng === "number" &&
|
|
143
|
+
typeof lat === "number" &&
|
|
144
|
+
Number.isFinite(lng) &&
|
|
145
|
+
Number.isFinite(lat) &&
|
|
146
|
+
lat >= -90 &&
|
|
147
|
+
lat <= 90);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Shifts each longitude by whole turns so consecutive vertices are never more
|
|
151
|
+
* than 180° apart. A sketch crossing the antimeridian (179° → -179°) is then
|
|
152
|
+
* measured the short way, matching what the geodesic ops already do, instead
|
|
153
|
+
* of across 358° of flat map.
|
|
154
|
+
*/
|
|
155
|
+
export function unwrapLongitudes(positions) {
|
|
156
|
+
const unwrapped = [];
|
|
157
|
+
for (const [lng, lat] of positions) {
|
|
158
|
+
const previous = unwrapped[unwrapped.length - 1];
|
|
159
|
+
if (!previous) {
|
|
160
|
+
unwrapped.push([lng, lat]);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const turns = Math.round((lng - previous[0]) / 360);
|
|
164
|
+
unwrapped.push([lng - turns * 360, lat]);
|
|
165
|
+
}
|
|
166
|
+
return unwrapped;
|
|
167
|
+
}
|
|
168
|
+
/** Projects unwrapped lng/lat into the planar frame's meters. */
|
|
169
|
+
function projector(positions, crs) {
|
|
170
|
+
if (crs === "EPSG:3857") {
|
|
171
|
+
return ([lng, lat]) => {
|
|
172
|
+
const clamped = Math.max(-WEB_MERCATOR_MAX_LATITUDE, Math.min(WEB_MERCATOR_MAX_LATITUDE, lat));
|
|
173
|
+
return [
|
|
174
|
+
WEB_MERCATOR_RADIUS * toRadians(lng),
|
|
175
|
+
WEB_MERCATOR_RADIUS * Math.log(Math.tan(Math.PI / 4 + toRadians(clamped) / 2)),
|
|
176
|
+
];
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const metersPerDegLon = metersPerDegreeLongitude(meanLatitude(positions));
|
|
180
|
+
return ([lng, lat]) => [lng * metersPerDegLon, lat * METERS_PER_DEGREE_LATITUDE];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Euclidean length of a line, in meters of the planar frame `crs`:
|
|
184
|
+
*
|
|
185
|
+
* - `"local"` (default): a flat-earth approximation centered on the vertices'
|
|
186
|
+
* mean latitude, matching `MeasurementCompat`'s approximation.
|
|
187
|
+
* - `"EPSG:3857"`: spherical Web Mercator, the projected CRS MapLibre renders
|
|
188
|
+
* in. Its meters are not scale-corrected, so lengths grow by `sec(latitude)`
|
|
189
|
+
* away from the equator — which is what an app mirroring ArcGIS planar
|
|
190
|
+
* measurement on a Web Mercator view expects.
|
|
191
|
+
*
|
|
192
|
+
* Unlike the geodesic `length` op in `@honua/geometry`, this never follows
|
|
193
|
+
* the great circle. Longitudes are unwrapped first, so an antimeridian
|
|
194
|
+
* crossing is measured the short way.
|
|
195
|
+
*/
|
|
196
|
+
export function planarLength(positions, crs = "local") {
|
|
197
|
+
if (positions.length < 2)
|
|
198
|
+
return 0;
|
|
199
|
+
const unwrapped = unwrapLongitudes(positions);
|
|
200
|
+
const project = projector(unwrapped, crs);
|
|
201
|
+
let total = 0;
|
|
202
|
+
for (let index = 1; index < unwrapped.length; index += 1) {
|
|
203
|
+
const [x0, y0] = project(unwrapped[index - 1]);
|
|
204
|
+
const [x1, y1] = project(unwrapped[index]);
|
|
205
|
+
total += Math.hypot(x1 - x0, y1 - y0);
|
|
206
|
+
}
|
|
207
|
+
return total;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Euclidean (shoelace) area of a ring, in square meters of the planar frame
|
|
211
|
+
* `crs` (see {@link planarLength}). `positions` need not be closed; the first
|
|
212
|
+
* vertex is implicitly repeated to close the ring.
|
|
213
|
+
*/
|
|
214
|
+
export function planarArea(positions, crs = "local") {
|
|
215
|
+
if (positions.length < 3)
|
|
216
|
+
return 0;
|
|
217
|
+
const unwrapped = unwrapLongitudes(positions);
|
|
218
|
+
const project = projector(unwrapped, crs);
|
|
219
|
+
const closed = [...unwrapped, unwrapped[0]].map(project);
|
|
220
|
+
let sum = 0;
|
|
221
|
+
for (let index = 1; index < closed.length; index += 1) {
|
|
222
|
+
const [x0, y0] = closed[index - 1];
|
|
223
|
+
const [x1, y1] = closed[index];
|
|
224
|
+
sum += x0 * y1 - x1 * y0;
|
|
225
|
+
}
|
|
226
|
+
return Math.abs(sum) / 2;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Whether the closed ring through `positions` crosses itself (a "bow tie").
|
|
230
|
+
* Such a ring has no single enclosed area: the shoelace and geodesic sums
|
|
231
|
+
* cancel the lobes against each other, so reporting their result would be a
|
|
232
|
+
* confident wrong number. Touching at a shared vertex of adjacent edges is
|
|
233
|
+
* not a crossing; collinear overlap of non-adjacent edges is.
|
|
234
|
+
*/
|
|
235
|
+
export function ringSelfIntersects(positions) {
|
|
236
|
+
const ring = unwrapLongitudes(positions);
|
|
237
|
+
const count = ring.length;
|
|
238
|
+
if (count < 4)
|
|
239
|
+
return false;
|
|
240
|
+
for (let first = 0; first < count; first += 1) {
|
|
241
|
+
const a = ring[first];
|
|
242
|
+
const b = ring[(first + 1) % count];
|
|
243
|
+
for (let second = first + 1; second < count; second += 1) {
|
|
244
|
+
// Skip the edge itself and the two edges that share a vertex with it.
|
|
245
|
+
if (second === first || (second + 1) % count === first || (first + 1) % count === second)
|
|
246
|
+
continue;
|
|
247
|
+
const c = ring[second];
|
|
248
|
+
const d = ring[(second + 1) % count];
|
|
249
|
+
if (segmentsIntersect(a, b, c, d))
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
function orientation(p, q, r) {
|
|
256
|
+
const value = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
|
|
257
|
+
if (Math.abs(value) < 1e-12)
|
|
258
|
+
return 0;
|
|
259
|
+
return value > 0 ? 1 : 2;
|
|
260
|
+
}
|
|
261
|
+
function onSegment(p, q, r) {
|
|
262
|
+
return (q[0] <= Math.max(p[0], r[0]) &&
|
|
263
|
+
q[0] >= Math.min(p[0], r[0]) &&
|
|
264
|
+
q[1] <= Math.max(p[1], r[1]) &&
|
|
265
|
+
q[1] >= Math.min(p[1], r[1]));
|
|
266
|
+
}
|
|
267
|
+
function segmentsIntersect(a, b, c, d) {
|
|
268
|
+
const o1 = orientation(a, b, c);
|
|
269
|
+
const o2 = orientation(a, b, d);
|
|
270
|
+
const o3 = orientation(c, d, a);
|
|
271
|
+
const o4 = orientation(c, d, b);
|
|
272
|
+
if (o1 !== o2 && o3 !== o4)
|
|
273
|
+
return true;
|
|
274
|
+
return ((o1 === 0 && onSegment(a, c, b)) ||
|
|
275
|
+
(o2 === 0 && onSegment(a, d, b)) ||
|
|
276
|
+
(o3 === 0 && onSegment(c, a, d)) ||
|
|
277
|
+
(o4 === 0 && onSegment(c, b, d)));
|
|
278
|
+
}
|
|
@@ -11,6 +11,15 @@
|
|
|
11
11
|
* suspended while a mode is active when the map exposes the handle);
|
|
12
12
|
* - **Escape** (or the Cancel button) discards the in-progress sketch.
|
|
13
13
|
*
|
|
14
|
+
* Touch input needs nothing extra: MapLibre reports a tap as `click`, and the
|
|
15
|
+
* Finish button stands in for double-tap (which MapLibre does not report as
|
|
16
|
+
* `dblclick`).
|
|
17
|
+
*
|
|
18
|
+
* While a mode is active the element claims the map pointer
|
|
19
|
+
* (`./map-pointer-claim.js`), so a vertex click that lands on a feature does
|
|
20
|
+
* not also rewrite the shared selection `<honua-map>` feeds to tables,
|
|
21
|
+
* inspectors, and editors.
|
|
22
|
+
*
|
|
14
23
|
* Distance and area are computed with the geodesic ops from the public
|
|
15
24
|
* `@honua/geometry` surface (`length` / `area`), so the widget's numbers match
|
|
16
25
|
* `geometryEngine` parity math everywhere else in the SDK.
|
|
@@ -25,6 +34,12 @@
|
|
|
25
34
|
* in-progress sketch renders as a line + fill overlay in a
|
|
26
35
|
* `honua-measurement` GeoJSON source; on plain stubs the overlay is skipped.
|
|
27
36
|
*
|
|
37
|
+
* ## Units, precision, fidelity, CRS
|
|
38
|
+
* `unit`, `areaUnit`, `precision`, `fidelity`, and `planarCrs` are
|
|
39
|
+
* properties, also settable declaratively as the `unit`, `area-unit`,
|
|
40
|
+
* `precision`, `fidelity`, and `planar-crs` attributes. Unknown values fall
|
|
41
|
+
* back to the default rather than producing a malformed display.
|
|
42
|
+
*
|
|
28
43
|
* ## Accessibility
|
|
29
44
|
* Mode buttons are a `role="group"` of toggle buttons with `aria-pressed`;
|
|
30
45
|
* the live result is announced through a `role="status"` region; Finish /
|
|
@@ -33,14 +48,66 @@
|
|
|
33
48
|
*
|
|
34
49
|
* @module
|
|
35
50
|
*/
|
|
36
|
-
import type { HonuaMeasureMode, HonuaMeasureResult, HonuaMeasurementMap, HonuaMeasurementMessages } from "./types.js";
|
|
51
|
+
import type { HonuaMeasureAreaUnit, HonuaMeasureDistanceUnit, HonuaMeasureFidelity, HonuaMeasureMode, HonuaMeasurePlanarCrs, HonuaMeasureResult, HonuaMeasurementMap, HonuaMeasurementMessages } from "./types.js";
|
|
37
52
|
declare const HTMLElementBase: typeof HTMLElement;
|
|
38
53
|
type LngLat = readonly [number, number];
|
|
54
|
+
/**
|
|
55
|
+
* Distance and area measurement drawn directly on a MapLibre map.
|
|
56
|
+
*
|
|
57
|
+
* @attr {string} for - `id` of the `<honua-map>` (or any element exposing `.map`) to measure on.
|
|
58
|
+
* @attr {string} label - Heading and accessible name of the panel; `messages.label` wins when set.
|
|
59
|
+
* @fires {CustomEvent<HonuaMeasureChangeDetail>} honua-measure-change - A mode, vertex, result, fidelity, or planar CRS change. Bubbles and is composed.
|
|
60
|
+
* @csspart panel - The measurement panel.
|
|
61
|
+
* @csspart mode - Each mode toggle button.
|
|
62
|
+
* @csspart value - The live result readout (`role="status"`).
|
|
63
|
+
* @csspart finish - The Finish button.
|
|
64
|
+
* @csspart cancel - The Cancel button.
|
|
65
|
+
*/
|
|
39
66
|
export declare class HonuaMeasurementElement extends HTMLElementBase {
|
|
40
67
|
#private;
|
|
41
68
|
static get observedAttributes(): string[];
|
|
69
|
+
/** Caller-supplied labels, statuses, and ARIA text; unset entries keep the English defaults. */
|
|
42
70
|
get messages(): HonuaMeasurementMessages;
|
|
43
71
|
set messages(messages: HonuaMeasurementMessages | undefined);
|
|
72
|
+
/**
|
|
73
|
+
* Displayed distance unit. `"auto"` (default) keeps the metric auto-scaling
|
|
74
|
+
* behavior. Changing this reformats the existing full-precision result — it
|
|
75
|
+
* never recomputes from a rounded display value.
|
|
76
|
+
*/
|
|
77
|
+
get unit(): HonuaMeasureDistanceUnit;
|
|
78
|
+
set unit(unit: HonuaMeasureDistanceUnit | undefined);
|
|
79
|
+
/**
|
|
80
|
+
* Displayed area unit. `"auto"` (default) keeps the metric auto-scaling
|
|
81
|
+
* behavior. Changing this reformats the existing full-precision result — it
|
|
82
|
+
* never recomputes from a rounded display value.
|
|
83
|
+
*/
|
|
84
|
+
get areaUnit(): HonuaMeasureAreaUnit;
|
|
85
|
+
set areaUnit(unit: HonuaMeasureAreaUnit | undefined);
|
|
86
|
+
/** Decimal digits for the displayed value. `undefined` (default) uses a per-unit default. */
|
|
87
|
+
get precision(): number | undefined;
|
|
88
|
+
set precision(precision: number | undefined);
|
|
89
|
+
/**
|
|
90
|
+
* Which math computes `result.distance` / `result.area`: `"geodesic"`
|
|
91
|
+
* (default, great-circle over WGS84) or `"planar"` (Euclidean, in the
|
|
92
|
+
* {@link planarCrs} frame — see `./measurement-units.js`). Changing this
|
|
93
|
+
* recomputes from the drawn vertices, since it changes which math produces
|
|
94
|
+
* the canonical value.
|
|
95
|
+
*/
|
|
96
|
+
get fidelity(): HonuaMeasureFidelity;
|
|
97
|
+
set fidelity(fidelity: HonuaMeasureFidelity | undefined);
|
|
98
|
+
/**
|
|
99
|
+
* Planar frame used when {@link fidelity} is `"planar"`: `"local"` (default,
|
|
100
|
+
* flat-earth meters around the sketch) or `"EPSG:3857"` (Web Mercator
|
|
101
|
+
* meters, not scale-corrected). Recomputes like {@link fidelity} does.
|
|
102
|
+
*/
|
|
103
|
+
get planarCrs(): HonuaMeasurePlanarCrs;
|
|
104
|
+
set planarCrs(crs: HonuaMeasurePlanarCrs | undefined);
|
|
105
|
+
/**
|
|
106
|
+
* CRS vertices are recorded in. Always WGS84 (`"EPSG:4326"`): vertices come
|
|
107
|
+
* from map click events, which report geographic lng/lat regardless of the
|
|
108
|
+
* map's own projection. Exposed so a consumer never has to assume it.
|
|
109
|
+
*/
|
|
110
|
+
get crs(): "EPSG:4326";
|
|
44
111
|
/** The MapLibre map this widget draws on. */
|
|
45
112
|
get map(): HonuaMeasurementMap | undefined;
|
|
46
113
|
set map(map: HonuaMeasurementMap | undefined);
|
|
@@ -54,13 +121,17 @@ export declare class HonuaMeasurementElement extends HTMLElementBase {
|
|
|
54
121
|
get vertices(): readonly LngLat[];
|
|
55
122
|
/** Activates a measure mode; `"off"` cancels any in-progress sketch. */
|
|
56
123
|
setMode(mode: HonuaMeasureMode): void;
|
|
57
|
-
/**
|
|
58
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Adds a vertex programmatically (same as a map click). Returns `false`,
|
|
126
|
+
* changing nothing, when no mode is active or the position is not a usable
|
|
127
|
+
* WGS84 vertex (non-finite, or latitude outside `[-90, 90]`).
|
|
128
|
+
*/
|
|
129
|
+
addVertex(lngLat: LngLat): boolean;
|
|
59
130
|
/** Finishes the in-progress sketch (same as double-click). */
|
|
60
131
|
finish(): void;
|
|
61
132
|
/** Cancels the in-progress sketch (same as Escape); the mode stays active. */
|
|
62
133
|
cancel(): void;
|
|
63
|
-
attributeChangedCallback(name: string): void;
|
|
134
|
+
attributeChangedCallback(name: string, _previous?: string | null, value?: string | null): void;
|
|
64
135
|
connectedCallback(): void;
|
|
65
136
|
disconnectedCallback(): void;
|
|
66
137
|
protected render(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measurement.d.ts","sourceRoot":"","sources":["../../../src/web-components/measurement.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"measurement.d.ts","sourceRoot":"","sources":["../../../src/web-components/measurement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAgBH,OAAO,KAAK,EACV,oBAAoB,EAEpB,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAQpB,QAAA,MAAM,eAAe,EAAE,OAAO,WAAkF,CAAC;AAMjH,KAAK,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAIxC;;;;;;;;;;;GAWG;AACH,qBAAa,uBAAwB,SAAQ,eAAe;;IAC1D,WAAkB,kBAAkB,IAAI,MAAM,EAAE,CAE/C;IAcD,gGAAgG;IAChG,IAAW,QAAQ,IAAI,wBAAwB,CAE9C;IACD,IAAW,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,GAAG,SAAS,EAGjE;IAED;;;;OAIG;IACH,IAAW,IAAI,IAAI,wBAAwB,CAE1C;IACD,IAAW,IAAI,CAAC,IAAI,EAAE,wBAAwB,GAAG,SAAS,EAGzD;IAED;;;;OAIG;IACH,IAAW,QAAQ,IAAI,oBAAoB,CAE1C;IACD,IAAW,QAAQ,CAAC,IAAI,EAAE,oBAAoB,GAAG,SAAS,EAGzD;IAED,6FAA6F;IAC7F,IAAW,SAAS,IAAI,MAAM,GAAG,SAAS,CAEzC;IACD,IAAW,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAQjD;IAED;;;;;;OAMG;IACH,IAAW,QAAQ,IAAI,oBAAoB,CAE1C;IACD,IAAW,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,SAAS,EAO7D;IAED;;;;OAIG;IACH,IAAW,SAAS,IAAI,qBAAqB,CAE5C;IACD,IAAW,SAAS,CAAC,GAAG,EAAE,qBAAqB,GAAG,SAAS,EAQ1D;IAED;;;;OAIG;IACH,IAAW,GAAG,IAAI,WAAW,CAE5B;IAUD,6CAA6C;IAC7C,IAAW,GAAG,IAAI,mBAAmB,GAAG,SAAS,CAEhD;IAED,IAAW,GAAG,CAAC,GAAG,EAAE,mBAAmB,GAAG,SAAS,EAMlD;IAED,wEAAwE;IACjE,OAAO,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI;IAI9C,+BAA+B;IAC/B,IAAW,IAAI,IAAI,gBAAgB,CAElC;IAED,yDAAyD;IACzD,IAAW,MAAM,IAAI,kBAAkB,GAAG,SAAS,CAElD;IAED,4DAA4D;IAC5D,IAAW,QAAQ,IAAI,SAAS,MAAM,EAAE,CAEvC;IAED,wEAAwE;IACjE,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAY5C;;;;OAIG;IACI,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAczC,8DAA8D;IACvD,MAAM,IAAI,IAAI;IASrB,8EAA8E;IACvE,MAAM,IAAI,IAAI;IAUd,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IA2B9F,iBAAiB,IAAI,IAAI;IAczB,oBAAoB,IAAI,IAAI;IA+OnC,SAAS,CAAC,MAAM,IAAI,IAAI;CA2GzB;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,QAAQ,wBAA2B,GAAG,IAAI,CAKhF"}
|