@goway.to/maps 0.0.0-stage → 0.1.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +676 -0
  3. package/NOTICE +25 -0
  4. package/README.md +59 -2
  5. package/app.plugin.js +19 -0
  6. package/dist/CameraBinding.web.d.ts +24 -0
  7. package/dist/CameraBinding.web.js +46 -0
  8. package/dist/DefaultMapMarker.d.ts +8 -0
  9. package/dist/DefaultMapMarker.js +30 -0
  10. package/dist/GeoJSONSource.d.ts +2 -0
  11. package/dist/GeoJSONSource.js +17 -0
  12. package/dist/GeoJSONSource.web.d.ts +2 -0
  13. package/dist/GeoJSONSource.web.js +68 -0
  14. package/dist/InteractionBinding.web.d.ts +7 -0
  15. package/dist/InteractionBinding.web.js +35 -0
  16. package/dist/Layer.d.ts +2 -0
  17. package/dist/Layer.js +21 -0
  18. package/dist/Layer.web.d.ts +2 -0
  19. package/dist/Layer.web.js +46 -0
  20. package/dist/LayerStyle.web.d.ts +10 -0
  21. package/dist/LayerStyle.web.js +45 -0
  22. package/dist/LightBinding.web.d.ts +5 -0
  23. package/dist/LightBinding.web.js +19 -0
  24. package/dist/MapAttribution.d.ts +27 -0
  25. package/dist/MapAttribution.js +149 -0
  26. package/dist/MapBrand.d.ts +22 -0
  27. package/dist/MapBrand.js +91 -0
  28. package/dist/MapErrorState.d.ts +9 -0
  29. package/dist/MapErrorState.js +58 -0
  30. package/dist/MapPlugin.d.ts +6 -0
  31. package/dist/MapPlugin.js +3 -0
  32. package/dist/MapPlugin.web.d.ts +2 -0
  33. package/dist/MapPlugin.web.js +43 -0
  34. package/dist/MapView.d.ts +2 -0
  35. package/dist/MapView.js +420 -0
  36. package/dist/MapView.web.d.ts +5 -0
  37. package/dist/MapView.web.js +413 -0
  38. package/dist/Marker.d.ts +13 -0
  39. package/dist/Marker.js +11 -0
  40. package/dist/Marker.web.d.ts +15 -0
  41. package/dist/Marker.web.js +51 -0
  42. package/dist/Markers.d.ts +2 -0
  43. package/dist/Markers.js +13 -0
  44. package/dist/Markers.web.d.ts +2 -0
  45. package/dist/Markers.web.js +13 -0
  46. package/dist/PluginProps.web.d.ts +7 -0
  47. package/dist/PluginProps.web.js +19 -0
  48. package/dist/RasterDEMSource.d.ts +2 -0
  49. package/dist/RasterDEMSource.js +7 -0
  50. package/dist/RasterDEMSource.web.d.ts +2 -0
  51. package/dist/RasterDEMSource.web.js +44 -0
  52. package/dist/SourceData.web.d.ts +6 -0
  53. package/dist/SourceData.web.js +14 -0
  54. package/dist/UserLocation.d.ts +2 -0
  55. package/dist/UserLocation.js +19 -0
  56. package/dist/UserLocation.web.d.ts +2 -0
  57. package/dist/UserLocation.web.js +21 -0
  58. package/dist/brand/BrandedChrome.d.ts +47 -0
  59. package/dist/brand/BrandedChrome.js +51 -0
  60. package/dist/brand/GowayLogo.d.ts +28 -0
  61. package/dist/brand/GowayLogo.js +50 -0
  62. package/dist/brand/artwork.d.ts +130 -0
  63. package/dist/brand/artwork.js +176 -0
  64. package/dist/cameraReports.web.d.ts +50 -0
  65. package/dist/cameraReports.web.js +117 -0
  66. package/dist/cameraTarget.d.ts +14 -0
  67. package/dist/cameraTarget.js +8 -0
  68. package/dist/chromeLift.d.ts +15 -0
  69. package/dist/chromeLift.js +15 -0
  70. package/dist/clusters.d.ts +57 -0
  71. package/dist/clusters.js +102 -0
  72. package/dist/commands.web.d.ts +16 -0
  73. package/dist/commands.web.js +158 -0
  74. package/dist/compactCredit.d.ts +52 -0
  75. package/dist/compactCredit.js +40 -0
  76. package/dist/creditHeight.d.ts +3 -0
  77. package/dist/creditHeight.js +12 -0
  78. package/dist/creditLabels.d.ts +30 -0
  79. package/dist/creditLabels.js +30 -0
  80. package/dist/creditRise.d.ts +28 -0
  81. package/dist/creditRise.js +33 -0
  82. package/dist/dragAxes.web.d.ts +65 -0
  83. package/dist/dragAxes.web.js +70 -0
  84. package/dist/engineContext.web.d.ts +13 -0
  85. package/dist/engineContext.web.js +12 -0
  86. package/dist/index.d.ts +12 -0
  87. package/dist/index.js +12 -0
  88. package/dist/index.web.d.ts +13 -0
  89. package/dist/index.web.js +13 -0
  90. package/dist/labels.d.ts +202 -0
  91. package/dist/labels.js +451 -0
  92. package/dist/loadedCamera.d.ts +64 -0
  93. package/dist/loadedCamera.js +56 -0
  94. package/dist/locationStore.d.ts +16 -0
  95. package/dist/locationStore.js +132 -0
  96. package/dist/mapRef.d.ts +4 -0
  97. package/dist/mapRef.js +13 -0
  98. package/dist/moveMarkers.d.ts +27 -0
  99. package/dist/moveMarkers.js +22 -0
  100. package/dist/nativeCameraReports.d.ts +44 -0
  101. package/dist/nativeCameraReports.js +73 -0
  102. package/dist/plugin.d.ts +63 -0
  103. package/dist/plugin.js +2 -0
  104. package/dist/queryLayers.d.ts +19 -0
  105. package/dist/queryLayers.js +54 -0
  106. package/dist/shared-exports.d.ts +17 -0
  107. package/dist/shared-exports.js +13 -0
  108. package/dist/shared.d.ts +170 -0
  109. package/dist/shared.js +337 -0
  110. package/dist/sourceContext.d.ts +2 -0
  111. package/dist/sourceContext.js +9 -0
  112. package/dist/styleUrl.d.ts +19 -0
  113. package/dist/styleUrl.js +22 -0
  114. package/dist/testing.d.ts +41 -0
  115. package/dist/testing.js +80 -0
  116. package/dist/types.d.ts +348 -0
  117. package/dist/types.js +1 -0
  118. package/dist/useMarkerSlots.d.ts +6 -0
  119. package/dist/useMarkerSlots.js +56 -0
  120. package/dist/useUserLocation.d.ts +44 -0
  121. package/dist/useUserLocation.js +247 -0
  122. package/dist/zoom.d.ts +2 -0
  123. package/dist/zoom.js +10 -0
  124. package/metro.cjs +70 -0
  125. package/package.json +115 -4
