isard 0.8.0 → 1.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.
- package/CHANGELOG.md +47 -0
- package/dist/{basemaps.d.ts → basemap/basemaps.d.ts} +4 -4
- package/dist/{basemaps.js → basemap/basemaps.js} +3 -15
- package/dist/basemap/basemaps.js.map +1 -0
- package/dist/{grid.d.ts → basemap/grid.d.ts} +1 -1
- package/dist/{grid.js → basemap/grid.js} +2 -2
- package/dist/basemap/grid.js.map +1 -0
- package/dist/basemap/mvt.js.map +1 -0
- package/dist/{pmtiles.d.ts → basemap/pmtiles.d.ts} +6 -0
- package/dist/{pmtiles.js → basemap/pmtiles.js} +2 -2
- package/dist/basemap/pmtiles.js.map +1 -0
- package/dist/{resample.d.ts → basemap/resample.d.ts} +9 -0
- package/dist/{resample.js → basemap/resample.js} +2 -2
- package/dist/basemap/resample.js.map +1 -0
- package/dist/{terrain.js → basemap/terrain.js} +1 -1
- package/dist/basemap/terrain.js.map +1 -0
- package/dist/{vectorstyle.js → basemap/vectorstyle.js} +1 -1
- package/dist/basemap/vectorstyle.js.map +1 -0
- package/dist/{vectortiles.d.ts → basemap/vectortiles.d.ts} +9 -9
- package/dist/{vectortiles.js → basemap/vectortiles.js} +19 -8
- package/dist/basemap/vectortiles.js.map +1 -0
- package/dist/{wmts.js → basemap/wmts.js} +3 -3
- package/dist/basemap/wmts.js.map +1 -0
- package/dist/basemap.worker.js +2 -2
- package/dist/basemap.worker.js.map +10 -10
- package/dist/bind/layers.d.ts +37 -0
- package/dist/bind/layers.js +130 -0
- package/dist/bind/layers.js.map +1 -0
- package/dist/{crs.d.ts → data/crs.d.ts} +1 -1
- package/dist/data/crs.js.map +1 -0
- package/dist/{gpx.d.ts → data/gpx.d.ts} +1 -1
- package/dist/data/gpx.js.map +1 -0
- package/dist/{kml.d.ts → data/kml.d.ts} +1 -1
- package/dist/data/kml.js.map +1 -0
- package/dist/{topojson.d.ts → data/topojson.d.ts} +1 -1
- package/dist/data/topojson.js.map +1 -0
- package/dist/data/xml.js.map +1 -0
- package/dist/decoder.worker.js +1 -1
- package/dist/decoder.worker.js.map +4 -4
- package/dist/{decoders.d.ts → engine/decoders.d.ts} +1 -1
- package/dist/engine/decoders.js.map +1 -0
- package/dist/{loader.d.ts → engine/loader.d.ts} +1 -1
- package/dist/{loader.js → engine/loader.js} +4 -0
- package/dist/engine/loader.js.map +1 -0
- package/dist/{protocol.d.ts → engine/protocol.d.ts} +1 -1
- package/dist/engine/protocol.js +3 -0
- package/dist/engine/protocol.js.map +1 -0
- package/dist/engine/requests.js.map +1 -0
- package/dist/engine-loader.d.ts +16 -5
- package/dist/engine-loader.js +39 -8
- package/dist/engine-loader.js.map +1 -1
- package/dist/formats.d.ts +3 -3
- package/dist/formats.js +3 -3
- package/dist/formats.js.map +1 -1
- package/dist/geoplateforme.d.ts +53 -3
- package/dist/geoplateforme.js +35 -1
- package/dist/geoplateforme.js.map +1 -1
- package/dist/index.d.ts +32 -32
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/isard_bg.wasm +0 -0
- package/dist/isard_core_bg.wasm +0 -0
- package/dist/layers/extrusions.js.map +1 -0
- package/dist/{features.js → layers/features.js} +3 -3
- package/dist/layers/features.js.map +1 -0
- package/dist/{glyphs.d.ts → layers/glyphs.d.ts} +3 -3
- package/dist/{glyphs.js → layers/glyphs.js} +11 -1
- package/dist/layers/glyphs.js.map +1 -0
- package/dist/{icons.d.ts → layers/icons.d.ts} +2 -2
- package/dist/{icons.js → layers/icons.js} +8 -1
- package/dist/layers/icons.js.map +1 -0
- package/dist/{layers.d.ts → layers/layers.d.ts} +11 -5
- package/dist/{layers.js → layers/layers.js} +17 -6
- package/dist/layers/layers.js.map +1 -0
- package/dist/{panoramas.d.ts → layers/panoramas.d.ts} +1 -1
- package/dist/{panoramas.js → layers/panoramas.js} +2 -2
- package/dist/layers/panoramas.js.map +1 -0
- package/dist/{pointclouds.d.ts → layers/pointclouds.d.ts} +2 -2
- package/dist/{pointclouds.js → layers/pointclouds.js} +1 -1
- package/dist/layers/pointclouds.js.map +1 -0
- package/dist/{symbols.d.ts → layers/symbols.d.ts} +2 -2
- package/dist/{symbols.js → layers/symbols.js} +1 -1
- package/dist/layers/symbols.js.map +1 -0
- package/dist/map.d.ts +93 -12
- package/dist/map.js +119 -28
- package/dist/map.js.map +1 -1
- package/dist/projections.d.ts +3 -3
- package/dist/projections.js +2 -2
- package/dist/projections.js.map +1 -1
- package/dist/react/index.d.ts +119 -0
- package/dist/react/index.js +409 -0
- package/dist/react/index.js.map +1 -0
- package/dist/settings.d.ts +1 -1
- package/dist/settings.js +2 -2
- package/dist/settings.js.map +1 -1
- package/dist/types.d.ts +16 -12
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/{controls.d.ts → ui/controls.d.ts} +5 -4
- package/dist/{controls.js → ui/controls.js} +0 -8
- package/dist/ui/controls.js.map +1 -0
- package/dist/{interaction.d.ts → ui/interaction.d.ts} +7 -3
- package/dist/{interaction.js → ui/interaction.js} +1 -1
- package/dist/ui/interaction.js.map +1 -0
- package/dist/{overlay.d.ts → ui/overlay.d.ts} +2 -2
- package/dist/ui/overlay.js.map +1 -0
- package/dist/ui/style.js.map +1 -0
- package/dist/{animation.d.ts → util/animation.d.ts} +6 -2
- package/dist/util/animation.js.map +1 -0
- package/dist/util/color.js.map +1 -0
- package/dist/util/geo.js.map +1 -0
- package/dist/util/lru.js.map +1 -0
- package/dist/{basemap.worker.d.ts → workers/basemap.worker.d.ts} +14 -4
- package/dist/workers/basemap.worker.js +647 -0
- package/dist/workers/basemap.worker.js.map +1 -0
- package/package.json +31 -2
- package/vite.d.ts +17 -0
- package/vite.js +64 -0
- package/dist/animation.js.map +0 -1
- package/dist/basemaps.js.map +0 -1
- package/dist/color.js.map +0 -1
- package/dist/controls.js.map +0 -1
- package/dist/crs.js.map +0 -1
- package/dist/decoders.js.map +0 -1
- package/dist/extrusions.js.map +0 -1
- package/dist/features.js.map +0 -1
- package/dist/geo.js.map +0 -1
- package/dist/glyphs.js.map +0 -1
- package/dist/gpx.js.map +0 -1
- package/dist/grid.js.map +0 -1
- package/dist/icons.js.map +0 -1
- package/dist/interaction.js.map +0 -1
- package/dist/kml.js.map +0 -1
- package/dist/layers.js.map +0 -1
- package/dist/loader.js.map +0 -1
- package/dist/lru.js.map +0 -1
- package/dist/mvt.js.map +0 -1
- package/dist/overlay.js.map +0 -1
- package/dist/panoramas.js.map +0 -1
- package/dist/pmtiles.js.map +0 -1
- package/dist/pointclouds.js.map +0 -1
- package/dist/protocol.js +0 -2
- package/dist/protocol.js.map +0 -1
- package/dist/requests.js.map +0 -1
- package/dist/resample.js.map +0 -1
- package/dist/style.js.map +0 -1
- package/dist/symbols.js.map +0 -1
- package/dist/terrain.js.map +0 -1
- package/dist/topojson.js.map +0 -1
- package/dist/vectorstyle.js.map +0 -1
- package/dist/vectortiles.js.map +0 -1
- package/dist/wmts.js.map +0 -1
- package/dist/xml.js.map +0 -1
- /package/dist/{mvt.d.ts → basemap/mvt.d.ts} +0 -0
- /package/dist/{mvt.js → basemap/mvt.js} +0 -0
- /package/dist/{terrain.d.ts → basemap/terrain.d.ts} +0 -0
- /package/dist/{vectorstyle.d.ts → basemap/vectorstyle.d.ts} +0 -0
- /package/dist/{wmts.d.ts → basemap/wmts.d.ts} +0 -0
- /package/dist/{crs.js → data/crs.js} +0 -0
- /package/dist/{gpx.js → data/gpx.js} +0 -0
- /package/dist/{kml.js → data/kml.js} +0 -0
- /package/dist/{topojson.js → data/topojson.js} +0 -0
- /package/dist/{xml.d.ts → data/xml.d.ts} +0 -0
- /package/dist/{xml.js → data/xml.js} +0 -0
- /package/dist/{decoders.js → engine/decoders.js} +0 -0
- /package/dist/{requests.d.ts → engine/requests.d.ts} +0 -0
- /package/dist/{requests.js → engine/requests.js} +0 -0
- /package/dist/{extrusions.d.ts → layers/extrusions.d.ts} +0 -0
- /package/dist/{extrusions.js → layers/extrusions.js} +0 -0
- /package/dist/{features.d.ts → layers/features.d.ts} +0 -0
- /package/dist/{overlay.js → ui/overlay.js} +0 -0
- /package/dist/{style.d.ts → ui/style.d.ts} +0 -0
- /package/dist/{style.js → ui/style.js} +0 -0
- /package/dist/{animation.js → util/animation.js} +0 -0
- /package/dist/{color.d.ts → util/color.d.ts} +0 -0
- /package/dist/{color.js → util/color.js} +0 -0
- /package/dist/{geo.d.ts → util/geo.d.ts} +0 -0
- /package/dist/{geo.js → util/geo.js} +0 -0
- /package/dist/{lru.d.ts → util/lru.d.ts} +0 -0
- /package/dist/{lru.js → util/lru.js} +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isard in React: the map, its layers, markers, popups, panels and effects as components, created when they mount,
|
|
3
|
+
* updated by the least their props need and removed when they unmount.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <IsardMap preset="ign-3d" center={[2.35, 48.85]} zoom={15} style={{ height: 400 }}>
|
|
7
|
+
* <Layer type="lines" data={routes} color="#0057b8" width={3} />
|
|
8
|
+
* <Marker lonLat={[2.35, 48.85]}><Popup>Here</Popup></Marker>
|
|
9
|
+
* </IsardMap>
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
13
|
+
import type { Layer as LayerHandle } from '../layers/layers.ts';
|
|
14
|
+
import { GeoMap, type LayerSpec } from '../map.ts';
|
|
15
|
+
import type { GeoMapOptions, LonLat, MapEvents, Point, SwipeOptions, ViewshedOptions } from '../types.ts';
|
|
16
|
+
import { type Anchor, Marker as MarkerHandle, Panel as PanelHandle, type PanelOptions, Popup as PopupHandle, type PopupOptions } from '../ui/overlay.ts';
|
|
17
|
+
import type { Camera } from '../wasm/isard.js';
|
|
18
|
+
/** The map of the `<IsardMap>` around, once created (undefined before) */
|
|
19
|
+
export declare function useMap(): GeoMap | undefined;
|
|
20
|
+
/** Listens to an event of the map around while the component is mounted; the listener may change at each render */
|
|
21
|
+
export declare function useMapEvent<K extends keyof MapEvents>(type: K, listener: (event: MapEvents[K]) => void): void;
|
|
22
|
+
/** The props of the map's events: `onClick` for 'click', `onMoveEnd` for 'moveend'… */
|
|
23
|
+
export interface MapEventProps {
|
|
24
|
+
onMoveStart?: (camera: Camera) => void;
|
|
25
|
+
onMove?: (camera: Camera) => void;
|
|
26
|
+
onMoveEnd?: (camera: Camera) => void;
|
|
27
|
+
onResize?: (event: MapEvents['resize']) => void;
|
|
28
|
+
onHover?: (event: MapEvents['hover']) => void;
|
|
29
|
+
onHoverFeature?: (event: MapEvents['hoverfeature']) => void;
|
|
30
|
+
onClick?: (event: MapEvents['click']) => void;
|
|
31
|
+
onRender?: (event: MapEvents['render']) => void;
|
|
32
|
+
onIdle?: () => void;
|
|
33
|
+
onLeave?: () => void;
|
|
34
|
+
}
|
|
35
|
+
export interface IsardMapProps extends Omit<GeoMapOptions, keyof Camera>, MapEventProps {
|
|
36
|
+
/** The camera at creation, then left to the user's gestures */
|
|
37
|
+
center?: LonLat;
|
|
38
|
+
zoom?: number;
|
|
39
|
+
pitch?: number;
|
|
40
|
+
bearing?: number;
|
|
41
|
+
/** A camera the map follows: set again whenever it changes (with `onMove` to keep it in a state) */
|
|
42
|
+
camera?: Partial<Camera>;
|
|
43
|
+
/** The map, once created */
|
|
44
|
+
onLoad?: (map: GeoMap) => void;
|
|
45
|
+
/** The map's creation failed (no WebGL2, the engine not found) */
|
|
46
|
+
onError?: (error: unknown) => void;
|
|
47
|
+
id?: string;
|
|
48
|
+
className?: string;
|
|
49
|
+
style?: CSSProperties;
|
|
50
|
+
/** Layers, markers, popups, panels, effects; drawn once the map is created */
|
|
51
|
+
children?: ReactNode;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A map filling its element (give it a height). Created once mounted, destroyed when unmounted; the options read at
|
|
55
|
+
* creation only (`body`, `preset`, `interactive`, the URLs) need a new `key` to take another value
|
|
56
|
+
*/
|
|
57
|
+
export declare const IsardMap: import("react").ForwardRefExoticComponent<IsardMapProps & import("react").RefAttributes<GeoMap | undefined>>;
|
|
58
|
+
export type LayerProps<P = Record<string, unknown>> = LayerSpec<P> & {
|
|
59
|
+
/** Its place among the layers of the components: from the bottom up, the same order by mounting (default 0) */
|
|
60
|
+
order?: number;
|
|
61
|
+
/** The layer, once created and each time it is made anew */
|
|
62
|
+
onLayer?: (layer: LayerHandle) => void;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* A layer: the options of its `add…` method with its `type` (`map.add`). A change of data, style or instances updates
|
|
66
|
+
* it; a change of what is set at creation (its URL, grid, nodes…) makes it anew, in its place. Data, functions and
|
|
67
|
+
* arrays are compared by identity: keep them in a memo or a state. The ref gives the layer's handle
|
|
68
|
+
*/
|
|
69
|
+
export declare const Layer: import("react").ForwardRefExoticComponent<LayerProps<Record<string, unknown>> & import("react").RefAttributes<LayerHandle | undefined>>;
|
|
70
|
+
export interface MarkerProps {
|
|
71
|
+
lonLat: LonLat;
|
|
72
|
+
/** Metres above the ground, its stem down to `base` (what it stands on: a roof, a mesh) */
|
|
73
|
+
height?: number;
|
|
74
|
+
base?: number;
|
|
75
|
+
draggable?: boolean;
|
|
76
|
+
offset?: Point;
|
|
77
|
+
/** Which part of the element sits on the position (default 'bottom' for the pin, 'center' with an icon) */
|
|
78
|
+
anchor?: Anchor;
|
|
79
|
+
className?: string;
|
|
80
|
+
/** The marker's look, in place of the default pin; the element changes with it */
|
|
81
|
+
icon?: ReactNode;
|
|
82
|
+
onDragStart?: (lonLat: LonLat) => void;
|
|
83
|
+
onDrag?: (lonLat: LonLat) => void;
|
|
84
|
+
onDragEnd?: (lonLat: LonLat) => void;
|
|
85
|
+
/** A `<Popup>` opened by a click on the marker */
|
|
86
|
+
children?: ReactNode;
|
|
87
|
+
}
|
|
88
|
+
/** A marker at a position, its look the default pin or the `icon` given */
|
|
89
|
+
export declare function Marker(props: MarkerProps): ReactNode;
|
|
90
|
+
export interface PopupProps extends PopupOptions {
|
|
91
|
+
/** Its position; in a `<Marker>`, the marker's */
|
|
92
|
+
lonLat?: LonLat;
|
|
93
|
+
/** Metres above the ground (in a `<Marker>`, the marker's) */
|
|
94
|
+
height?: number;
|
|
95
|
+
/** Open or closed, when the application decides; else open when mounted alone, on the marker's click in a marker */
|
|
96
|
+
open?: boolean;
|
|
97
|
+
onOpen?: () => void;
|
|
98
|
+
/** Closed by its button, a click on the map, or the marker's click */
|
|
99
|
+
onClose?: () => void;
|
|
100
|
+
children?: ReactNode;
|
|
101
|
+
}
|
|
102
|
+
/** A popup of JSX content, at a position or on a `<Marker>` */
|
|
103
|
+
export declare function Popup(props: PopupProps): ReactNode;
|
|
104
|
+
export interface PanelProps extends Omit<PanelOptions, 'element'> {
|
|
105
|
+
lonLat: LonLat;
|
|
106
|
+
/** What stands in 3D: a video, an iframe, a sign */
|
|
107
|
+
children?: ReactNode;
|
|
108
|
+
}
|
|
109
|
+
/** JSX standing in 3D at a position, `width` metres wide, facing `heading` */
|
|
110
|
+
export declare function Panel(props: PanelProps): ReactNode;
|
|
111
|
+
/** An observer's viewshed; several `<Viewshed>` are drawn together */
|
|
112
|
+
export declare function Viewshed(props: ViewshedOptions): ReactNode;
|
|
113
|
+
export interface SwipeProps extends Omit<SwipeOptions, 'layers'> {
|
|
114
|
+
/** The `<Layer>` shown on one side of the line only */
|
|
115
|
+
children?: ReactNode;
|
|
116
|
+
}
|
|
117
|
+
/** The `<Layer>` in it shown on one side of a vertical line only, at `position` CSS pixels from the left */
|
|
118
|
+
export declare function Swipe(props: SwipeProps): ReactNode;
|
|
119
|
+
export type { Camera, GeoMap, LayerHandle, LayerSpec, LonLat, MarkerHandle, PanelHandle, PopupHandle };
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isard in React: the map, its layers, markers, popups, panels and effects as components, created when they mount,
|
|
3
|
+
* updated by the least their props need and removed when they unmount.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <IsardMap preset="ign-3d" center={[2.35, 48.85]} zoom={15} style={{ height: 400 }}>
|
|
7
|
+
* <Layer type="lines" data={routes} color="#0057b8" width={3} />
|
|
8
|
+
* <Marker lonLat={[2.35, 48.85]}><Popup>Here</Popup></Marker>
|
|
9
|
+
* </IsardMap>
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import { createContext, createElement, forwardRef, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
13
|
+
import { createPortal } from 'react-dom';
|
|
14
|
+
import { applyLayerChange, LayerOrder, layerChange } from "../bind/layers.js";
|
|
15
|
+
import { GeoMap } from "../map.js";
|
|
16
|
+
import { Marker as MarkerHandle, Panel as PanelHandle, Popup as PopupHandle, } from "../ui/overlay.js";
|
|
17
|
+
const MapContext = createContext(undefined);
|
|
18
|
+
/** The map of the `<IsardMap>` around, once created (undefined before) */
|
|
19
|
+
export function useMap() {
|
|
20
|
+
return useContext(MapContext)?.map;
|
|
21
|
+
}
|
|
22
|
+
/** Listens to an event of the map around while the component is mounted; the listener may change at each render */
|
|
23
|
+
export function useMapEvent(type, listener) {
|
|
24
|
+
const map = useMap();
|
|
25
|
+
const latest = useRef(listener);
|
|
26
|
+
latest.current = listener;
|
|
27
|
+
useEffect(() => map?.on(type, (event) => latest.current(event)), [map, type]);
|
|
28
|
+
}
|
|
29
|
+
const EVENT_PROPS = {
|
|
30
|
+
onMoveStart: 'movestart',
|
|
31
|
+
onMove: 'move',
|
|
32
|
+
onMoveEnd: 'moveend',
|
|
33
|
+
onResize: 'resize',
|
|
34
|
+
onHover: 'hover',
|
|
35
|
+
onHoverFeature: 'hoverfeature',
|
|
36
|
+
onClick: 'click',
|
|
37
|
+
onRender: 'render',
|
|
38
|
+
onIdle: 'idle',
|
|
39
|
+
onLeave: 'leave',
|
|
40
|
+
};
|
|
41
|
+
/** Options the map takes again after its creation, and how */
|
|
42
|
+
const LIVE_OPTIONS = {
|
|
43
|
+
basemap: (map, p) => {
|
|
44
|
+
if (p.basemap)
|
|
45
|
+
map.setBasemap(p.basemap, p.hillshade);
|
|
46
|
+
},
|
|
47
|
+
hillshade: (map, p) => {
|
|
48
|
+
if (p.basemap)
|
|
49
|
+
map.setBasemap(p.basemap, p.hillshade);
|
|
50
|
+
},
|
|
51
|
+
terrain: (map, p) => map.setTerrain(p.terrain ?? false),
|
|
52
|
+
minZoom: (map, p) => map.setMinZoom(p.minZoom ?? 0),
|
|
53
|
+
maxZoom: (map, p) => map.setMaxZoom(p.maxZoom ?? 22),
|
|
54
|
+
maxPitch: (map, p) => map.setMaxPitch(p.maxPitch ?? 70),
|
|
55
|
+
atmosphere: (map, p) => map.setAtmosphere(p.atmosphere),
|
|
56
|
+
projection: (map, p) => map.setProjection(p.projection ?? 'globe'),
|
|
57
|
+
loading: (map, p) => map.setLoading(p.loading ?? 'immediate'),
|
|
58
|
+
sky: (map, p) => map.setSky(p.sky ?? true),
|
|
59
|
+
background: (map, p) => {
|
|
60
|
+
if (p.background)
|
|
61
|
+
map.setBackground(p.background);
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
function sameCamera(a, b) {
|
|
65
|
+
return ((b.center === undefined || (a.center[0] === b.center[0] && a.center[1] === b.center[1])) &&
|
|
66
|
+
(b.zoom === undefined || a.zoom === b.zoom) &&
|
|
67
|
+
(b.pitch === undefined || a.pitch === b.pitch) &&
|
|
68
|
+
(b.bearing === undefined || a.bearing === b.bearing));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A map filling its element (give it a height). Created once mounted, destroyed when unmounted; the options read at
|
|
72
|
+
* creation only (`body`, `preset`, `interactive`, the URLs) need a new `key` to take another value
|
|
73
|
+
*/
|
|
74
|
+
export const IsardMap = forwardRef(function IsardMap(props, ref) {
|
|
75
|
+
const element = useRef(null);
|
|
76
|
+
const [binding, setBinding] = useState();
|
|
77
|
+
const latest = useRef(props);
|
|
78
|
+
latest.current = props;
|
|
79
|
+
useImperativeHandle(ref, () => binding?.map, [binding]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const container = element.current;
|
|
82
|
+
if (!container)
|
|
83
|
+
return;
|
|
84
|
+
let cancelled = false;
|
|
85
|
+
let created;
|
|
86
|
+
const { camera, center, zoom, pitch, bearing, children: _, ...options } = latest.current;
|
|
87
|
+
GeoMap.create(container, {
|
|
88
|
+
...stripProps(options),
|
|
89
|
+
...defined({ center, zoom, pitch, bearing }),
|
|
90
|
+
...camera,
|
|
91
|
+
}).then((map) => {
|
|
92
|
+
// Unmounted while the engine was loading (or StrictMode's second mount): that map is not wanted
|
|
93
|
+
if (cancelled) {
|
|
94
|
+
map.destroy();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
created = map;
|
|
98
|
+
const viewsheds = new Map();
|
|
99
|
+
setBinding({
|
|
100
|
+
map,
|
|
101
|
+
order: new LayerOrder(),
|
|
102
|
+
viewsheds,
|
|
103
|
+
applyViewsheds: () => map.setViewshed(viewsheds.size > 0 ? [...viewsheds.values()] : undefined),
|
|
104
|
+
});
|
|
105
|
+
latest.current.onLoad?.(map);
|
|
106
|
+
}, (error) => {
|
|
107
|
+
if (cancelled)
|
|
108
|
+
return;
|
|
109
|
+
if (latest.current.onError)
|
|
110
|
+
latest.current.onError(error);
|
|
111
|
+
else
|
|
112
|
+
console.error(error);
|
|
113
|
+
});
|
|
114
|
+
return () => {
|
|
115
|
+
cancelled = true;
|
|
116
|
+
created?.destroy();
|
|
117
|
+
setBinding(undefined);
|
|
118
|
+
};
|
|
119
|
+
}, []);
|
|
120
|
+
// The events, each listened to once: the props may give another function at each render
|
|
121
|
+
const map = binding?.map;
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!map)
|
|
124
|
+
return;
|
|
125
|
+
const offs = Object.entries(EVENT_PROPS).map(([prop, type]) => map.on(type, (event) => latest.current[prop]?.(event)));
|
|
126
|
+
return () => {
|
|
127
|
+
for (const off of offs)
|
|
128
|
+
off();
|
|
129
|
+
};
|
|
130
|
+
}, [map]);
|
|
131
|
+
// The options taken again, compared with what the map was given last
|
|
132
|
+
const applied = useRef(props);
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (!map)
|
|
135
|
+
return;
|
|
136
|
+
const last = applied.current;
|
|
137
|
+
applied.current = props;
|
|
138
|
+
const done = new Set();
|
|
139
|
+
for (const [key, apply] of Object.entries(LIVE_OPTIONS)) {
|
|
140
|
+
if (!apply || done.has(apply) || Object.is(last[key], props[key])) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
done.add(apply);
|
|
144
|
+
apply(map, props);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
const { camera } = props;
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (map && camera && !sameCamera(map.getCamera(), camera))
|
|
150
|
+
map.setCamera(camera);
|
|
151
|
+
}, [map, camera]);
|
|
152
|
+
return createElement('div', { ref: element, id: props.id, className: props.className, style: props.style }, binding ? createElement(MapContext.Provider, { value: binding }, props.children) : null);
|
|
153
|
+
});
|
|
154
|
+
/** The props that are not options of the map */
|
|
155
|
+
function stripProps(props) {
|
|
156
|
+
const options = { ...props };
|
|
157
|
+
for (const key of [...Object.keys(EVENT_PROPS), 'onLoad', 'onError', 'id', 'className', 'style'])
|
|
158
|
+
delete options[key];
|
|
159
|
+
return options;
|
|
160
|
+
}
|
|
161
|
+
/** The entries of an object whose value is defined (`exactOptionalPropertyTypes`) */
|
|
162
|
+
function defined(object) {
|
|
163
|
+
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined));
|
|
164
|
+
}
|
|
165
|
+
// ── Layers ──
|
|
166
|
+
const SwipeContext = createContext(undefined);
|
|
167
|
+
/**
|
|
168
|
+
* A layer: the options of its `add…` method with its `type` (`map.add`). A change of data, style or instances updates
|
|
169
|
+
* it; a change of what is set at creation (its URL, grid, nodes…) makes it anew, in its place. Data, functions and
|
|
170
|
+
* arrays are compared by identity: keep them in a memo or a state. The ref gives the layer's handle
|
|
171
|
+
*/
|
|
172
|
+
export const Layer = forwardRef(function Layer(props, ref) {
|
|
173
|
+
const binding = useContext(MapContext);
|
|
174
|
+
const swipe = useContext(SwipeContext);
|
|
175
|
+
const { order = 0, onLayer, ...spec } = props;
|
|
176
|
+
const state = useRef(undefined);
|
|
177
|
+
const [layer, setLayer] = useState();
|
|
178
|
+
useImperativeHandle(ref, () => layer, [layer]);
|
|
179
|
+
const latest = useRef({ spec, order, onLayer });
|
|
180
|
+
latest.current = { spec, order, onLayer };
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!binding)
|
|
183
|
+
return;
|
|
184
|
+
const { spec, order, onLayer } = latest.current;
|
|
185
|
+
const created = binding.map.add(spec);
|
|
186
|
+
binding.order.place(created, order);
|
|
187
|
+
state.current = { layer: created, spec, order };
|
|
188
|
+
swipe?.set(created);
|
|
189
|
+
setLayer(created);
|
|
190
|
+
onLayer?.(created);
|
|
191
|
+
return () => {
|
|
192
|
+
const current = state.current;
|
|
193
|
+
state.current = undefined;
|
|
194
|
+
if (!current)
|
|
195
|
+
return;
|
|
196
|
+
binding.order.remove(current.layer);
|
|
197
|
+
swipe?.unset(current.layer);
|
|
198
|
+
if (binding.map.hasLayer(current.layer))
|
|
199
|
+
current.layer.remove();
|
|
200
|
+
};
|
|
201
|
+
}, [binding, swipe]);
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
const current = state.current;
|
|
204
|
+
if (!binding || !current)
|
|
205
|
+
return;
|
|
206
|
+
const next = spec;
|
|
207
|
+
const change = layerChange(current.spec, next);
|
|
208
|
+
current.spec = next;
|
|
209
|
+
if (change?.rebuild) {
|
|
210
|
+
const old = current.layer;
|
|
211
|
+
const fresh = binding.map.rebuildLayer(old, next);
|
|
212
|
+
binding.order.place(fresh, order, old);
|
|
213
|
+
swipe?.unset(old);
|
|
214
|
+
swipe?.set(fresh);
|
|
215
|
+
current.layer = fresh;
|
|
216
|
+
current.order = order;
|
|
217
|
+
setLayer(fresh);
|
|
218
|
+
latest.current.onLayer?.(fresh);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (change)
|
|
222
|
+
applyLayerChange(current.layer, next, change);
|
|
223
|
+
if (current.order !== order) {
|
|
224
|
+
current.order = order;
|
|
225
|
+
binding.order.place(current.layer, order, current.layer);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
return null;
|
|
229
|
+
});
|
|
230
|
+
// ── Markers, popups, panels ──
|
|
231
|
+
/** The marker of a `<Marker>` around (a `<Popup>` in it opens on its click) */
|
|
232
|
+
const MarkerContext = createContext(undefined);
|
|
233
|
+
/** A marker at a position, its look the default pin or the `icon` given */
|
|
234
|
+
export function Marker(props) {
|
|
235
|
+
const map = useMap();
|
|
236
|
+
const custom = props.icon !== undefined;
|
|
237
|
+
const { anchor, className } = props;
|
|
238
|
+
const element = useMemo(() => (custom ? document.createElement('div') : undefined), [custom]);
|
|
239
|
+
const marker = useMemo(() => new MarkerHandle({ ...defined({ anchor, className, element }) }), [anchor, className, element]);
|
|
240
|
+
const latest = useRef(props);
|
|
241
|
+
latest.current = props;
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
if (!map)
|
|
244
|
+
return;
|
|
245
|
+
marker.addTo(map);
|
|
246
|
+
const offs = [
|
|
247
|
+
marker.on('dragstart', (e) => latest.current.onDragStart?.(e.lonLat)),
|
|
248
|
+
marker.on('drag', (e) => latest.current.onDrag?.(e.lonLat)),
|
|
249
|
+
marker.on('dragend', (e) => latest.current.onDragEnd?.(e.lonLat)),
|
|
250
|
+
];
|
|
251
|
+
return () => {
|
|
252
|
+
for (const off of offs)
|
|
253
|
+
off();
|
|
254
|
+
marker.remove();
|
|
255
|
+
};
|
|
256
|
+
}, [map, marker]);
|
|
257
|
+
const { lonLat, height = 0, base = 0, draggable = false, offset } = props;
|
|
258
|
+
useLayoutEffect(() => {
|
|
259
|
+
marker.setLonLat(lonLat);
|
|
260
|
+
}, [marker, lonLat[0], lonLat[1]]);
|
|
261
|
+
useLayoutEffect(() => {
|
|
262
|
+
marker.setHeight(height, base);
|
|
263
|
+
}, [marker, height, base]);
|
|
264
|
+
useLayoutEffect(() => {
|
|
265
|
+
marker.setDraggable(draggable);
|
|
266
|
+
}, [marker, draggable]);
|
|
267
|
+
useLayoutEffect(() => {
|
|
268
|
+
if (offset)
|
|
269
|
+
marker.setOffset(offset);
|
|
270
|
+
}, [marker, offset?.[0], offset?.[1]]);
|
|
271
|
+
return createElement(MarkerContext.Provider, { value: marker }, element ? createPortal(props.icon, element) : null, props.children);
|
|
272
|
+
}
|
|
273
|
+
/** A popup of JSX content, at a position or on a `<Marker>` */
|
|
274
|
+
export function Popup(props) {
|
|
275
|
+
const map = useMap();
|
|
276
|
+
const marker = useContext(MarkerContext);
|
|
277
|
+
const { anchor, closeButton, closeOnClick, maxWidth, className } = props;
|
|
278
|
+
const content = useMemo(() => document.createElement('div'), []);
|
|
279
|
+
const popup = useMemo(() => new PopupHandle(defined({ anchor, closeButton, closeOnClick, maxWidth, className })).setDOMContent(content), [anchor, closeButton, closeOnClick, maxWidth, className, content]);
|
|
280
|
+
const latest = useRef(props);
|
|
281
|
+
latest.current = props;
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
if (!map)
|
|
284
|
+
return;
|
|
285
|
+
const offs = [
|
|
286
|
+
popup.on('open', () => latest.current.onOpen?.()),
|
|
287
|
+
popup.on('close', () => latest.current.onClose?.()),
|
|
288
|
+
];
|
|
289
|
+
if (marker)
|
|
290
|
+
marker.setPopup(popup);
|
|
291
|
+
return () => {
|
|
292
|
+
for (const off of offs)
|
|
293
|
+
off();
|
|
294
|
+
if (marker?.getPopup() === popup)
|
|
295
|
+
marker.setPopup(undefined);
|
|
296
|
+
popup.remove();
|
|
297
|
+
};
|
|
298
|
+
}, [map, marker, popup]);
|
|
299
|
+
const { lonLat, height, open, offset } = props;
|
|
300
|
+
const opened = open ?? !marker;
|
|
301
|
+
useLayoutEffect(() => {
|
|
302
|
+
if (offset)
|
|
303
|
+
popup.setOffset(offset);
|
|
304
|
+
}, [popup, offset?.[0], offset?.[1]]);
|
|
305
|
+
useLayoutEffect(() => {
|
|
306
|
+
if (!map)
|
|
307
|
+
return;
|
|
308
|
+
if (lonLat)
|
|
309
|
+
popup.setLonLat(lonLat);
|
|
310
|
+
else if (marker)
|
|
311
|
+
popup.setLonLat(marker.getLonLat());
|
|
312
|
+
if (height !== undefined)
|
|
313
|
+
popup.setHeight(height);
|
|
314
|
+
if (open === undefined && marker)
|
|
315
|
+
return;
|
|
316
|
+
if (opened && !popup.isOpen())
|
|
317
|
+
popup.addTo(map);
|
|
318
|
+
if (!opened && popup.isOpen())
|
|
319
|
+
popup.remove();
|
|
320
|
+
}, [map, marker, popup, lonLat?.[0], lonLat?.[1], height, open, opened]);
|
|
321
|
+
return createPortal(props.children, content);
|
|
322
|
+
}
|
|
323
|
+
/** JSX standing in 3D at a position, `width` metres wide, facing `heading` */
|
|
324
|
+
export function Panel(props) {
|
|
325
|
+
const map = useMap();
|
|
326
|
+
const { className } = props;
|
|
327
|
+
const element = useMemo(() => document.createElement('div'), []);
|
|
328
|
+
// The width at creation: the placement is set again by the effect below
|
|
329
|
+
const initialWidth = useRef(props.width);
|
|
330
|
+
const panel = useMemo(() => new PanelHandle({ element, width: initialWidth.current, ...defined({ className }) }), [element, className]);
|
|
331
|
+
useEffect(() => {
|
|
332
|
+
if (!map)
|
|
333
|
+
return;
|
|
334
|
+
panel.addTo(map);
|
|
335
|
+
return () => {
|
|
336
|
+
panel.remove();
|
|
337
|
+
};
|
|
338
|
+
}, [map, panel]);
|
|
339
|
+
const { lonLat, width, heading, pitch, height } = props;
|
|
340
|
+
useLayoutEffect(() => {
|
|
341
|
+
panel.setLonLat(lonLat);
|
|
342
|
+
}, [panel, lonLat[0], lonLat[1]]);
|
|
343
|
+
useLayoutEffect(() => {
|
|
344
|
+
panel.setPlacement(defined({ width, heading, pitch, height }));
|
|
345
|
+
}, [panel, width, heading, pitch, height]);
|
|
346
|
+
// Its content may change its CSS size
|
|
347
|
+
useLayoutEffect(() => {
|
|
348
|
+
panel.resize();
|
|
349
|
+
});
|
|
350
|
+
return createPortal(props.children, element);
|
|
351
|
+
}
|
|
352
|
+
// ── Effects ──
|
|
353
|
+
/** An observer's viewshed; several `<Viewshed>` are drawn together */
|
|
354
|
+
export function Viewshed(props) {
|
|
355
|
+
const binding = useContext(MapContext);
|
|
356
|
+
const key = useMemo(() => ({}), []);
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
if (!binding)
|
|
359
|
+
return;
|
|
360
|
+
return () => {
|
|
361
|
+
binding.viewsheds.delete(key);
|
|
362
|
+
binding.applyViewsheds();
|
|
363
|
+
};
|
|
364
|
+
}, [binding, key]);
|
|
365
|
+
// Numbers, strings and the position: compared by value
|
|
366
|
+
const options = JSON.stringify(props);
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
if (!binding)
|
|
369
|
+
return;
|
|
370
|
+
binding.viewsheds.set(key, JSON.parse(options));
|
|
371
|
+
binding.applyViewsheds();
|
|
372
|
+
}, [binding, key, options]);
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
/** The `<Layer>` in it shown on one side of a vertical line only, at `position` CSS pixels from the left */
|
|
376
|
+
export function Swipe(props) {
|
|
377
|
+
const map = useMap();
|
|
378
|
+
const layers = useMemo(() => new Set(), []);
|
|
379
|
+
const latest = useRef(props);
|
|
380
|
+
latest.current = props;
|
|
381
|
+
const group = useMemo(() => {
|
|
382
|
+
if (!map)
|
|
383
|
+
return undefined;
|
|
384
|
+
const apply = () => {
|
|
385
|
+
const { position, side } = latest.current;
|
|
386
|
+
map.setSwipe(layers.size > 0 ? { position, layers: [...layers], ...defined({ side }) } : undefined);
|
|
387
|
+
};
|
|
388
|
+
return {
|
|
389
|
+
set: (layer) => {
|
|
390
|
+
layers.add(layer);
|
|
391
|
+
apply();
|
|
392
|
+
},
|
|
393
|
+
unset: (layer) => {
|
|
394
|
+
layers.delete(layer);
|
|
395
|
+
apply();
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
}, [map, layers]);
|
|
399
|
+
const { position, side } = props;
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
if (map && layers.size > 0)
|
|
402
|
+
map.setSwipe({ position, layers: [...layers], ...defined({ side }) });
|
|
403
|
+
}, [map, layers, position, side]);
|
|
404
|
+
useEffect(() => () => {
|
|
405
|
+
map?.setSwipe(undefined);
|
|
406
|
+
}, [map]);
|
|
407
|
+
return createElement(SwipeContext.Provider, { value: group }, props.children);
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Isard in React: the map, its layers, markers, popups, panels and effects as components, created when they mount,\n * updated by the least their props need and removed when they unmount.\n *\n * ```tsx\n * <IsardMap preset=\"ign-3d\" center={[2.35, 48.85]} zoom={15} style={{ height: 400 }}>\n * <Layer type=\"lines\" data={routes} color=\"#0057b8\" width={3} />\n * <Marker lonLat={[2.35, 48.85]}><Popup>Here</Popup></Marker>\n * </IsardMap>\n * ```\n */\n\nimport {\n type CSSProperties,\n createContext,\n createElement,\n forwardRef,\n type ReactNode,\n useContext,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { applyLayerChange, LayerOrder, layerChange } from '../bind/layers.ts';\nimport type { Layer as LayerHandle } from '../layers/layers.ts';\nimport { GeoMap, type LayerSpec } from '../map.ts';\nimport type { GeoMapOptions, LonLat, MapEvents, Point, SwipeOptions, ViewshedOptions } from '../types.ts';\nimport {\n type Anchor,\n Marker as MarkerHandle,\n Panel as PanelHandle,\n type PanelOptions,\n Popup as PopupHandle,\n type PopupOptions,\n} from '../ui/overlay.ts';\nimport type { Camera } from '../wasm/isard.js';\n\n// ── The map ──\n\ninterface Binding {\n map: GeoMap;\n order: LayerOrder;\n /** The observers of the `<Viewshed>` components, set on the map together */\n viewsheds: Map<object, ViewshedOptions>;\n applyViewsheds(): void;\n}\n\nconst MapContext = createContext<Binding | undefined>(undefined);\n\n/** The map of the `<IsardMap>` around, once created (undefined before) */\nexport function useMap(): GeoMap | undefined {\n return useContext(MapContext)?.map;\n}\n\n/** Listens to an event of the map around while the component is mounted; the listener may change at each render */\nexport function useMapEvent<K extends keyof MapEvents>(type: K, listener: (event: MapEvents[K]) => void): void {\n const map = useMap();\n const latest = useRef(listener);\n latest.current = listener;\n useEffect(() => map?.on(type, (event) => latest.current(event)), [map, type]);\n}\n\n/** The props of the map's events: `onClick` for 'click', `onMoveEnd` for 'moveend'… */\nexport interface MapEventProps {\n onMoveStart?: (camera: Camera) => void;\n onMove?: (camera: Camera) => void;\n onMoveEnd?: (camera: Camera) => void;\n onResize?: (event: MapEvents['resize']) => void;\n onHover?: (event: MapEvents['hover']) => void;\n onHoverFeature?: (event: MapEvents['hoverfeature']) => void;\n onClick?: (event: MapEvents['click']) => void;\n onRender?: (event: MapEvents['render']) => void;\n onIdle?: () => void;\n onLeave?: () => void;\n}\n\nconst EVENT_PROPS = {\n onMoveStart: 'movestart',\n onMove: 'move',\n onMoveEnd: 'moveend',\n onResize: 'resize',\n onHover: 'hover',\n onHoverFeature: 'hoverfeature',\n onClick: 'click',\n onRender: 'render',\n onIdle: 'idle',\n onLeave: 'leave',\n} as const satisfies Record<keyof MapEventProps, keyof MapEvents>;\n\nexport interface IsardMapProps extends Omit<GeoMapOptions, keyof Camera>, MapEventProps {\n /** The camera at creation, then left to the user's gestures */\n center?: LonLat;\n zoom?: number;\n pitch?: number;\n bearing?: number;\n /** A camera the map follows: set again whenever it changes (with `onMove` to keep it in a state) */\n camera?: Partial<Camera>;\n /** The map, once created */\n onLoad?: (map: GeoMap) => void;\n /** The map's creation failed (no WebGL2, the engine not found) */\n onError?: (error: unknown) => void;\n id?: string;\n className?: string;\n style?: CSSProperties;\n /** Layers, markers, popups, panels, effects; drawn once the map is created */\n children?: ReactNode;\n}\n\n/** Options the map takes again after its creation, and how */\nconst LIVE_OPTIONS: { [K in keyof GeoMapOptions]?: (map: GeoMap, props: IsardMapProps) => void } = {\n basemap: (map, p) => {\n if (p.basemap) map.setBasemap(p.basemap, p.hillshade);\n },\n hillshade: (map, p) => {\n if (p.basemap) map.setBasemap(p.basemap, p.hillshade);\n },\n terrain: (map, p) => map.setTerrain(p.terrain ?? false),\n minZoom: (map, p) => map.setMinZoom(p.minZoom ?? 0),\n maxZoom: (map, p) => map.setMaxZoom(p.maxZoom ?? 22),\n maxPitch: (map, p) => map.setMaxPitch(p.maxPitch ?? 70),\n atmosphere: (map, p) => map.setAtmosphere(p.atmosphere),\n projection: (map, p) => map.setProjection(p.projection ?? 'globe'),\n loading: (map, p) => map.setLoading(p.loading ?? 'immediate'),\n sky: (map, p) => map.setSky(p.sky ?? true),\n background: (map, p) => {\n if (p.background) map.setBackground(p.background);\n },\n};\n\nfunction sameCamera(a: Camera, b: Partial<Camera>): boolean {\n return (\n (b.center === undefined || (a.center[0] === b.center[0] && a.center[1] === b.center[1])) &&\n (b.zoom === undefined || a.zoom === b.zoom) &&\n (b.pitch === undefined || a.pitch === b.pitch) &&\n (b.bearing === undefined || a.bearing === b.bearing)\n );\n}\n\n/**\n * A map filling its element (give it a height). Created once mounted, destroyed when unmounted; the options read at\n * creation only (`body`, `preset`, `interactive`, the URLs) need a new `key` to take another value\n */\nexport const IsardMap = forwardRef<GeoMap | undefined, IsardMapProps>(function IsardMap(props, ref) {\n const element = useRef<HTMLDivElement>(null);\n const [binding, setBinding] = useState<Binding>();\n const latest = useRef(props);\n latest.current = props;\n useImperativeHandle(ref, () => binding?.map, [binding]);\n\n useEffect(() => {\n const container = element.current;\n if (!container) return;\n let cancelled = false;\n let created: GeoMap | undefined;\n const { camera, center, zoom, pitch, bearing, children: _, ...options } = latest.current;\n GeoMap.create(container, {\n ...stripProps(options),\n ...defined({ center, zoom, pitch, bearing }),\n ...camera,\n }).then(\n (map) => {\n // Unmounted while the engine was loading (or StrictMode's second mount): that map is not wanted\n if (cancelled) {\n map.destroy();\n return;\n }\n created = map;\n const viewsheds = new Map<object, ViewshedOptions>();\n setBinding({\n map,\n order: new LayerOrder(),\n viewsheds,\n applyViewsheds: () => map.setViewshed(viewsheds.size > 0 ? [...viewsheds.values()] : undefined),\n });\n latest.current.onLoad?.(map);\n },\n (error: unknown) => {\n if (cancelled) return;\n if (latest.current.onError) latest.current.onError(error);\n else console.error(error);\n },\n );\n return () => {\n cancelled = true;\n created?.destroy();\n setBinding(undefined);\n };\n }, []);\n\n // The events, each listened to once: the props may give another function at each render\n const map = binding?.map;\n useEffect(() => {\n if (!map) return;\n const offs = Object.entries(EVENT_PROPS).map(([prop, type]) =>\n map.on(type, (event) =>\n (latest.current[prop as keyof MapEventProps] as ((e: unknown) => void) | undefined)?.(event),\n ),\n );\n return () => {\n for (const off of offs) off();\n };\n }, [map]);\n\n // The options taken again, compared with what the map was given last\n const applied = useRef<IsardMapProps>(props);\n useEffect(() => {\n if (!map) return;\n const last = applied.current;\n applied.current = props;\n const done = new Set<(map: GeoMap, props: IsardMapProps) => void>();\n for (const [key, apply] of Object.entries(LIVE_OPTIONS)) {\n if (!apply || done.has(apply) || Object.is(last[key as keyof IsardMapProps], props[key as keyof IsardMapProps])) {\n continue;\n }\n done.add(apply);\n apply(map, props);\n }\n });\n\n const { camera } = props;\n useEffect(() => {\n if (map && camera && !sameCamera(map.getCamera(), camera)) map.setCamera(camera);\n }, [map, camera]);\n\n return createElement(\n 'div',\n { ref: element, id: props.id, className: props.className, style: props.style },\n binding ? createElement(MapContext.Provider, { value: binding }, props.children) : null,\n );\n});\n\n/** The props that are not options of the map */\nfunction stripProps(props: Partial<IsardMapProps>): GeoMapOptions {\n const options: Record<string, unknown> = { ...props };\n for (const key of [...Object.keys(EVENT_PROPS), 'onLoad', 'onError', 'id', 'className', 'style']) delete options[key];\n return options as GeoMapOptions;\n}\n\n/** The entries of an object whose value is defined (`exactOptionalPropertyTypes`) */\nfunction defined<T extends object>(object: T): { [K in keyof T]?: Exclude<T[K], undefined> } {\n return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined)) as {\n [K in keyof T]?: Exclude<T[K], undefined>;\n };\n}\n\n// ── Layers ──\n\nconst SwipeContext = createContext<SwipeGroup | undefined>(undefined);\n\nexport type LayerProps<P = Record<string, unknown>> = LayerSpec<P> & {\n /** Its place among the layers of the components: from the bottom up, the same order by mounting (default 0) */\n order?: number;\n /** The layer, once created and each time it is made anew */\n onLayer?: (layer: LayerHandle) => void;\n};\n\n/**\n * A layer: the options of its `add…` method with its `type` (`map.add`). A change of data, style or instances updates\n * it; a change of what is set at creation (its URL, grid, nodes…) makes it anew, in its place. Data, functions and\n * arrays are compared by identity: keep them in a memo or a state. The ref gives the layer's handle\n */\nexport const Layer = forwardRef<LayerHandle | undefined, LayerProps>(function Layer(props, ref) {\n const binding = useContext(MapContext);\n const swipe = useContext(SwipeContext);\n const { order = 0, onLayer, ...spec } = props;\n const state = useRef<{ layer: LayerHandle; spec: LayerSpec; order: number }>(undefined);\n const [layer, setLayer] = useState<LayerHandle>();\n useImperativeHandle(ref, () => layer, [layer]);\n const latest = useRef({ spec, order, onLayer });\n latest.current = { spec, order, onLayer };\n\n useEffect(() => {\n if (!binding) return;\n const { spec, order, onLayer } = latest.current;\n const created = binding.map.add(spec as LayerSpec);\n binding.order.place(created, order);\n state.current = { layer: created, spec, order };\n swipe?.set(created);\n setLayer(created);\n onLayer?.(created);\n return () => {\n const current = state.current;\n state.current = undefined;\n if (!current) return;\n binding.order.remove(current.layer);\n swipe?.unset(current.layer);\n if (binding.map.hasLayer(current.layer)) current.layer.remove();\n };\n }, [binding, swipe]);\n\n useEffect(() => {\n const current = state.current;\n if (!binding || !current) return;\n const next = spec as LayerSpec;\n const change = layerChange(current.spec, next);\n current.spec = next;\n if (change?.rebuild) {\n const old = current.layer;\n const fresh = binding.map.rebuildLayer(old, next);\n binding.order.place(fresh, order, old);\n swipe?.unset(old);\n swipe?.set(fresh);\n current.layer = fresh;\n current.order = order;\n setLayer(fresh);\n latest.current.onLayer?.(fresh);\n return;\n }\n if (change) applyLayerChange(current.layer, next, change);\n if (current.order !== order) {\n current.order = order;\n binding.order.place(current.layer, order, current.layer);\n }\n });\n\n return null;\n});\n\n// ── Markers, popups, panels ──\n\n/** The marker of a `<Marker>` around (a `<Popup>` in it opens on its click) */\nconst MarkerContext = createContext<MarkerHandle | undefined>(undefined);\n\nexport interface MarkerProps {\n lonLat: LonLat;\n /** Metres above the ground, its stem down to `base` (what it stands on: a roof, a mesh) */\n height?: number;\n base?: number;\n draggable?: boolean;\n offset?: Point;\n /** Which part of the element sits on the position (default 'bottom' for the pin, 'center' with an icon) */\n anchor?: Anchor;\n className?: string;\n /** The marker's look, in place of the default pin; the element changes with it */\n icon?: ReactNode;\n onDragStart?: (lonLat: LonLat) => void;\n onDrag?: (lonLat: LonLat) => void;\n onDragEnd?: (lonLat: LonLat) => void;\n /** A `<Popup>` opened by a click on the marker */\n children?: ReactNode;\n}\n\n/** A marker at a position, its look the default pin or the `icon` given */\nexport function Marker(props: MarkerProps): ReactNode {\n const map = useMap();\n const custom = props.icon !== undefined;\n const { anchor, className } = props;\n const element = useMemo(() => (custom ? document.createElement('div') : undefined), [custom]);\n const marker = useMemo(\n () => new MarkerHandle({ ...defined({ anchor, className, element }) }),\n [anchor, className, element],\n );\n const latest = useRef(props);\n latest.current = props;\n\n useEffect(() => {\n if (!map) return;\n marker.addTo(map);\n const offs = [\n marker.on('dragstart', (e) => latest.current.onDragStart?.(e.lonLat)),\n marker.on('drag', (e) => latest.current.onDrag?.(e.lonLat)),\n marker.on('dragend', (e) => latest.current.onDragEnd?.(e.lonLat)),\n ];\n return () => {\n for (const off of offs) off();\n marker.remove();\n };\n }, [map, marker]);\n\n const { lonLat, height = 0, base = 0, draggable = false, offset } = props;\n useLayoutEffect(() => {\n marker.setLonLat(lonLat);\n }, [marker, lonLat[0], lonLat[1]]);\n useLayoutEffect(() => {\n marker.setHeight(height, base);\n }, [marker, height, base]);\n useLayoutEffect(() => {\n marker.setDraggable(draggable);\n }, [marker, draggable]);\n useLayoutEffect(() => {\n if (offset) marker.setOffset(offset);\n }, [marker, offset?.[0], offset?.[1]]);\n\n return createElement(\n MarkerContext.Provider,\n { value: marker },\n element ? createPortal(props.icon, element) : null,\n props.children,\n );\n}\n\nexport interface PopupProps extends PopupOptions {\n /** Its position; in a `<Marker>`, the marker's */\n lonLat?: LonLat;\n /** Metres above the ground (in a `<Marker>`, the marker's) */\n height?: number;\n /** Open or closed, when the application decides; else open when mounted alone, on the marker's click in a marker */\n open?: boolean;\n onOpen?: () => void;\n /** Closed by its button, a click on the map, or the marker's click */\n onClose?: () => void;\n children?: ReactNode;\n}\n\n/** A popup of JSX content, at a position or on a `<Marker>` */\nexport function Popup(props: PopupProps): ReactNode {\n const map = useMap();\n const marker = useContext(MarkerContext);\n const { anchor, closeButton, closeOnClick, maxWidth, className } = props;\n const content = useMemo(() => document.createElement('div'), []);\n const popup = useMemo(\n () => new PopupHandle(defined({ anchor, closeButton, closeOnClick, maxWidth, className })).setDOMContent(content),\n [anchor, closeButton, closeOnClick, maxWidth, className, content],\n );\n const latest = useRef(props);\n latest.current = props;\n\n useEffect(() => {\n if (!map) return;\n const offs = [\n popup.on('open', () => latest.current.onOpen?.()),\n popup.on('close', () => latest.current.onClose?.()),\n ];\n if (marker) marker.setPopup(popup);\n return () => {\n for (const off of offs) off();\n if (marker?.getPopup() === popup) marker.setPopup(undefined);\n popup.remove();\n };\n }, [map, marker, popup]);\n\n const { lonLat, height, open, offset } = props;\n const opened = open ?? !marker;\n useLayoutEffect(() => {\n if (offset) popup.setOffset(offset);\n }, [popup, offset?.[0], offset?.[1]]);\n useLayoutEffect(() => {\n if (!map) return;\n if (lonLat) popup.setLonLat(lonLat);\n else if (marker) popup.setLonLat(marker.getLonLat());\n if (height !== undefined) popup.setHeight(height);\n if (open === undefined && marker) return;\n if (opened && !popup.isOpen()) popup.addTo(map);\n if (!opened && popup.isOpen()) popup.remove();\n }, [map, marker, popup, lonLat?.[0], lonLat?.[1], height, open, opened]);\n\n return createPortal(props.children, content);\n}\n\nexport interface PanelProps extends Omit<PanelOptions, 'element'> {\n lonLat: LonLat;\n /** What stands in 3D: a video, an iframe, a sign */\n children?: ReactNode;\n}\n\n/** JSX standing in 3D at a position, `width` metres wide, facing `heading` */\nexport function Panel(props: PanelProps): ReactNode {\n const map = useMap();\n const { className } = props;\n const element = useMemo(() => document.createElement('div'), []);\n // The width at creation: the placement is set again by the effect below\n const initialWidth = useRef(props.width);\n const panel = useMemo(\n () => new PanelHandle({ element, width: initialWidth.current, ...defined({ className }) }),\n [element, className],\n );\n\n useEffect(() => {\n if (!map) return;\n panel.addTo(map);\n return () => {\n panel.remove();\n };\n }, [map, panel]);\n\n const { lonLat, width, heading, pitch, height } = props;\n useLayoutEffect(() => {\n panel.setLonLat(lonLat);\n }, [panel, lonLat[0], lonLat[1]]);\n useLayoutEffect(() => {\n panel.setPlacement(defined({ width, heading, pitch, height }));\n }, [panel, width, heading, pitch, height]);\n // Its content may change its CSS size\n useLayoutEffect(() => {\n panel.resize();\n });\n\n return createPortal(props.children, element);\n}\n\n// ── Effects ──\n\n/** An observer's viewshed; several `<Viewshed>` are drawn together */\nexport function Viewshed(props: ViewshedOptions): ReactNode {\n const binding = useContext(MapContext);\n const key = useMemo(() => ({}), []);\n useEffect(() => {\n if (!binding) return;\n return () => {\n binding.viewsheds.delete(key);\n binding.applyViewsheds();\n };\n }, [binding, key]);\n // Numbers, strings and the position: compared by value\n const options = JSON.stringify(props);\n useEffect(() => {\n if (!binding) return;\n binding.viewsheds.set(key, JSON.parse(options) as ViewshedOptions);\n binding.applyViewsheds();\n }, [binding, key, options]);\n return null;\n}\n\ninterface SwipeGroup {\n set(layer: LayerHandle): void;\n unset(layer: LayerHandle): void;\n}\n\nexport interface SwipeProps extends Omit<SwipeOptions, 'layers'> {\n /** The `<Layer>` shown on one side of the line only */\n children?: ReactNode;\n}\n\n/** The `<Layer>` in it shown on one side of a vertical line only, at `position` CSS pixels from the left */\nexport function Swipe(props: SwipeProps): ReactNode {\n const map = useMap();\n const layers = useMemo(() => new Set<LayerHandle>(), []);\n const latest = useRef(props);\n latest.current = props;\n const group = useMemo<SwipeGroup | undefined>(() => {\n if (!map) return undefined;\n const apply = () => {\n const { position, side } = latest.current;\n map.setSwipe(layers.size > 0 ? { position, layers: [...layers], ...defined({ side }) } : undefined);\n };\n return {\n set: (layer) => {\n layers.add(layer);\n apply();\n },\n unset: (layer) => {\n layers.delete(layer);\n apply();\n },\n };\n }, [map, layers]);\n\n const { position, side } = props;\n useEffect(() => {\n if (map && layers.size > 0) map.setSwipe({ position, layers: [...layers], ...defined({ side }) });\n }, [map, layers, position, side]);\n useEffect(\n () => () => {\n map?.setSwipe(undefined);\n },\n [map],\n );\n\n return createElement(SwipeContext.Provider, { value: group }, props.children);\n}\n\nexport type { Camera, GeoMap, LayerHandle, LayerSpec, LonLat, MarkerHandle, PanelHandle, PopupHandle };\n"]}
|
package/dist/settings.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PngEncoding } from './terrain.ts';
|
|
1
|
+
import type { PngEncoding } from './basemap/terrain.ts';
|
|
2
2
|
import type { AtmosphereOptions, BodyOptions, CelestialBody, LoadingMode, TerrainModel, TerrainOptions } from './types.ts';
|
|
3
3
|
/** Decodes the relief tiles of a URL for the engine: 256 × 256 altitudes, null where there is no tile */
|
|
4
4
|
export type TerrainLoader = (z: number, x: number, y: number, signal: AbortSignal) => Promise<Float32Array | null>;
|
package/dist/settings.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* The settings of a map's engine from its options: the body, its halo, its relief, the sky. Kept by the map and
|
|
3
3
|
* replayed on a new engine (after a context loss, or on the engine with 3D Tiles).
|
|
4
4
|
*/
|
|
5
|
-
import { BODY_TERRAINS, IGN_ATTRIBUTION, IMAGE_SERVER, imageServerTerrain, TERRAIN_TILES, TERRAIN_TILES_ATTRIBUTION, TERRAIN_URL, } from "./basemaps.js";
|
|
6
|
-
import { parseCssColor } from "./color.js";
|
|
5
|
+
import { BODY_TERRAINS, IGN_ATTRIBUTION, IMAGE_SERVER, imageServerTerrain, TERRAIN_TILES, TERRAIN_TILES_ATTRIBUTION, TERRAIN_URL, } from "./basemap/basemaps.js";
|
|
6
|
+
import { parseCssColor } from "./util/color.js";
|
|
7
7
|
/** The named bodies; a body's `color` is that of its globe under the tiles (the Earth's follows its basemap), its
|
|
8
8
|
* `relief` the range of its ground (the Earth's dry land, from the Dead Sea to Everest). Mars: the sphere of its
|
|
9
9
|
* equatorial radius (its data take planetocentric latitudes), a faint butterscotch halo of dust */
|