@@ -0,0 +1,132 @@
1
+ /**
2
+ * The reader's location permission and position, as external stores.
3
+ *
4
+ * `<UserLocation>` reads both through `useSyncExternalStore`, so the dot
5
+ * appears the moment a permission is granted — by `useUserLocation().locate()`
6
+ * on a tap, or because it already was — with no effect anywhere.
7
+ *
8
+ * The permission is READ (`getForegroundPermissionsAsync`, which never
9
+ * prompts) when the first `<UserLocation>` subscribes, and settled by
10
+ * `useUserLocation` whenever it asks. Nothing here can show a prompt.
11
+ *
12
+ * The position is watched only while something subscribes AND the permission
13
+ * is granted, and it is kept in memory only: no store, cache or history.
14
+ */
15
+ import * as Location from 'expo-location';
16
+ import { isDrawableCoordinate, reportMapDefect } from './shared';
17
+ function createPermissionStore() {
18
+ let state = 'unknown';
19
+ let reading = false;
20
+ const listeners = new Set();
21
+ const notify = () => {
22
+ for (const listener of listeners)
23
+ listener();
24
+ };
25
+ const settle = (next) => {
26
+ if (next === state)
27
+ return;
28
+ state = next;
29
+ notify();
30
+ };
31
+ const read = () => {
32
+ if (reading || state !== 'unknown')
33
+ return;
34
+ reading = true;
35
+ Location.getForegroundPermissionsAsync()
36
+ .then((permission) => settle(permission.granted ? 'granted' : 'denied'))
37
+ .catch(() => settle('denied'))
38
+ .finally(() => {
39
+ reading = false;
40
+ });
41
+ };
42
+ return {
43
+ subscribe(listener) {
44
+ listeners.add(listener);
45
+ read();
46
+ return () => {
47
+ listeners.delete(listener);
48
+ };
49
+ },
50
+ getSnapshot() {
51
+ return state;
52
+ },
53
+ /** What `useUserLocation` learned by asking. */
54
+ settle,
55
+ };
56
+ }
57
+ /** The one permission store; the platform's permission is one per app. */
58
+ export const locationPermission = createPermissionStore();
59
+ function createPositionStore() {
60
+ let position = null;
61
+ let watcher = null;
62
+ let starting = false;
63
+ let unsubscribePermission = null;
64
+ const listeners = new Set();
65
+ const notify = () => {
66
+ for (const listener of listeners)
67
+ listener();
68
+ };
69
+ const stop = () => {
70
+ watcher?.remove();
71
+ watcher = null;
72
+ if (position !== null) {
73
+ position = null;
74
+ notify();
75
+ }
76
+ };
77
+ const start = () => {
78
+ if (watcher || starting || locationPermission.getSnapshot() !== 'granted')
79
+ return;
80
+ starting = true;
81
+ Location.watchPositionAsync({ accuracy: Location.Accuracy.Balanced, distanceInterval: 10 }, (fix) => {
82
+ const next = { latitude: fix.coords.latitude, longitude: fix.coords.longitude };
83
+ // A fix with no usable position is "no dot", not "no map".
84
+ if (!isDrawableCoordinate(next)) {
85
+ reportMapDefect('userLocation', 'Skipped a location fix with a non-drawable coordinate.');
86
+ return;
87
+ }
88
+ position = next;
89
+ notify();
90
+ })
91
+ .then((subscription) => {
92
+ if (listeners.size === 0)
93
+ subscription.remove();
94
+ else
95
+ watcher = subscription;
96
+ })
97
+ .catch(() => {
98
+ // No fix available is not a map failure; the dot simply does not appear.
99
+ })
100
+ .finally(() => {
101
+ starting = false;
102
+ });
103
+ };
104
+ const onPermission = () => {
105
+ if (locationPermission.getSnapshot() === 'granted')
106
+ start();
107
+ else
108
+ stop();
109
+ };
110
+ return {
111
+ subscribe(listener) {
112
+ listeners.add(listener);
113
+ if (listeners.size === 1) {
114
+ unsubscribePermission = locationPermission.subscribe(onPermission);
115
+ onPermission();
116
+ }
117
+ return () => {
118
+ listeners.delete(listener);
119
+ if (listeners.size === 0) {
120
+ unsubscribePermission?.();
121
+ unsubscribePermission = null;
122
+ stop();
123
+ }
124
+ };
125
+ },
126
+ getSnapshot() {
127
+ return position;
128
+ },
129
+ };
130
+ }
131
+ /** The reader's position while a `<UserLocation>` is drawn, in memory only. */
132
+ export const userPosition = createPositionStore();
@@ -0,0 +1,4 @@
1
+ import type { MapViewRef } from './types';
2
+ export declare const MapRefContext: import("react").Context<MapViewRef | null>;
3
+ /** The enclosing `MapView`'s handle. Throws outside one: there is no map to move. */
4
+ export declare function useMap(): MapViewRef;
package/dist/mapRef.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `useMap()` — the nearest `MapView`'s handle, for chrome laid over a map
3
+ * (a "locate me" button, a re-frame) to call from its own event handlers.
4
+ */
5
+ import { createContext, useContext } from 'react';
6
+ export const MapRefContext = createContext(null);
7
+ /** The enclosing `MapView`'s handle. Throws outside one: there is no map to move. */
8
+ export function useMap() {
9
+ const handle = useContext(MapRefContext);
10
+ if (!handle)
11
+ throw new Error('useMap() must be called inside a <MapView>.');
12
+ return handle;
13
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The marks a camera COMMAND carries, so the map reports the camera it
3
+ * produces as the app's move and never as the reader's. Every command
4
+ * `@goway.to/maps` issues is marked; an engine plugin that moves the camera
5
+ * itself passes {@link PROGRAMMATIC} as the command's event data
6
+ * (`map.easeTo(options, PROGRAMMATIC)`).
7
+ */
8
+ /**
9
+ * Marker put on every camera COMMAND the map issues, read back off the event.
10
+ *
11
+ * Marking our own commands (rather than sniffing for a DOM `originalEvent`) is
12
+ * the safer direction of failure: a command somebody forgets to mark reads as a
13
+ * user gesture, so "Search this area" appears once when it need not have. The
14
+ * alternative — treat anything unrecognised as programmatic — hides the button
15
+ * for good and looks like the feature was never built.
16
+ */
17
+ export declare const PROGRAMMATIC: {
18
+ readonly gowayProgrammatic: true;
19
+ };
20
+ /**
21
+ * One frame of a caller-driven animation (`MapCameraOptions.transient`): a
22
+ * programmatic move whose `moveend` is not a settled camera.
23
+ */
24
+ export declare const PROGRAMMATIC_TRANSIENT: {
25
+ readonly gowayProgrammatic: true;
26
+ readonly gowayTransient: true;
27
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The marks a camera COMMAND carries, so the map reports the camera it
3
+ * produces as the app's move and never as the reader's. Every command
4
+ * `@goway.to/maps` issues is marked; an engine plugin that moves the camera
5
+ * itself passes {@link PROGRAMMATIC} as the command's event data
6
+ * (`map.easeTo(options, PROGRAMMATIC)`).
7
+ */
8
+ /**
9
+ * Marker put on every camera COMMAND the map issues, read back off the event.
10
+ *
11
+ * Marking our own commands (rather than sniffing for a DOM `originalEvent`) is
12
+ * the safer direction of failure: a command somebody forgets to mark reads as a
13
+ * user gesture, so "Search this area" appears once when it need not have. The
14
+ * alternative — treat anything unrecognised as programmatic — hides the button
15
+ * for good and looks like the feature was never built.
16
+ */
17
+ export const PROGRAMMATIC = { gowayProgrammatic: true };
18
+ /**
19
+ * One frame of a caller-driven animation (`MapCameraOptions.transient`): a
20
+ * programmatic move whose `moveend` is not a settled camera.
21
+ */
22
+ export const PROGRAMMATIC_TRANSIENT = { gowayProgrammatic: true, gowayTransient: true };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The native canvas's camera reports: MapLibre Native's region events and its
3
+ * style load turned into `MapViewProps.onCameraChanged`.
4
+ *
5
+ * The counterpart of `cameraReports.web.ts`, kept free of React Native so it
6
+ * runs in a test; `MapCanvas.native.tsx` creates one per engine (a retry
7
+ * remounts the engine and creates another) and forwards the engine's events.
8
+ *
9
+ * - `onRegionIsChanging`: the live camera, never settled.
10
+ * - `onRegionDidChange`: a settled camera, unless the move was a frame of a
11
+ * caller-driven animation (`MapCameraOptions.transient`).
12
+ * - the style load: the camera the map opened at, by the rule both forks
13
+ * share (`loadedCamera.ts`), read with `getViewState()`.
14
+ *
15
+ * `source` is the engine's own answer: MapLibre Native says whether a finger
16
+ * caused the change, so GoWay never infers it from a marker on the command.
17
+ */
18
+ import { type CameraReading } from './loadedCamera';
19
+ import type { MapCameraChange } from './types';
20
+ /** MapLibre Native's `ViewState`, structurally (`center` and `bounds` in MapLibre order). */
21
+ export interface NativeViewState {
22
+ center: readonly [number, number];
23
+ zoom: number;
24
+ bearing: number;
25
+ pitch: number;
26
+ bounds: readonly [number, number, number, number];
27
+ }
28
+ /** A region event's payload (`ViewStateChangeEvent`). */
29
+ export interface NativeRegionChange extends NativeViewState {
30
+ userInteraction: boolean;
31
+ }
32
+ /** MapLibre Native's view state, in GoWay's terms. */
33
+ export declare function cameraReadingOf(view: NativeViewState): CameraReading;
34
+ export interface NativeCameraReports {
35
+ /** GoWay commanded the camera; `transient` for one frame of a caller-driven animation. */
36
+ commanded(transient: boolean): void;
37
+ regionWillChange(region: NativeRegionChange): void;
38
+ regionIsChanging(region: NativeRegionChange): void;
39
+ /** `true` when the change was a settled camera, `false` for a transient frame. */
40
+ regionDidChange(region: NativeRegionChange): boolean;
41
+ /** The style finished loading; only the engine's first load reports. */
42
+ loaded(getViewState: () => Promise<NativeViewState>): void;
43
+ }
44
+ export declare function createNativeCameraReports(listener: () => ((change: MapCameraChange) => void) | null | undefined): NativeCameraReports;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The native canvas's camera reports: MapLibre Native's region events and its
3
+ * style load turned into `MapViewProps.onCameraChanged`.
4
+ *
5
+ * The counterpart of `cameraReports.web.ts`, kept free of React Native so it
6
+ * runs in a test; `MapCanvas.native.tsx` creates one per engine (a retry
7
+ * remounts the engine and creates another) and forwards the engine's events.
8
+ *
9
+ * - `onRegionIsChanging`: the live camera, never settled.
10
+ * - `onRegionDidChange`: a settled camera, unless the move was a frame of a
11
+ * caller-driven animation (`MapCameraOptions.transient`).
12
+ * - the style load: the camera the map opened at, by the rule both forks
13
+ * share (`loadedCamera.ts`), read with `getViewState()`.
14
+ *
15
+ * `source` is the engine's own answer: MapLibre Native says whether a finger
16
+ * caused the change, so GoWay never infers it from a marker on the command.
17
+ */
18
+ import { createLoadedCameraReporter } from './loadedCamera';
19
+ /** MapLibre Native's view state, in GoWay's terms. */
20
+ export function cameraReadingOf(view) {
21
+ const [west, south, east, north] = view.bounds;
22
+ return {
23
+ viewport: {
24
+ latitude: view.center[1],
25
+ longitude: view.center[0],
26
+ zoom: view.zoom,
27
+ bearing: view.bearing,
28
+ pitch: view.pitch,
29
+ },
30
+ bounds: { west, south, east, north },
31
+ };
32
+ }
33
+ export function createNativeCameraReports(listener) {
34
+ const loadedReport = createLoadedCameraReporter();
35
+ // A frame of a caller-driven animation is not a settled camera. The flag is
36
+ // the latest COMMAND's: every camera command declares it, and a move the
37
+ // reader starts ends it — a flyover the reader interrupted, or one that
38
+ // stopped without its closing ordinary move, leaves nothing behind, so the
39
+ // reader's settles are final again (on the web the marker is per event).
40
+ // Not cleared at every move start: a flyover's frames cross the bridge as
41
+ // commands ahead of their region events, so the next command's flag can
42
+ // already be set when the previous frame's move starts.
43
+ let transientCommand = false;
44
+ const report = (region, isFinal) => {
45
+ listener()?.({
46
+ ...cameraReadingOf(region),
47
+ source: region.userInteraction ? 'user' : 'programmatic',
48
+ isFinal,
49
+ });
50
+ };
51
+ return {
52
+ commanded(transient) {
53
+ transientCommand = transient;
54
+ },
55
+ regionWillChange(region) {
56
+ if (region.userInteraction)
57
+ transientCommand = false;
58
+ loadedReport.moveStarted();
59
+ },
60
+ regionIsChanging(region) {
61
+ report(region, false);
62
+ },
63
+ regionDidChange(region) {
64
+ const transient = transientCommand && !region.userInteraction;
65
+ loadedReport.moveEnded(transient);
66
+ report(region, !transient);
67
+ return !transient;
68
+ },
69
+ loaded(getViewState) {
70
+ loadedReport.loaded(async () => cameraReadingOf(await getViewState()), listener);
71
+ },
72
+ };
73
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Engine plugins: what an app attaches to the web engine beyond sources,
3
+ * layers and markers — a three.js custom layer, terrain, a camera rule.
4
+ *
5
+ * A plugin is an imperative object with a lifecycle, and the `<MapPlugin>`
6
+ * component is the ONE place an app's plugin is bound to the engine: it
7
+ * attaches when the engine is ready, hands the plugin every new `props` through
8
+ * `update`, and detaches it when the component unmounts or the engine is
9
+ * rebuilt. The app passes props; it never runs an effect.
10
+ *
11
+ * Web only. On native `<MapPlugin>` renders nothing: MapLibre Native has no
12
+ * custom-layer API.
13
+ *
14
+ * `@goway.to/maps/plugin` is this module: the protocol, and the guards every
15
+ * coordinate and command crossing into an engine goes through — pure, with no
16
+ * React and no React Native, so an engine module (and its tests) can load it.
17
+ */
18
+ import type { Map as MapLibreMap } from 'maplibre-gl';
19
+ export { asFinite, isDrawableBounds, isDrawableCoordinate, isDrawableGeoJSON, optionalScalar, reportMapDefect, runEngineCommand, toLngLat, } from './shared';
20
+ export { PROGRAMMATIC, PROGRAMMATIC_TRANSIENT } from './moveMarkers';
21
+ /** A source's failure, as the engine reported it. */
22
+ export interface MapSourceError {
23
+ sourceId: string;
24
+ /** The HTTP status, when the failure was a response. */
25
+ status?: number;
26
+ message?: string;
27
+ }
28
+ export interface MapPluginHost {
29
+ /** The element the engine draws into. */
30
+ container: HTMLElement;
31
+ }
32
+ export interface MapPluginHandle<Props> {
33
+ /** New props for an attached plugin. */
34
+ update?(props: Props): void;
35
+ /** Undo everything `attach` did. */
36
+ detach(): void;
37
+ /**
38
+ * A press at a screen point, after the markers and before the basemap's
39
+ * labels. `true` takes it: no label is resolved and `onPress` does not fire.
40
+ */
41
+ press?(point: {
42
+ x: number;
43
+ y: number;
44
+ }): boolean;
45
+ /** Whether the pointer at `point` is over something this plugin draws (the cursor says so). */
46
+ hover?(point: {
47
+ x: number;
48
+ y: number;
49
+ }): boolean;
50
+ /**
51
+ * Whether a source's error is the plugin's to handle — a decorative source
52
+ * that may legitimately 404 — rather than the map's degraded state. The
53
+ * plugin reports what it takes, if anything is worth reporting.
54
+ */
55
+ ownsError?(error: MapSourceError): boolean;
56
+ }
57
+ export interface MapPlugin<Props> {
58
+ attach(map: MapLibreMap, props: Props, host: MapPluginHost): MapPluginHandle<Props>;
59
+ }
60
+ export interface MapPluginProps<Props> {
61
+ plugin: MapPlugin<Props>;
62
+ props: Props;
63
+ }
package/dist/plugin.js ADDED
@@ -0,0 +1,2 @@
1
+ export { asFinite, isDrawableBounds, isDrawableCoordinate, isDrawableGeoJSON, optionalScalar, reportMapDefect, runEngineCommand, toLngLat, } from './shared';
2
+ export { PROGRAMMATIC, PROGRAMMATIC_TRANSIENT } from './moveMarkers';
@@ -0,0 +1,19 @@
1
+ export interface QueryLayers {
2
+ tap: readonly string[];
3
+ labels: readonly string[];
4
+ }
5
+ export declare const NO_QUERY_LAYERS: QueryLayers;
6
+ interface StyleLayerLike {
7
+ id: string;
8
+ type: string;
9
+ 'source-layer'?: string;
10
+ }
11
+ /** The tap and label layer ids of a style document. */
12
+ export declare function queryLayersOf(layers: readonly StyleLayerLike[] | undefined): QueryLayers;
13
+ /**
14
+ * The query layers of the style at `url`, fetched once per URL and shared. A
15
+ * style that cannot be read answers with none: presses then resolve to bare
16
+ * map, which is what they did before labels were tappable.
17
+ */
18
+ export declare function queryLayersForUrl(url: string): Promise<QueryLayers>;
19
+ export {};
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Which style layers a press and a label read are queried against, derived
3
+ * from the style document itself.
4
+ *
5
+ * Deriving beats naming: layers can be flag-gated, renamed, or absent from a
6
+ * style GoWay did not author, and the OpenMapTiles `source-layer` is the part
7
+ * no style gets to rename. Web reads the loaded style from the engine; native,
8
+ * whose engine does not hand the style back to JS, reads the same document
9
+ * over the network once per URL ({@link queryLayersForUrl}).
10
+ *
11
+ * `tap` is symbols AND circles — the POI dot is what sits at the anchor, and
12
+ * the label is drawn below it, so a tap on the dot has to resolve. `labels` is
13
+ * symbols only: a dot whose label lost its collision is not a NAME on screen.
14
+ */
15
+ import { isLabelSourceLayer } from './labels';
16
+ export const NO_QUERY_LAYERS = { tap: [], labels: [] };
17
+ /** The tap and label layer ids of a style document. */
18
+ export function queryLayersOf(layers) {
19
+ const tap = [];
20
+ const labels = [];
21
+ for (const layer of layers ?? []) {
22
+ const sourceLayer = layer['source-layer'];
23
+ if (!sourceLayer || !isLabelSourceLayer(sourceLayer))
24
+ continue;
25
+ if (layer.type === 'symbol') {
26
+ tap.push(layer.id);
27
+ labels.push(layer.id);
28
+ }
29
+ else if (layer.type === 'circle') {
30
+ tap.push(layer.id);
31
+ }
32
+ }
33
+ return { tap, labels };
34
+ }
35
+ const byUrl = new Map();
36
+ /**
37
+ * The query layers of the style at `url`, fetched once per URL and shared. A
38
+ * style that cannot be read answers with none: presses then resolve to bare
39
+ * map, which is what they did before labels were tappable.
40
+ */
41
+ export function queryLayersForUrl(url) {
42
+ let pending = byUrl.get(url);
43
+ if (!pending) {
44
+ pending = fetch(url)
45
+ .then((response) => (response.ok ? response.json() : null))
46
+ .then((style) => queryLayersOf(style?.layers))
47
+ .catch(() => {
48
+ byUrl.delete(url);
49
+ return NO_QUERY_LAYERS;
50
+ });
51
+ byUrl.set(url, pending);
52
+ }
53
+ return pending;
54
+ }
@@ -0,0 +1,17 @@
1
+ /** What both entries export beyond their platform's components. */
2
+ export { useMap } from './mapRef';
3
+ export { useUserLocation } from './useUserLocation';
4
+ export type { LocationErrorReason, LocationPermissionStatus, UserLocationApi, UserLocationState, } from './useUserLocation';
5
+ export { DefaultMapMarker } from './DefaultMapMarker';
6
+ export { MAP_CREDIT_CLEARANCE, MAP_DATA_CREDITS } from './MapAttribution';
7
+ export { MAP_BRAND_CLEARANCE, MAP_BRAND_HEIGHT, MAP_BRAND_WIDTH } from './MapBrand';
8
+ export { CREDIT_GAP, creditRise } from './creditRise';
9
+ export { sameLabelSet } from './labels';
10
+ export { MapChromeLiftContext, useMapChromeLift } from './chromeLift';
11
+ export type { MapChromeLift } from './chromeLift';
12
+ export { MapCreditHeightContext, useMapCreditHeight } from './creditHeight';
13
+ export { BrandedChromeProvider, useBrandedChrome } from './brand/BrandedChrome';
14
+ export { GowayLogo, gowayLogoHeight } from './brand/GowayLogo';
15
+ export type { GowayLogoProps, GowayLogoVariant } from './brand/GowayLogo';
16
+ export { DEFAULT_MAP_STYLE } from './styleUrl';
17
+ export type { GeoBoundingBox, GeoCoordinate, GeoJSONSourceProps, LayerProps, MapCameraChange, MapCameraOptions, MapCameraTarget, MapCreditDisclosure, MapCreditLink, MapError, MapErrorReason, MapFitOptions, MapFrame, MapInitialViewState, MapInteractionOptions, MapLabelFeature, MapLabelKind, MapLayerType, MapMarkerCluster, MapMarkerData, MapMarkerItem, MapMoveSource, MapPadding, MapPressEvent, MapViewport, MapViewProps, MapViewRef, MarkersProps, RasterDEMSourceProps, ResolvedMapViewport, UserLocationProps, } from './types';
@@ -0,0 +1,13 @@
1
+ /** What both entries export beyond their platform's components. */
2
+ export { useMap } from './mapRef';
3
+ export { useUserLocation } from './useUserLocation';
4
+ export { DefaultMapMarker } from './DefaultMapMarker';
5
+ export { MAP_CREDIT_CLEARANCE, MAP_DATA_CREDITS } from './MapAttribution';
6
+ export { MAP_BRAND_CLEARANCE, MAP_BRAND_HEIGHT, MAP_BRAND_WIDTH } from './MapBrand';
7
+ export { CREDIT_GAP, creditRise } from './creditRise';
8
+ export { sameLabelSet } from './labels';
9
+ export { MapChromeLiftContext, useMapChromeLift } from './chromeLift';
10
+ export { MapCreditHeightContext, useMapCreditHeight } from './creditHeight';
11
+ export { BrandedChromeProvider, useBrandedChrome } from './brand/BrandedChrome';
12
+ export { GowayLogo, gowayLogoHeight } from './brand/GowayLogo';
13
+ export { DEFAULT_MAP_STYLE } from './styleUrl';
@@ -0,0 +1,170 @@
1
+ import type { GeoBoundingBox, GeoCoordinate, MapInteractionOptions, MapMarkerData, MapPadding, MapViewport } from './types';
2
+ /** MapLibre position order, on both engines. */
3
+ export type LngLat = [longitude: number, latitude: number];
4
+ /** MapLibre `[west, south, east, north]`, on both engines. */
5
+ export type LngLatBoundsArray = [west: number, south: number, east: number, north: number];
6
+ export declare function toLngLat(coordinate: GeoCoordinate): LngLat;
7
+ export declare function fromLngLat(position: readonly [number, number]): GeoCoordinate;
8
+ /**
9
+ * A box in engine order, its east edge carried past 180 when it crosses the
10
+ * antimeridian so both engines frame the strip it describes.
11
+ */
12
+ export declare function toBoundsArray(bounds: GeoBoundingBox): LngLatBoundsArray;
13
+ /** Every interaction is on unless a caller turns it off. */
14
+ export declare function resolveInteraction(options?: MapInteractionOptions): Required<MapInteractionOptions>;
15
+ /**
16
+ * Normalise a `moveTo` target: a bare coordinate leaves the camera's zoom alone.
17
+ *
18
+ * `Number.isFinite`, not `typeof === 'number'` — `typeof NaN` IS `'number'`, so
19
+ * the old test accepted `{ latitude, longitude, zoom: NaN }` as a viewport and
20
+ * passed that `NaN` on to the engine. See {@link asFinite} for what that costs.
21
+ * A viewport whose zoom is not a number is treated as the bare coordinate it
22
+ * usefully is.
23
+ */
24
+ export declare function isViewport(target: GeoCoordinate | MapViewport): target is MapViewport;
25
+ /**
26
+ * A camera scalar the engine can use, or `undefined`.
27
+ *
28
+ * Zoom, bearing, pitch, duration and maxZoom are the seam's unchecked numbers,
29
+ * and a `NaN` in any of them is not a bad frame — it is the SAME crash a bad
30
+ * coordinate is, one step later. `transform.setZoom(NaN)` survives MapLibre's
31
+ * own `clamp` (every comparison against `NaN` is false), so `_scale` and
32
+ * `worldSize` become `NaN`; the next unprojection — `map.getBounds()`, which
33
+ * this canvas calls on every `move` event, and which `jumpTo` fires
34
+ * SYNCHRONOUSLY — builds a `LngLat` out of two `NaN`s and throws
35
+ * `Invalid LngLat object: (NaN, NaN)` from inside the caller's own React
36
+ * handler. The camera simply not moving is the better failure.
37
+ */
38
+ export declare function asFinite(value: number | null | undefined): number | undefined;
39
+ /**
40
+ * A scalar an engine's options object may carry — or NOTHING AT ALL.
41
+ *
42
+ * ## The defect this exists to remove
43
+ *
44
+ * `{ maxZoom: undefined }` and `{}` look interchangeable in TypeScript, and to
45
+ * MapLibre GL JS they are opposites. `Map._cameraForBoxAndBearing` fills in its
46
+ * own defaults with
47
+ *
48
+ * ```
49
+ * options = extend({ padding, offset: [0, 0], maxZoom: this.transform.maxZoom }, options);
50
+ * ```
51
+ *
52
+ * and `extend` is `for (const k in src) dest[k] = src[k]` — an OWN key wins
53
+ * even when its value is `undefined`. So passing the key at all replaces the
54
+ * engine's maximum zoom with `undefined`, and the very next line is
55
+ *
56
+ * ```
57
+ * const zoom = Math.min(scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom);
58
+ * ```
59
+ *
60
+ * `Math.min(anything, undefined)` is `NaN`. That `NaN` divides into
61
+ * `offsetAtFinalZoom`, reaches the centre `cameraForBoxAndBearing` unprojects,
62
+ * and `new LngLat(NaN, NaN)` THROWS — from inside the React effect that asked
63
+ * for the fit, which is the error boundary and the whole app. Omitting the key
64
+ * is the only way to mean "use yours".
65
+ *
66
+ * Every optional scalar this seam forwards therefore goes through here, so
67
+ * "the caller did not say" can never arrive as "the caller said nothing".
68
+ */
69
+ export declare function optionalScalar<K extends string>(key: K, value: number | null | undefined): Partial<Record<K, number>>;
70
+ /**
71
+ * Fit padding, expanded to the four-sided form both engines want — or `null`
72
+ * when it is not a number the engine can use.
73
+ *
74
+ * See {@link isDrawableCoordinate} for why a non-finite number is refused here
75
+ * rather than passed on: MapLibre's `cameraForBoxAndBearing` divides the free
76
+ * viewport by the padding, and `NaN` propagates all the way to the LngLat it
77
+ * constructs for the new centre, which throws. A NEGATIVE padding does not
78
+ * throw, but it is never what a caller meant (it frames content OUTSIDE the
79
+ * viewport), and a negative pair is one of the ways the free space lands on
80
+ * exactly zero, which does throw. So negatives clamp to zero and non-finite
81
+ * values refuse the fit outright.
82
+ */
83
+ export declare function resolvePadding(padding: MapPadding | undefined, fallback: number): {
84
+ top: number;
85
+ right: number;
86
+ bottom: number;
87
+ left: number;
88
+ } | null;
89
+ /**
90
+ * Both MapLibre engines build a `LngLat` out of whatever they are handed, and
91
+ * `LngLat` THROWS on a non-number — `Invalid LngLat object: (NaN, NaN)`. Thrown
92
+ * from a React effect (setting a marker's position, framing a route) that
93
+ * reaches the nearest error boundary, so one bad pin takes the whole screen
94
+ * down and the user sees "Something went wrong" instead of a map.
95
+ *
96
+ * `@goway.to/maps` is the single seam every coordinate crosses on its way to
97
+ * an engine, so it is where that stops being possible. A marker whose
98
+ * coordinate is not drawable is DROPPED (a map missing one pin is a degraded
99
+ * map; a map that throws is no map) and a camera move whose target is not
100
+ * drawable is a no-op.
101
+ *
102
+ * ## Why this is not `isValidCoordinate`
103
+ *
104
+ * `@goway.to/sdk/geo`'s `isValidCoordinate` is the PRODUCT's rule — a real point
105
+ * on the earth, longitude within ±180. This is the ENGINE's rule, which is
106
+ * looser on longitude on purpose: MapLibre reports unwrapped longitudes across
107
+ * the antimeridian (a click at 181°E comes back as 181, not −179), and feeding
108
+ * one of those back in is legal and does the right thing. Rejecting it here
109
+ * would break panning past the date line in the name of safety. Latitude is
110
+ * bounded, because `LngLat` throws outside ±90 too.
111
+ */
112
+ export declare function isDrawableCoordinate(value: unknown): value is GeoCoordinate;
113
+ /** The same rule as {@link isDrawableCoordinate}, applied to a box's corners. */
114
+ export declare function isDrawableBounds(bounds: GeoBoundingBox | null | undefined): bounds is GeoBoundingBox;
115
+ /**
116
+ * The markers an engine may be given, with the undrawable ones removed.
117
+ *
118
+ * Returns the INPUT ARRAY when nothing is wrong, so the common path adds one
119
+ * pass and no allocation, and the forks' effect dependencies do not change
120
+ * identity every render.
121
+ */
122
+ export declare function drawableMarkers(markers: readonly MapMarkerData[] | undefined): readonly MapMarkerData[];
123
+ /**
124
+ * Every position in a GeoJSON document is a point the engine can project, and
125
+ * every geometry has enough of them to BE that geometry.
126
+ *
127
+ * The arity check is not pedantry about RFC 7946: a one-point `LineString` and
128
+ * an empty ring are exactly what a mis-sliced route produces, they draw
129
+ * nothing, and they are indistinguishable from "the route did not arrive"
130
+ * unless something says so.
131
+ *
132
+ * A composite geometry needs the same check one level up, for the same reason
133
+ * and one that is easier to miss: `[].every()` is `true`, so an empty
134
+ * `coordinates` array satisfies every per-part rule vacuously. An empty
135
+ * `Polygon` has no ring that can fail `ring()`, and a `MultiPolygon` of `[[]]`
136
+ * has no polygon that can. Both are what a slice that matched nothing
137
+ * produces, both reach the engine, and both draw exactly nothing — so every
138
+ * composite array is required to be non-empty BEFORE its parts are checked.
139
+ */
140
+ export declare function isDrawableGeoJSON(data: GeoJSON.GeoJSON | null | undefined): boolean;
141
+ export declare function reportMapDefect(key: string, message: string): void;
142
+ /**
143
+ * Run one engine command, and let a throw cost the COMMAND rather than the app.
144
+ *
145
+ * This is the BACKSTOP, deliberately separate from the checks above. Those
146
+ * refuse the bad values we know about; this one is for the ones we do not. Every
147
+ * guard in this file was written after a crash, which is the evidence that the
148
+ * next non-finite number will arrive from somewhere nobody predicted — a
149
+ * renderer upgrade that starts rejecting an input it used to accept, a new
150
+ * option forwarded through an unexamined path, a device API that changes shape.
151
+ *
152
+ * What makes that catastrophic rather than cosmetic is WHERE these calls run:
153
+ * a camera move issued from a `useEffect` throws inside React's commit phase,
154
+ * so the nearest error boundary unmounts the tree and the user gets "Something
155
+ * went wrong" instead of a map. A camera that did not move is a map. That is
156
+ * the whole trade, and it is not close.
157
+ *
158
+ * It is not a licence to stop validating: a swallowed throw is a `console.warn`
159
+ * nobody reads, so anything we can name is still refused BEFORE it gets here,
160
+ * by a check that says which value was wrong and why.
161
+ *
162
+ * Answers whether the command ran: `false` is a dropped command, which a
163
+ * caller waiting on its outcome (a camera move's settle) must not wait for.
164
+ */
165
+ export declare function runEngineCommand(command: string, run: () => void): boolean;
166
+ /**
167
+ * `key NaN` — never `JSON.stringify`, which prints `NaN` and `Infinity` as
168
+ * `null` and so erases the one detail the reader needs.
169
+ */
170
+ export declare function describeNumbers(value: unknown): string;