isard 2.3.0 → 2.4.1
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 +31 -0
- package/dist/isard_grid_bg.wasm +0 -0
- package/dist/isard_tess_bg.wasm +0 -0
- package/dist/isard_webgl2_bg.wasm +0 -0
- package/dist/isard_webgpu_bg.wasm +0 -0
- package/dist/react/Ifc.d.ts +32 -0
- package/dist/react/Ifc.js +52 -0
- package/dist/react/Ifc.js.map +1 -0
- package/dist/react/Street.d.ts +29 -0
- package/dist/react/Street.js +54 -0
- package/dist/react/Street.js.map +1 -0
- package/dist/react/VectorTiles.d.ts +6 -0
- package/dist/react/VectorTiles.js +16 -1
- package/dist/react/VectorTiles.js.map +1 -1
- package/dist/react/context.d.ts +6 -0
- package/dist/react/context.js +13 -0
- package/dist/react/context.js.map +1 -1
- package/dist/react/index.d.ts +5 -2
- package/dist/react/index.js +5 -2
- package/dist/react/index.js.map +1 -1
- package/dist/react/sources.d.ts +51 -0
- package/dist/react/sources.js +104 -0
- package/dist/react/sources.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor versio
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [2.4.1] - 2026-11-02
|
|
9
|
+
|
|
10
|
+
With `isard-react-native` and `isard-map` (Python) 2.4.1.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- `<IsardMapView>`'s `onReady` gets the view's handle, the object its ref gives (`onReady={(view) => …}`): what the app
|
|
15
|
+
adds once by isard's API, or reads in the page, goes there in place of an effect, called again if the page loads
|
|
16
|
+
again. A callback without an argument goes on working. React Native's examples write no `useEffect` any more: their
|
|
17
|
+
layers added in `onReady`, the map changed where the app's state changes, the phone's position watched by a hook of
|
|
18
|
+
their own (`useLocation`)
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- The documentation site built again: the React page *Loading data* had double braces in its text, read by VitePress
|
|
23
|
+
as an expression
|
|
24
|
+
|
|
25
|
+
## [2.4.0] - 2026-11-01
|
|
26
|
+
|
|
27
|
+
With `isard-react-native` and `isard-map` (Python) 2.4.0.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- In React, what a component loads is declared, no effect to write: `useSource(source)` gives what a source loads
|
|
32
|
+
(`geojson(url)`, `gpx(url)`, `wfs(typeName, box)`, `collada(url)`, `windGrids(url)`, or `source(key, load)` of your
|
|
33
|
+
own), again only when the source changes, given up when the component goes; `useLoaded(key, load, { dispose })` for
|
|
34
|
+
anything else. `<Ifc url>` reads an IFC file and shows its storeys as they are meshed (`hidden`, `upTo`, `onLoad`,
|
|
35
|
+
`onLayer`); `<Panoramas>`, `<Photo>`, `<FirstPerson>` at street level; `<StreetWind wind>` under a `<VectorTiles>`;
|
|
36
|
+
`useFrame` at each frame, `useMapRedraw` for an overlay of your own. The React examples write no `useEffect`: their
|
|
37
|
+
data, models and panoramas loaded by these
|
|
38
|
+
|
|
8
39
|
## [2.3.0] - 2026-11-01
|
|
9
40
|
|
|
10
41
|
With `isard-react-native` and `isard-map` (Python) 2.3.0.
|
package/dist/isard_grid_bg.wasm
CHANGED
|
Binary file
|
package/dist/isard_tess_bg.wasm
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** `<Ifc>`: an IFC file read in a worker (`loadIfc`), its storeys meshed there one after the other, each a models layer */
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import type { IfcModel, IfcOptions, IfcStorey } from '../data/ifc.ts';
|
|
4
|
+
import type { ModelInstance, ModelLayer } from '../layers/models.ts';
|
|
5
|
+
export interface IfcProps {
|
|
6
|
+
/** The file's URL */
|
|
7
|
+
url: string;
|
|
8
|
+
/** Where it stands: by default its own place (`model.place`: its map conversion or its site) */
|
|
9
|
+
instance?: ModelInstance;
|
|
10
|
+
/** What the altitude is above: `'sea'` (its own place's height above the sea, the default with its own place) or
|
|
11
|
+
* `'ground'` (on the relief: its own place's height left out; the default with an `instance`) */
|
|
12
|
+
altitudeReference?: 'ground' | 'sea';
|
|
13
|
+
/** Hidden: its storeys' layers kept, without instances */
|
|
14
|
+
hidden?: boolean;
|
|
15
|
+
/** The storeys shown, from the lowest up to this one (its index in `model.storeys`); all by default */
|
|
16
|
+
upTo?: number;
|
|
17
|
+
/** Where web-ifc is taken from (`IfcOptions.webIfc`) */
|
|
18
|
+
webIfc?: IfcOptions['webIfc'];
|
|
19
|
+
/** The model, once read: its elements, its storeys, its properties */
|
|
20
|
+
onLoad?: (model: IfcModel) => void;
|
|
21
|
+
/** Each storey's layer, once added, with its storey and the model: to pick its elements, highlight one
|
|
22
|
+
* (`setHighlight`), read their properties (`model.properties`) */
|
|
23
|
+
onLayer?: (layer: ModelLayer, storey: IfcStorey, model: IfcModel) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* An IFC model on the map: read when mounted (again when its URL changes), its storeys shown as they are meshed, from
|
|
27
|
+
* the lowest up, the file closed when unmounted. Placed by its own georeferencing unless `instance` says where
|
|
28
|
+
*/
|
|
29
|
+
export declare const Ifc: ({ url, instance, altitudeReference, hidden, upTo, webIfc, onLoad, onLayer, }: IfcProps) => ReactNode;
|
|
30
|
+
/** Calls `callback` at each of the browser's frames while mounted (an animation: a turning globe, a scene of your
|
|
31
|
+
* own), with the frame's time in milliseconds; the callback may change at each render */
|
|
32
|
+
export declare const useFrame: (callback: (time: number) => void) => void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/** `<Ifc>`: an IFC file read in a worker (`loadIfc`), its storeys meshed there one after the other, each a models layer */
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { Layer } from "./Layer.js";
|
|
5
|
+
import { useLoaded, useLoadedEvent } from "./sources.js";
|
|
6
|
+
/** A storey meshed in the worker, then its layer */
|
|
7
|
+
const Storey = ({ model, index, instances, altitudeReference, onLayer, }) => {
|
|
8
|
+
const storey = model.storeys[index];
|
|
9
|
+
const data = useLoaded(storey && `${storey.id}`, () => storey?.load() ?? Promise.reject(new Error('no storey')));
|
|
10
|
+
if (!data || !storey)
|
|
11
|
+
return null;
|
|
12
|
+
return (_jsx(Layer, { type: "models", data: data, instances: instances, altitudeReference: altitudeReference, onLayer: (layer) => onLayer?.(layer, storey, model) }));
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* An IFC model on the map: read when mounted (again when its URL changes), its storeys shown as they are meshed, from
|
|
16
|
+
* the lowest up, the file closed when unmounted. Placed by its own georeferencing unless `instance` says where
|
|
17
|
+
*/
|
|
18
|
+
export const Ifc = ({ url, instance, altitudeReference, hidden, upTo = Number.POSITIVE_INFINITY, webIfc, onLoad, onLayer, }) => {
|
|
19
|
+
// Read once a URL, the file closed in its worker when the model goes
|
|
20
|
+
const model = useLoaded(url, async () => {
|
|
21
|
+
const { loadIfc } = await import("../data/ifc.js");
|
|
22
|
+
return loadIfc(url, webIfc === undefined ? {} : { webIfc });
|
|
23
|
+
}, { dispose: (gone) => gone.close() });
|
|
24
|
+
useLoadedEvent(model, onLoad);
|
|
25
|
+
if (!model)
|
|
26
|
+
return null;
|
|
27
|
+
const place = model.place;
|
|
28
|
+
const reference = altitudeReference ?? (instance || !place ? 'ground' : 'sea');
|
|
29
|
+
const own = place && {
|
|
30
|
+
lonLat: place.lonLat,
|
|
31
|
+
heading: place.heading,
|
|
32
|
+
...(reference === 'sea' && { altitude: place.elevation }),
|
|
33
|
+
};
|
|
34
|
+
const at = instance ?? own;
|
|
35
|
+
return model.storeys.map((storey, index) => (_jsx(Storey, { model: model, index: index, instances: hidden || !at || index > upTo ? [] : [at], altitudeReference: reference, onLayer: onLayer }, storey.id)));
|
|
36
|
+
};
|
|
37
|
+
/** Calls `callback` at each of the browser's frames while mounted (an animation: a turning globe, a scene of your
|
|
38
|
+
* own), with the frame's time in milliseconds; the callback may change at each render */
|
|
39
|
+
export const useFrame = (callback) => {
|
|
40
|
+
const latest = useRef(callback);
|
|
41
|
+
latest.current = callback;
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const loop = { frame: 0 };
|
|
44
|
+
const tick = (time) => {
|
|
45
|
+
latest.current(time);
|
|
46
|
+
loop.frame = requestAnimationFrame(tick);
|
|
47
|
+
};
|
|
48
|
+
loop.frame = requestAnimationFrame(tick);
|
|
49
|
+
return () => cancelAnimationFrame(loop.frame);
|
|
50
|
+
}, []);
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=Ifc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Ifc.js","sourceRoot":"","sources":["../../src/react/Ifc.tsx"],"names":[],"mappings":"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","sourcesContent":["/** `<Ifc>`: an IFC file read in a worker (`loadIfc`), its storeys meshed there one after the other, each a models layer */\n\nimport { type ReactNode, useEffect, useRef } from 'react';\nimport type { IfcModel, IfcOptions, IfcStorey } from '../data/ifc.ts';\nimport type { ModelInstance, ModelLayer } from '../layers/models.ts';\nimport { Layer } from './Layer.tsx';\nimport { useLoaded, useLoadedEvent } from './sources.ts';\n\nexport interface IfcProps {\n /** The file's URL */\n url: string;\n /** Where it stands: by default its own place (`model.place`: its map conversion or its site) */\n instance?: ModelInstance;\n /** What the altitude is above: `'sea'` (its own place's height above the sea, the default with its own place) or\n * `'ground'` (on the relief: its own place's height left out; the default with an `instance`) */\n altitudeReference?: 'ground' | 'sea';\n /** Hidden: its storeys' layers kept, without instances */\n hidden?: boolean;\n /** The storeys shown, from the lowest up to this one (its index in `model.storeys`); all by default */\n upTo?: number;\n /** Where web-ifc is taken from (`IfcOptions.webIfc`) */\n webIfc?: IfcOptions['webIfc'];\n /** The model, once read: its elements, its storeys, its properties */\n onLoad?: (model: IfcModel) => void;\n /** Each storey's layer, once added, with its storey and the model: to pick its elements, highlight one\n * (`setHighlight`), read their properties (`model.properties`) */\n onLayer?: (layer: ModelLayer, storey: IfcStorey, model: IfcModel) => void;\n}\n\n/** A storey meshed in the worker, then its layer */\nconst Storey = ({\n model,\n index,\n instances,\n altitudeReference,\n onLayer,\n}: {\n model: IfcModel;\n index: number;\n instances: ModelInstance[];\n altitudeReference: 'ground' | 'sea';\n onLayer: ((layer: ModelLayer, storey: IfcStorey, model: IfcModel) => void) | undefined;\n}): ReactNode => {\n const storey = model.storeys[index];\n const data = useLoaded(storey && `${storey.id}`, () => storey?.load() ?? Promise.reject(new Error('no storey')));\n\n if (!data || !storey) return null;\n\n return (\n <Layer<Record<string, unknown>, 'models'>\n type=\"models\"\n data={data}\n instances={instances}\n altitudeReference={altitudeReference}\n onLayer={(layer) => onLayer?.(layer, storey, model)}\n />\n );\n};\n\n/**\n * An IFC model on the map: read when mounted (again when its URL changes), its storeys shown as they are meshed, from\n * the lowest up, the file closed when unmounted. Placed by its own georeferencing unless `instance` says where\n */\nexport const Ifc = ({\n url,\n instance,\n altitudeReference,\n hidden,\n upTo = Number.POSITIVE_INFINITY,\n webIfc,\n onLoad,\n onLayer,\n}: IfcProps): ReactNode => {\n // Read once a URL, the file closed in its worker when the model goes\n const model = useLoaded(\n url,\n async () => {\n const { loadIfc } = await import('../data/ifc.ts');\n\n return loadIfc(url, webIfc === undefined ? {} : { webIfc });\n },\n { dispose: (gone) => gone.close() },\n );\n\n useLoadedEvent(model, onLoad);\n\n if (!model) return null;\n\n const place = model.place;\n const reference = altitudeReference ?? (instance || !place ? 'ground' : 'sea');\n const own = place && {\n lonLat: place.lonLat,\n heading: place.heading,\n ...(reference === 'sea' && { altitude: place.elevation }),\n };\n const at = instance ?? own;\n\n return model.storeys.map((storey, index) => (\n <Storey\n key={storey.id}\n model={model}\n index={index}\n instances={hidden || !at || index > upTo ? [] : [at]}\n altitudeReference={reference}\n onLayer={onLayer}\n />\n ));\n};\n\n/** Calls `callback` at each of the browser's frames while mounted (an animation: a turning globe, a scene of your\n * own), with the frame's time in milliseconds; the callback may change at each render */\nexport const useFrame = (callback: (time: number) => void): void => {\n const latest = useRef(callback);\n\n latest.current = callback;\n useEffect(() => {\n const loop = { frame: 0 };\n const tick = (time: number) => {\n latest.current(time);\n loop.frame = requestAnimationFrame(tick);\n };\n\n loop.frame = requestAnimationFrame(tick);\n\n return () => cancelAnimationFrame(loop.frame);\n }, []);\n};\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Street level in React: a photo standing where it was taken (`<Photo>`), panoramas to walk through (`<Panoramas>`),
|
|
2
|
+
* the eye at street level (`<FirstPerson>`), each while mounted */
|
|
3
|
+
import { type ReactNode } from 'react';
|
|
4
|
+
import type { ModelLayer } from '../layers/models.ts';
|
|
5
|
+
import type { PanoramaOptions, Panoramas as PanoramasHandle } from '../layers/panoramas.ts';
|
|
6
|
+
import type { PhotoOptions } from '../layers/photos.ts';
|
|
7
|
+
import type { FirstPersonView, LonLat } from '../types.ts';
|
|
8
|
+
export type PhotoProps = PhotoOptions & {
|
|
9
|
+
/** Its layer, once added */
|
|
10
|
+
onLayer?: (layer: ModelLayer) => void;
|
|
11
|
+
};
|
|
12
|
+
/** A photo standing where it was taken, facing its way (`map.addPhoto`): added again when its options change (compared
|
|
13
|
+
* by value), removed when unmounted; in a `<Swipe>`, on its side of the line */
|
|
14
|
+
export declare const Photo: ({ onLayer, ...options }: PhotoProps) => ReactNode;
|
|
15
|
+
export type PanoramasProps = PanoramaOptions & {
|
|
16
|
+
/** Walked through from the start (`enter`): at the panorama of this index, or the one nearest to a position */
|
|
17
|
+
enter?: {
|
|
18
|
+
index?: number;
|
|
19
|
+
near?: LonLat;
|
|
20
|
+
};
|
|
21
|
+
/** The panoramas, once loaded (and entered) */
|
|
22
|
+
onLoad?: (panoramas: PanoramasHandle) => void;
|
|
23
|
+
};
|
|
24
|
+
/** Panoramas on the map (`map.addPanoramas`): loaded again when their options change (compared by value), removed when
|
|
25
|
+
* unmounted (the walk ended, the eye back above the map) */
|
|
26
|
+
export declare const Panoramas: ({ enter, onLoad, ...options }: PanoramasProps) => ReactNode;
|
|
27
|
+
/** The eye at street level while mounted (`map.setFirstPerson`), a drag turning it; turning around a point again when
|
|
28
|
+
* unmounted */
|
|
29
|
+
export declare const FirstPerson: (view: FirstPersonView) => ReactNode;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Street level in React: a photo standing where it was taken (`<Photo>`), panoramas to walk through (`<Panoramas>`),
|
|
2
|
+
* the eye at street level (`<FirstPerson>`), each while mounted */
|
|
3
|
+
import { useContext, useEffect } from 'react';
|
|
4
|
+
import { SwipeContext, useMap, useStableOptions } from "./context.js";
|
|
5
|
+
import { useLoaded, useLoadedEvent } from "./sources.js";
|
|
6
|
+
/** A photo standing where it was taken, facing its way (`map.addPhoto`): added again when its options change (compared
|
|
7
|
+
* by value), removed when unmounted; in a `<Swipe>`, on its side of the line */
|
|
8
|
+
export const Photo = ({ onLayer, ...options }) => {
|
|
9
|
+
const map = useMap();
|
|
10
|
+
const swipe = useContext(SwipeContext);
|
|
11
|
+
const stable = useStableOptions(options);
|
|
12
|
+
const layer = useLoaded(map && stable, () => map?.addPhoto(stable) ?? Promise.reject(new Error('no map')), {
|
|
13
|
+
dispose: (gone) => gone.remove(),
|
|
14
|
+
});
|
|
15
|
+
useLoadedEvent(layer, onLayer);
|
|
16
|
+
// On its side of the swipe around while there
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!layer || !swipe)
|
|
19
|
+
return;
|
|
20
|
+
swipe.set(layer);
|
|
21
|
+
return () => swipe.unset(layer);
|
|
22
|
+
}, [layer, swipe]);
|
|
23
|
+
return null;
|
|
24
|
+
};
|
|
25
|
+
/** Panoramas on the map (`map.addPanoramas`): loaded again when their options change (compared by value), removed when
|
|
26
|
+
* unmounted (the walk ended, the eye back above the map) */
|
|
27
|
+
export const Panoramas = ({ enter, onLoad, ...options }) => {
|
|
28
|
+
const map = useMap();
|
|
29
|
+
const stable = useStableOptions(options);
|
|
30
|
+
const panoramas = useLoaded(map && stable, async () => {
|
|
31
|
+
if (!map)
|
|
32
|
+
throw new Error('no map');
|
|
33
|
+
const added = await map.addPanoramas(stable);
|
|
34
|
+
if (enter)
|
|
35
|
+
await added.enter(enter);
|
|
36
|
+
return added;
|
|
37
|
+
}, { dispose: (gone) => gone.remove() });
|
|
38
|
+
useLoadedEvent(panoramas, onLoad);
|
|
39
|
+
return null;
|
|
40
|
+
};
|
|
41
|
+
/** The eye at street level while mounted (`map.setFirstPerson`), a drag turning it; turning around a point again when
|
|
42
|
+
* unmounted */
|
|
43
|
+
export const FirstPerson = (view) => {
|
|
44
|
+
const map = useMap();
|
|
45
|
+
const stable = useStableOptions(view);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!map)
|
|
48
|
+
return;
|
|
49
|
+
map.setFirstPerson(stable);
|
|
50
|
+
return () => map.setFirstPerson(undefined);
|
|
51
|
+
}, [map, stable]);
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=Street.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Street.js","sourceRoot":"","sources":["../../src/react/Street.tsx"],"names":[],"mappings":"AAAA;mEACmE;AAEnE,OAAO,EAAkB,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAK9D,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAOzD;gFACgF;AAChF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,EAAc,EAAa,EAAE;IACtE,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,IAAI,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE;QACzG,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;KACjC,CAAC,CAAC;IAEH,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/B,8CAA8C;IAC9C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK;YAAE,OAAO;QAC7B,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEjB,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAEnB,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AASF;4DAC4D;AAC5D,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,EAAkB,EAAa,EAAE;IACpF,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,SAAS,CACzB,GAAG,IAAI,MAAM,EACb,KAAK,IAAI,EAAE;QACT,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEpC,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAE7C,IAAI,KAAK;YAAE,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEpC,OAAO,KAAK,CAAC;IACf,CAAC,EACD,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CACrC,CAAC;IAEF,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAElC,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF;eACe;AACf,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAqB,EAAa,EAAE;IAC9D,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAEtC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE3B,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IAElB,OAAO,IAAI,CAAC;AACd,CAAC,CAAC","sourcesContent":["/** Street level in React: a photo standing where it was taken (`<Photo>`), panoramas to walk through (`<Panoramas>`),\n * the eye at street level (`<FirstPerson>`), each while mounted */\n\nimport { type ReactNode, useContext, useEffect } from 'react';\nimport type { ModelLayer } from '../layers/models.ts';\nimport type { PanoramaOptions, Panoramas as PanoramasHandle } from '../layers/panoramas.ts';\nimport type { PhotoOptions } from '../layers/photos.ts';\nimport type { FirstPersonView, LonLat } from '../types.ts';\nimport { SwipeContext, useMap, useStableOptions } from './context.ts';\nimport { useLoaded, useLoadedEvent } from './sources.ts';\n\nexport type PhotoProps = PhotoOptions & {\n /** Its layer, once added */\n onLayer?: (layer: ModelLayer) => void;\n};\n\n/** A photo standing where it was taken, facing its way (`map.addPhoto`): added again when its options change (compared\n * by value), removed when unmounted; in a `<Swipe>`, on its side of the line */\nexport const Photo = ({ onLayer, ...options }: PhotoProps): ReactNode => {\n const map = useMap();\n const swipe = useContext(SwipeContext);\n const stable = useStableOptions(options);\n const layer = useLoaded(map && stable, () => map?.addPhoto(stable) ?? Promise.reject(new Error('no map')), {\n dispose: (gone) => gone.remove(),\n });\n\n useLoadedEvent(layer, onLayer);\n // On its side of the swipe around while there\n useEffect(() => {\n if (!layer || !swipe) return;\n swipe.set(layer);\n\n return () => swipe.unset(layer);\n }, [layer, swipe]);\n\n return null;\n};\n\nexport type PanoramasProps = PanoramaOptions & {\n /** Walked through from the start (`enter`): at the panorama of this index, or the one nearest to a position */\n enter?: { index?: number; near?: LonLat };\n /** The panoramas, once loaded (and entered) */\n onLoad?: (panoramas: PanoramasHandle) => void;\n};\n\n/** Panoramas on the map (`map.addPanoramas`): loaded again when their options change (compared by value), removed when\n * unmounted (the walk ended, the eye back above the map) */\nexport const Panoramas = ({ enter, onLoad, ...options }: PanoramasProps): ReactNode => {\n const map = useMap();\n const stable = useStableOptions(options);\n const panoramas = useLoaded(\n map && stable,\n async () => {\n if (!map) throw new Error('no map');\n\n const added = await map.addPanoramas(stable);\n\n if (enter) await added.enter(enter);\n\n return added;\n },\n { dispose: (gone) => gone.remove() },\n );\n\n useLoadedEvent(panoramas, onLoad);\n\n return null;\n};\n\n/** The eye at street level while mounted (`map.setFirstPerson`), a drag turning it; turning around a point again when\n * unmounted */\nexport const FirstPerson = (view: FirstPersonView): ReactNode => {\n const map = useMap();\n const stable = useStableOptions(view);\n\n useEffect(() => {\n if (!map) return;\n map.setFirstPerson(stable);\n\n return () => map.setFirstPerson(undefined);\n }, [map, stable]);\n\n return null;\n};\n"]}
|
|
@@ -5,6 +5,7 @@ import type { VectorExtrusionOptions } from '../basemap/vectortiles/extrusions.t
|
|
|
5
5
|
import type { FurnitureOptions } from '../basemap/vectortiles/furniture.ts';
|
|
6
6
|
import type { LabelsOptions } from '../basemap/vectortiles/labels.ts';
|
|
7
7
|
import type { LampsOptions } from '../basemap/vectortiles/lamps.ts';
|
|
8
|
+
import type { StreetWindLayer, StreetWindOptions } from '../basemap/vectortiles/street-wind.ts';
|
|
8
9
|
import type { TreesOptions } from '../basemap/vectortiles/trees.ts';
|
|
9
10
|
import type { VectorRasterOptions, VectorTiles as VectorTilesHandle, VectorTilesOptions } from '../basemap/vectortiles.ts';
|
|
10
11
|
import type { ExtrusionLayer } from '../layers/extrusions.ts';
|
|
@@ -63,6 +64,11 @@ export declare const Labels: (props: PartProps<LabelsOptions, SymbolLayer<LabelC
|
|
|
63
64
|
* its options as props; functions compared by identity, so at the module's level or in a memo). The properties of
|
|
64
65
|
* its features named by `<Extrusions<Building> …>` */
|
|
65
66
|
export declare const Extrusions: <P = Record<string, unknown>>(props: PartProps<VectorExtrusionOptions<P>, ExtrusionLayer<P>>) => ReactNode;
|
|
67
|
+
/** The wind in the streets of the vector tiles around, round their buildings (`VectorTiles.streetWind`: its options as
|
|
68
|
+
* props); `wind` the wind above the town, `[u, v]` m/s, the flow turned to it as it changes */
|
|
69
|
+
export declare const StreetWind: (props: PartProps<StreetWindOptions & {
|
|
70
|
+
wind?: [u: number, v: number];
|
|
71
|
+
}, StreetWindLayer>) => ReactNode;
|
|
66
72
|
/** The fills, lines and circles of the vector tiles around drawn into images over the ground (`VectorTiles.raster`): the
|
|
67
73
|
* light way over the relief, under 3D buildings (their own drawn as geometry by a `<Geometry>`, or none) */
|
|
68
74
|
export declare const Raster: (props: PartProps<VectorRasterOptions, RasterLayer>) => ReactNode;
|
|
@@ -44,7 +44,7 @@ export const VectorTiles = VectorTilesComponent;
|
|
|
44
44
|
* (`streetWind`, `raster`…) */
|
|
45
45
|
export const useVectorTiles = () => useContext(VectorTilesContext);
|
|
46
46
|
/** A part drawn from the vector tiles around: made by `make` once they are there, anew when its options change
|
|
47
|
-
* (compared by value, `useStableOptions`), removed when unmounted */
|
|
47
|
+
* (compared by value, `useStableOptions`), removed when unmounted; its layer */
|
|
48
48
|
const usePart = (make, props, ref) => {
|
|
49
49
|
const binding = useContext(MapContext);
|
|
50
50
|
const tiles = useContext(VectorTilesContext);
|
|
@@ -67,6 +67,7 @@ const usePart = (make, props, ref) => {
|
|
|
67
67
|
setLayer(undefined);
|
|
68
68
|
};
|
|
69
69
|
}, [binding, tiles, stable, make]);
|
|
70
|
+
return layer;
|
|
70
71
|
};
|
|
71
72
|
/** The parts' layers made from the vector tiles (at the module's level: the same functions from render to render) */
|
|
72
73
|
const makeTrees = (tiles, options) => tiles.trees(options);
|
|
@@ -75,6 +76,7 @@ const makeFurniture = (tiles, options) => tiles.furniture(options);
|
|
|
75
76
|
const makeLabels = (tiles, options) => tiles.labels(options);
|
|
76
77
|
const makeRaster = (tiles, options) => tiles.raster(options);
|
|
77
78
|
const makeExtrusions = (tiles, options) => tiles.extrusions(options);
|
|
79
|
+
const makeStreetWind = (tiles, options) => tiles.streetWind(options);
|
|
78
80
|
const TreesComponent = forwardRef(function Trees(props, ref) {
|
|
79
81
|
usePart(makeTrees, props, ref);
|
|
80
82
|
return null;
|
|
@@ -99,6 +101,16 @@ const RasterComponent = forwardRef(function Raster(props, ref) {
|
|
|
99
101
|
usePart(makeRaster, props, ref);
|
|
100
102
|
return null;
|
|
101
103
|
});
|
|
104
|
+
const StreetWindComponent = forwardRef(function StreetWind({ wind, ...props }, ref) {
|
|
105
|
+
const layer = usePart(makeStreetWind, props, ref);
|
|
106
|
+
const [u, v] = wind ?? [Number.NaN, Number.NaN];
|
|
107
|
+
// The wind given: the flow round the buildings turned to it, not computed again
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
if (layer && Number.isFinite(u) && Number.isFinite(v))
|
|
110
|
+
layer.setWind([u, v]);
|
|
111
|
+
}, [layer, u, v]);
|
|
112
|
+
return null;
|
|
113
|
+
});
|
|
102
114
|
/** The trees of the vector tiles around on their woods, forests and grass, as 3D models (`VectorTiles.trees`: its
|
|
103
115
|
* options as props) */
|
|
104
116
|
export const Trees = TreesComponent;
|
|
@@ -114,6 +126,9 @@ export const Labels = LabelsComponent;
|
|
|
114
126
|
* its options as props; functions compared by identity, so at the module's level or in a memo). The properties of
|
|
115
127
|
* its features named by `<Extrusions<Building> …>` */
|
|
116
128
|
export const Extrusions = ExtrusionsComponent;
|
|
129
|
+
/** The wind in the streets of the vector tiles around, round their buildings (`VectorTiles.streetWind`: its options as
|
|
130
|
+
* props); `wind` the wind above the town, `[u, v]` m/s, the flow turned to it as it changes */
|
|
131
|
+
export const StreetWind = StreetWindComponent;
|
|
117
132
|
/** The fills, lines and circles of the vector tiles around drawn into images over the ground (`VectorTiles.raster`): the
|
|
118
133
|
* light way over the relief, under 3D buildings (their own drawn as geometry by a `<Geometry>`, or none) */
|
|
119
134
|
export const Raster = RasterComponent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VectorTiles.js","sourceRoot":"","sources":["../../src/react/VectorTiles.tsx"],"names":[],"mappings":"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","sourcesContent":["/** `<VectorTiles>`: the vector tiles of a MapLibre style, the parts drawn from them as its children (`<Geometry>`,\n * `<Trees>`, `<Lamps>`, `<Furniture>`, `<Labels>`) sharing its tiles and its workers */\n\nimport {\n type ForwardedRef,\n forwardRef,\n type ReactNode,\n type Ref,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react';\nimport type { VectorExtrusionOptions } from '../basemap/vectortiles/extrusions.ts';\nimport type { FurnitureOptions } from '../basemap/vectortiles/furniture.ts';\nimport type { LabelsOptions } from '../basemap/vectortiles/labels.ts';\nimport type { LampsOptions } from '../basemap/vectortiles/lamps.ts';\nimport type { TreesOptions } from '../basemap/vectortiles/trees.ts';\nimport type {\n VectorRasterOptions,\n VectorTiles as VectorTilesHandle,\n VectorTilesOptions,\n} from '../basemap/vectortiles.ts';\nimport type { ExtrusionLayer } from '../layers/extrusions.ts';\nimport type { Layer as LayerHandle } from '../layers/layers.ts';\nimport type { ModelLayer } from '../layers/models.ts';\nimport type { RasterLayer } from '../layers/raster.ts';\nimport type { SymbolLayer } from '../layers/symbols.ts';\nimport type { LabelCandidate } from '../workers/basemap/protocol.ts';\nimport { MapContext, useStableOptions, VectorTilesContext } from './context.ts';\n\nexport interface VectorTilesProps extends VectorTilesOptions {\n /** The vector tiles, once made and each time they are made anew (another style, other workers) */\n onTiles?: (tiles: VectorTilesHandle) => void;\n ref?: Ref<VectorTilesHandle | undefined>;\n /** The parts drawn from its tiles: `<Geometry>`, `<Trees>`, `<Lamps>`, `<Furniture>`, `<Labels>`, or a component of\n * your own using `useVectorTiles` */\n children?: ReactNode;\n}\n\nconst VectorTilesComponent = forwardRef<VectorTilesHandle | undefined, Omit<VectorTilesProps, 'ref'>>(\n function VectorTiles({ children, onTiles, ...options }, ref) {\n const binding = useContext(MapContext);\n const stable = useStableOptions(options);\n const latest = useRef(onTiles);\n const [tiles, setTiles] = useState<VectorTilesHandle>();\n\n latest.current = onTiles;\n useImperativeHandle(ref, () => tiles, [tiles]);\n\n // Made once the map is there, anew when its options change; destroyed with its parts when unmounted\n useEffect(() => {\n if (!binding) return;\n\n const made = binding.map.vectorTiles(stable);\n\n setTiles(made);\n latest.current?.(made);\n\n return () => {\n made.remove();\n setTiles(undefined);\n };\n }, [binding, stable]);\n\n return <VectorTilesContext.Provider value={tiles}>{children}</VectorTilesContext.Provider>;\n },\n);\n\n/**\n * The vector tiles of a MapLibre style (`map.vectorTiles`), its children drawing from them: the style's fills, lines\n * and buildings as geometry (`<Geometry>`), its trees, street lamps, street furniture, labels. One set of tiles and\n * workers for all of them: the tiles fetched and decoded once. Its options (`style`, `workers`…) compared by value: an\n * inline object keeps the tiles, another style makes them anew. The ref and `onTiles` give its handle\n * (`VectorTiles`).\n *\n * ```tsx\n * <VectorTiles style={OPENFREEMAP_STYLES.liberty}>\n * <Geometry roofs=\"generated\" />\n * <Trees />\n * <Labels />\n * </VectorTiles>\n * ```\n */\nexport const VectorTiles = VectorTilesComponent as (props: VectorTilesProps) => ReactNode;\n\n/** The vector tiles of the `<VectorTiles>` around, once made (undefined before): a part of your own drawn from them\n * (`streetWind`, `raster`…) */\nexport const useVectorTiles = (): VectorTilesHandle | undefined => useContext(VectorTilesContext);\n\n/** What a part drawn from the vector tiles around takes besides its options */\ninterface PartHandle<L> {\n /** The layer, once made and each time it is made anew */\n onLayer?: (layer: L) => void;\n}\n\n/** The props of a part: its options (those of its `VectorTiles` method), its handle */\ntype PartProps<O, L> = O & PartHandle<L> & { ref?: Ref<L | undefined> };\n\n/** A part drawn from the vector tiles around: made by `make` once they are there, anew when its options change\n * (compared by value, `useStableOptions`), removed when unmounted */\nconst usePart = <O extends object, L extends LayerHandle>(\n make: (tiles: VectorTilesHandle, options: O) => L,\n props: O & PartHandle<L>,\n ref: ForwardedRef<L | undefined>,\n): void => {\n const binding = useContext(MapContext);\n const tiles = useContext(VectorTilesContext);\n // Its options: its props but `onLayer` (a function written inline would make it anew at each render)\n const stable = useStableOptions({ ...props, onLayer: undefined });\n const latest = useRef(props.onLayer);\n const [layer, setLayer] = useState<L>();\n\n latest.current = props.onLayer;\n useImperativeHandle(ref, () => layer, [layer]);\n useEffect(() => {\n if (!binding || !tiles) return;\n\n const made = make(tiles, stable);\n\n setLayer(made);\n latest.current?.(made);\n\n return () => {\n // Gone already with its tiles, or with the map\n if (binding.map.hasLayer(made)) made.remove();\n setLayer(undefined);\n };\n }, [binding, tiles, stable, make]);\n};\n\n/** The parts' layers made from the vector tiles (at the module's level: the same functions from render to render) */\nconst makeTrees = (tiles: VectorTilesHandle, options: TreesOptions) => tiles.trees(options);\nconst makeLamps = (tiles: VectorTilesHandle, options: LampsOptions) => tiles.lamps(options);\nconst makeFurniture = (tiles: VectorTilesHandle, options: FurnitureOptions) => tiles.furniture(options);\nconst makeLabels = (tiles: VectorTilesHandle, options: LabelsOptions) => tiles.labels(options);\nconst makeRaster = (tiles: VectorTilesHandle, options: VectorRasterOptions) => tiles.raster(options);\nconst makeExtrusions = (tiles: VectorTilesHandle, options: VectorExtrusionOptions<Record<string, unknown>>) =>\n tiles.extrusions(options);\n\nconst TreesComponent = forwardRef<ModelLayer | undefined, TreesOptions & PartHandle<ModelLayer>>(\n function Trees(props, ref) {\n usePart(makeTrees, props, ref);\n\n return null;\n },\n);\n\nconst LampsComponent = forwardRef<ModelLayer | undefined, LampsOptions & PartHandle<ModelLayer>>(\n function Lamps(props, ref) {\n usePart(makeLamps, props, ref);\n\n return null;\n },\n);\n\nconst FurnitureComponent = forwardRef<ModelLayer | undefined, FurnitureOptions & PartHandle<ModelLayer>>(\n function Furniture(props, ref) {\n usePart(makeFurniture, props, ref);\n\n return null;\n },\n);\n\nconst LabelsComponent = forwardRef<\n SymbolLayer<LabelCandidate> | undefined,\n LabelsOptions & PartHandle<SymbolLayer<LabelCandidate>>\n>(function Labels(props, ref) {\n usePart(makeLabels, props, ref);\n\n return null;\n});\n\nconst ExtrusionsComponent = forwardRef<\n ExtrusionLayer<Record<string, unknown>> | undefined,\n VectorExtrusionOptions<Record<string, unknown>> & PartHandle<ExtrusionLayer<Record<string, unknown>>>\n>(function Extrusions(props, ref) {\n usePart(makeExtrusions, props, ref);\n\n return null;\n});\n\nconst RasterComponent = forwardRef<RasterLayer | undefined, VectorRasterOptions & PartHandle<RasterLayer>>(\n function Raster(props, ref) {\n usePart(makeRaster, props, ref);\n\n return null;\n },\n);\n\n/** The trees of the vector tiles around on their woods, forests and grass, as 3D models (`VectorTiles.trees`: its\n * options as props) */\nexport const Trees = TreesComponent as (props: PartProps<TreesOptions, ModelLayer>) => ReactNode;\n\n/** Street lamps along the streets of the vector tiles around, lit at night (`VectorTiles.lamps`) */\nexport const Lamps = LampsComponent as (props: PartProps<LampsOptions, ModelLayer>) => ReactNode;\n\n/** The street furniture of the vector tiles around: bus shelters, waste baskets, post boxes, fountains, bicycle racks,\n * benches (`VectorTiles.furniture`) */\nexport const Furniture = FurnitureComponent as (props: PartProps<FurnitureOptions, ModelLayer>) => ReactNode;\n\n/** The labels of the vector tiles around (`VectorTiles.labels`): hidden by the buildings of a `<Geometry>` beside\n * them */\nexport const Labels = LabelsComponent as (props: PartProps<LabelsOptions, SymbolLayer<LabelCandidate>>) => ReactNode;\n\n/** The buildings of the vector tiles around as an extrusion layer, its style per feature (`VectorTiles.extrusions`:\n * its options as props; functions compared by identity, so at the module's level or in a memo). The properties of\n * its features named by `<Extrusions<Building> …>` */\nexport const Extrusions = ExtrusionsComponent as <P = Record<string, unknown>>(\n props: PartProps<VectorExtrusionOptions<P>, ExtrusionLayer<P>>,\n) => ReactNode;\n\n/** The fills, lines and circles of the vector tiles around drawn into images over the ground (`VectorTiles.raster`): the\n * light way over the relief, under 3D buildings (their own drawn as geometry by a `<Geometry>`, or none) */\nexport const Raster = RasterComponent as (props: PartProps<VectorRasterOptions, RasterLayer>) => ReactNode;\n"]}
|
|
1
|
+
{"version":3,"file":"VectorTiles.js","sourceRoot":"","sources":["../../src/react/VectorTiles.tsx"],"names":[],"mappings":"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","sourcesContent":["/** `<VectorTiles>`: the vector tiles of a MapLibre style, the parts drawn from them as its children (`<Geometry>`,\n * `<Trees>`, `<Lamps>`, `<Furniture>`, `<Labels>`) sharing its tiles and its workers */\n\nimport {\n type ForwardedRef,\n forwardRef,\n type ReactNode,\n type Ref,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react';\nimport type { VectorExtrusionOptions } from '../basemap/vectortiles/extrusions.ts';\nimport type { FurnitureOptions } from '../basemap/vectortiles/furniture.ts';\nimport type { LabelsOptions } from '../basemap/vectortiles/labels.ts';\nimport type { LampsOptions } from '../basemap/vectortiles/lamps.ts';\nimport type { StreetWindLayer, StreetWindOptions } from '../basemap/vectortiles/street-wind.ts';\nimport type { TreesOptions } from '../basemap/vectortiles/trees.ts';\nimport type {\n VectorRasterOptions,\n VectorTiles as VectorTilesHandle,\n VectorTilesOptions,\n} from '../basemap/vectortiles.ts';\nimport type { ExtrusionLayer } from '../layers/extrusions.ts';\nimport type { Layer as LayerHandle } from '../layers/layers.ts';\nimport type { ModelLayer } from '../layers/models.ts';\nimport type { RasterLayer } from '../layers/raster.ts';\nimport type { SymbolLayer } from '../layers/symbols.ts';\nimport type { LabelCandidate } from '../workers/basemap/protocol.ts';\nimport { MapContext, useStableOptions, VectorTilesContext } from './context.ts';\n\nexport interface VectorTilesProps extends VectorTilesOptions {\n /** The vector tiles, once made and each time they are made anew (another style, other workers) */\n onTiles?: (tiles: VectorTilesHandle) => void;\n ref?: Ref<VectorTilesHandle | undefined>;\n /** The parts drawn from its tiles: `<Geometry>`, `<Trees>`, `<Lamps>`, `<Furniture>`, `<Labels>`, or a component of\n * your own using `useVectorTiles` */\n children?: ReactNode;\n}\n\nconst VectorTilesComponent = forwardRef<VectorTilesHandle | undefined, Omit<VectorTilesProps, 'ref'>>(\n function VectorTiles({ children, onTiles, ...options }, ref) {\n const binding = useContext(MapContext);\n const stable = useStableOptions(options);\n const latest = useRef(onTiles);\n const [tiles, setTiles] = useState<VectorTilesHandle>();\n\n latest.current = onTiles;\n useImperativeHandle(ref, () => tiles, [tiles]);\n\n // Made once the map is there, anew when its options change; destroyed with its parts when unmounted\n useEffect(() => {\n if (!binding) return;\n\n const made = binding.map.vectorTiles(stable);\n\n setTiles(made);\n latest.current?.(made);\n\n return () => {\n made.remove();\n setTiles(undefined);\n };\n }, [binding, stable]);\n\n return <VectorTilesContext.Provider value={tiles}>{children}</VectorTilesContext.Provider>;\n },\n);\n\n/**\n * The vector tiles of a MapLibre style (`map.vectorTiles`), its children drawing from them: the style's fills, lines\n * and buildings as geometry (`<Geometry>`), its trees, street lamps, street furniture, labels. One set of tiles and\n * workers for all of them: the tiles fetched and decoded once. Its options (`style`, `workers`…) compared by value: an\n * inline object keeps the tiles, another style makes them anew. The ref and `onTiles` give its handle\n * (`VectorTiles`).\n *\n * ```tsx\n * <VectorTiles style={OPENFREEMAP_STYLES.liberty}>\n * <Geometry roofs=\"generated\" />\n * <Trees />\n * <Labels />\n * </VectorTiles>\n * ```\n */\nexport const VectorTiles = VectorTilesComponent as (props: VectorTilesProps) => ReactNode;\n\n/** The vector tiles of the `<VectorTiles>` around, once made (undefined before): a part of your own drawn from them\n * (`streetWind`, `raster`…) */\nexport const useVectorTiles = (): VectorTilesHandle | undefined => useContext(VectorTilesContext);\n\n/** What a part drawn from the vector tiles around takes besides its options */\ninterface PartHandle<L> {\n /** The layer, once made and each time it is made anew */\n onLayer?: (layer: L) => void;\n}\n\n/** The props of a part: its options (those of its `VectorTiles` method), its handle */\ntype PartProps<O, L> = O & PartHandle<L> & { ref?: Ref<L | undefined> };\n\n/** A part drawn from the vector tiles around: made by `make` once they are there, anew when its options change\n * (compared by value, `useStableOptions`), removed when unmounted; its layer */\nconst usePart = <O extends object, L extends LayerHandle>(\n make: (tiles: VectorTilesHandle, options: O) => L,\n props: O & PartHandle<L>,\n ref: ForwardedRef<L | undefined>,\n): L | undefined => {\n const binding = useContext(MapContext);\n const tiles = useContext(VectorTilesContext);\n // Its options: its props but `onLayer` (a function written inline would make it anew at each render)\n const stable = useStableOptions({ ...props, onLayer: undefined });\n const latest = useRef(props.onLayer);\n const [layer, setLayer] = useState<L>();\n\n latest.current = props.onLayer;\n useImperativeHandle(ref, () => layer, [layer]);\n useEffect(() => {\n if (!binding || !tiles) return;\n\n const made = make(tiles, stable);\n\n setLayer(made);\n latest.current?.(made);\n\n return () => {\n // Gone already with its tiles, or with the map\n if (binding.map.hasLayer(made)) made.remove();\n setLayer(undefined);\n };\n }, [binding, tiles, stable, make]);\n\n return layer;\n};\n\n/** The parts' layers made from the vector tiles (at the module's level: the same functions from render to render) */\nconst makeTrees = (tiles: VectorTilesHandle, options: TreesOptions) => tiles.trees(options);\nconst makeLamps = (tiles: VectorTilesHandle, options: LampsOptions) => tiles.lamps(options);\nconst makeFurniture = (tiles: VectorTilesHandle, options: FurnitureOptions) => tiles.furniture(options);\nconst makeLabels = (tiles: VectorTilesHandle, options: LabelsOptions) => tiles.labels(options);\nconst makeRaster = (tiles: VectorTilesHandle, options: VectorRasterOptions) => tiles.raster(options);\nconst makeExtrusions = (tiles: VectorTilesHandle, options: VectorExtrusionOptions<Record<string, unknown>>) =>\n tiles.extrusions(options);\nconst makeStreetWind = (tiles: VectorTilesHandle, options: StreetWindOptions) => tiles.streetWind(options);\n\nconst TreesComponent = forwardRef<ModelLayer | undefined, TreesOptions & PartHandle<ModelLayer>>(\n function Trees(props, ref) {\n usePart(makeTrees, props, ref);\n\n return null;\n },\n);\n\nconst LampsComponent = forwardRef<ModelLayer | undefined, LampsOptions & PartHandle<ModelLayer>>(\n function Lamps(props, ref) {\n usePart(makeLamps, props, ref);\n\n return null;\n },\n);\n\nconst FurnitureComponent = forwardRef<ModelLayer | undefined, FurnitureOptions & PartHandle<ModelLayer>>(\n function Furniture(props, ref) {\n usePart(makeFurniture, props, ref);\n\n return null;\n },\n);\n\nconst LabelsComponent = forwardRef<\n SymbolLayer<LabelCandidate> | undefined,\n LabelsOptions & PartHandle<SymbolLayer<LabelCandidate>>\n>(function Labels(props, ref) {\n usePart(makeLabels, props, ref);\n\n return null;\n});\n\nconst ExtrusionsComponent = forwardRef<\n ExtrusionLayer<Record<string, unknown>> | undefined,\n VectorExtrusionOptions<Record<string, unknown>> & PartHandle<ExtrusionLayer<Record<string, unknown>>>\n>(function Extrusions(props, ref) {\n usePart(makeExtrusions, props, ref);\n\n return null;\n});\n\nconst RasterComponent = forwardRef<RasterLayer | undefined, VectorRasterOptions & PartHandle<RasterLayer>>(\n function Raster(props, ref) {\n usePart(makeRaster, props, ref);\n\n return null;\n },\n);\n\nconst StreetWindComponent = forwardRef<\n StreetWindLayer | undefined,\n StreetWindOptions & { wind?: [u: number, v: number] } & PartHandle<StreetWindLayer>\n>(function StreetWind({ wind, ...props }, ref) {\n const layer = usePart(makeStreetWind, props, ref);\n const [u, v] = wind ?? [Number.NaN, Number.NaN];\n\n // The wind given: the flow round the buildings turned to it, not computed again\n useEffect(() => {\n if (layer && Number.isFinite(u) && Number.isFinite(v)) layer.setWind([u, v]);\n }, [layer, u, v]);\n\n return null;\n});\n\n/** The trees of the vector tiles around on their woods, forests and grass, as 3D models (`VectorTiles.trees`: its\n * options as props) */\nexport const Trees = TreesComponent as (props: PartProps<TreesOptions, ModelLayer>) => ReactNode;\n\n/** Street lamps along the streets of the vector tiles around, lit at night (`VectorTiles.lamps`) */\nexport const Lamps = LampsComponent as (props: PartProps<LampsOptions, ModelLayer>) => ReactNode;\n\n/** The street furniture of the vector tiles around: bus shelters, waste baskets, post boxes, fountains, bicycle racks,\n * benches (`VectorTiles.furniture`) */\nexport const Furniture = FurnitureComponent as (props: PartProps<FurnitureOptions, ModelLayer>) => ReactNode;\n\n/** The labels of the vector tiles around (`VectorTiles.labels`): hidden by the buildings of a `<Geometry>` beside\n * them */\nexport const Labels = LabelsComponent as (props: PartProps<LabelsOptions, SymbolLayer<LabelCandidate>>) => ReactNode;\n\n/** The buildings of the vector tiles around as an extrusion layer, its style per feature (`VectorTiles.extrusions`:\n * its options as props; functions compared by identity, so at the module's level or in a memo). The properties of\n * its features named by `<Extrusions<Building> …>` */\nexport const Extrusions = ExtrusionsComponent as <P = Record<string, unknown>>(\n props: PartProps<VectorExtrusionOptions<P>, ExtrusionLayer<P>>,\n) => ReactNode;\n\n/** The wind in the streets of the vector tiles around, round their buildings (`VectorTiles.streetWind`: its options as\n * props); `wind` the wind above the town, `[u, v]` m/s, the flow turned to it as it changes */\nexport const StreetWind = StreetWindComponent as (\n props: PartProps<StreetWindOptions & { wind?: [u: number, v: number] }, StreetWindLayer>,\n) => ReactNode;\n\n/** The fills, lines and circles of the vector tiles around drawn into images over the ground (`VectorTiles.raster`): the\n * light way over the relief, under 3D buildings (their own drawn as geometry by a `<Geometry>`, or none) */\nexport const Raster = RasterComponent as (props: PartProps<VectorRasterOptions, RasterLayer>) => ReactNode;\n"]}
|
package/dist/react/context.d.ts
CHANGED
|
@@ -35,6 +35,12 @@ export declare const useRank: () => number | undefined;
|
|
|
35
35
|
export declare const useMap: () => GeoMap | undefined;
|
|
36
36
|
/** Listens to an event of the map around while the component is mounted; the listener may change at each render */
|
|
37
37
|
export declare const useMapEvent: <K extends keyof MapEvents>(type: K, listener: (event: MapEvents[K]) => void) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Calls `callback` after each `type` event of the map around (`'render'` by default: an overlay of your own over each
|
|
40
|
+
* frame; `'idle'`: once what is in view is loaded), and at once whenever it changes (its function from a `useCallback`
|
|
41
|
+
* of what it shows), the camera still
|
|
42
|
+
*/
|
|
43
|
+
export declare const useMapRedraw: (callback: () => void, type?: keyof MapEvents) => void;
|
|
38
44
|
/** The entries of an object whose value is defined (`exactOptionalPropertyTypes`) */
|
|
39
45
|
export declare const defined: <T extends object>(object: T) => { [K in keyof T]?: Exclude<T[K], undefined>; };
|
|
40
46
|
/** A key of options that changes when they do: plain data compared by value (an object written inline in the JSX
|
package/dist/react/context.js
CHANGED
|
@@ -24,6 +24,19 @@ export const useMapEvent = (type, listener) => {
|
|
|
24
24
|
// Listening while mounted, to the listener of the last render; stops when unmounted or the map changes
|
|
25
25
|
useEffect(() => map?.on(type, (event) => latest.current(event)), [map, type]);
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Calls `callback` after each `type` event of the map around (`'render'` by default: an overlay of your own over each
|
|
29
|
+
* frame; `'idle'`: once what is in view is loaded), and at once whenever it changes (its function from a `useCallback`
|
|
30
|
+
* of what it shows), the camera still
|
|
31
|
+
*/
|
|
32
|
+
export const useMapRedraw = (callback, type = 'render') => {
|
|
33
|
+
const map = useMap();
|
|
34
|
+
useMapEvent(type, callback);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (map)
|
|
37
|
+
callback();
|
|
38
|
+
}, [map, callback]);
|
|
39
|
+
};
|
|
27
40
|
/** The entries of an object whose value is defined (`exactOptionalPropertyTypes`) */
|
|
28
41
|
export const defined = (object) => Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined));
|
|
29
42
|
/** Numbers standing for the values compared by identity in `propsKey`, the next one */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/react/context.ts"],"names":[],"mappings":"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","sourcesContent":["/** What the components of a map share: the map, the order of their layers, the viewsheds, the swipe and the marker\n * around; the hooks */\n\nimport { createContext, useContext, useEffect, useRef, useState } from 'react';\nimport type { VectorTiles } from '../basemap/vectortiles.ts';\nimport type { LayerOrder } from '../bind/layers.ts';\nimport type { Layer as LayerHandle } from '../layers/layers.ts';\nimport type { GeoMap } from '../map.ts';\nimport type { LonLat, MapEvents, ViewshedOptions } from '../types.ts';\nimport type { Marker as MarkerHandle } from '../ui/overlay.ts';\n\nexport interface 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\nexport const MapContext = createContext<Binding | undefined>(undefined);\n\n/** The layers of a `<Swipe>` around, its `<Layer>` children joining it; `other`: the group of its `<SwipeOther>` */\nexport interface SwipeGroup {\n set(layer: LayerHandle): void;\n unset(layer: LayerHandle): void;\n other?: SwipeGroup;\n}\n\nexport const SwipeContext = createContext<SwipeGroup | undefined>(undefined);\n\n/** The marker of a `<Marker>` around and its position (a `<Popup>` in it opens on its click, at that position) */\nexport const MarkerContext = createContext<{ marker: MarkerHandle; lonLat: LonLat } | undefined>(undefined);\n\n/** The vector tiles of the `<VectorTiles>` around, once the map is there */\nexport const VectorTilesContext = createContext<VectorTiles | undefined>(undefined);\n\n/** A component's rank among the layers of the components (`LayerOrder.rank`): taken at its first render, in the\n * declared order, kept after */\nexport const useRank = (): number | undefined => {\n const binding = useContext(MapContext);\n const [rank] = useState(() => binding?.order.rank());\n\n return rank;\n};\n\n/** The map of the `<IsardMap>` around, once created (undefined before) */\nexport const useMap = (): GeoMap | undefined => useContext(MapContext)?.map;\n\n/** Listens to an event of the map around while the component is mounted; the listener may change at each render */\nexport const useMapEvent = <K extends keyof MapEvents>(type: K, listener: (event: MapEvents[K]) => void): void => {\n const map = useMap();\n const latest = useRef(listener);\n\n latest.current = listener;\n // Listening while mounted, to the listener of the last render; stops when unmounted or the map changes\n useEffect(() => map?.on(type, (event) => latest.current(event)), [map, type]);\n};\n\n/** The entries of an object whose value is defined (`exactOptionalPropertyTypes`) */\nexport const defined = <T extends object>(object: T): { [K in keyof T]?: Exclude<T[K], undefined> } =>\n Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined)) as {\n [K in keyof T]?: Exclude<T[K], undefined>;\n };\n\n/** Numbers standing for the values compared by identity in `propsKey`, the next one */\nconst identities = { of: new WeakMap<object, number>(), next: 1 };\n\n/** The number of a value compared by identity */\nconst identity = (value: object): number => {\n const known = identities.of.get(value);\n\n if (known !== undefined) return known;\n identities.of.set(value, identities.next);\n identities.next += 1;\n\n return identities.next - 1;\n};\n\n/** A value compared by identity: a function, a class's instance (a layer), binary data */\nconst byIdentity = (value: unknown): value is object =>\n typeof value === 'function' ||\n (typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) !== Object.prototype &&\n Object.getPrototypeOf(value) !== null);\n\n/** A key of options that changes when they do: plain data compared by value (an object written inline in the JSX\n * keeps its key), functions, layers and binary data by identity */\nexport const propsKey = (options: object): string =>\n JSON.stringify(options, (_, value: unknown) => (byIdentity(value) ? `#${identity(value)}` : value));\n\n/** The same options while their `propsKey` stays: an object written inline in the JSX, the same from render to render\n * until a value in it changes */\nexport const useStableOptions = <T extends object>(options: T): T => {\n const kept = useRef({ key: propsKey(options), options });\n const key = propsKey(options);\n\n if (kept.current.key !== key) kept.current = { key, options };\n\n return kept.current.options;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/react/context.ts"],"names":[],"mappings":"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","sourcesContent":["/** What the components of a map share: the map, the order of their layers, the viewsheds, the swipe and the marker\n * around; the hooks */\n\nimport { createContext, useContext, useEffect, useRef, useState } from 'react';\nimport type { VectorTiles } from '../basemap/vectortiles.ts';\nimport type { LayerOrder } from '../bind/layers.ts';\nimport type { Layer as LayerHandle } from '../layers/layers.ts';\nimport type { GeoMap } from '../map.ts';\nimport type { LonLat, MapEvents, ViewshedOptions } from '../types.ts';\nimport type { Marker as MarkerHandle } from '../ui/overlay.ts';\n\nexport interface 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\nexport const MapContext = createContext<Binding | undefined>(undefined);\n\n/** The layers of a `<Swipe>` around, its `<Layer>` children joining it; `other`: the group of its `<SwipeOther>` */\nexport interface SwipeGroup {\n set(layer: LayerHandle): void;\n unset(layer: LayerHandle): void;\n other?: SwipeGroup;\n}\n\nexport const SwipeContext = createContext<SwipeGroup | undefined>(undefined);\n\n/** The marker of a `<Marker>` around and its position (a `<Popup>` in it opens on its click, at that position) */\nexport const MarkerContext = createContext<{ marker: MarkerHandle; lonLat: LonLat } | undefined>(undefined);\n\n/** The vector tiles of the `<VectorTiles>` around, once the map is there */\nexport const VectorTilesContext = createContext<VectorTiles | undefined>(undefined);\n\n/** A component's rank among the layers of the components (`LayerOrder.rank`): taken at its first render, in the\n * declared order, kept after */\nexport const useRank = (): number | undefined => {\n const binding = useContext(MapContext);\n const [rank] = useState(() => binding?.order.rank());\n\n return rank;\n};\n\n/** The map of the `<IsardMap>` around, once created (undefined before) */\nexport const useMap = (): GeoMap | undefined => useContext(MapContext)?.map;\n\n/** Listens to an event of the map around while the component is mounted; the listener may change at each render */\nexport const useMapEvent = <K extends keyof MapEvents>(type: K, listener: (event: MapEvents[K]) => void): void => {\n const map = useMap();\n const latest = useRef(listener);\n\n latest.current = listener;\n // Listening while mounted, to the listener of the last render; stops when unmounted or the map changes\n useEffect(() => map?.on(type, (event) => latest.current(event)), [map, type]);\n};\n\n/**\n * Calls `callback` after each `type` event of the map around (`'render'` by default: an overlay of your own over each\n * frame; `'idle'`: once what is in view is loaded), and at once whenever it changes (its function from a `useCallback`\n * of what it shows), the camera still\n */\nexport const useMapRedraw = (callback: () => void, type: keyof MapEvents = 'render'): void => {\n const map = useMap();\n\n useMapEvent(type, callback);\n useEffect(() => {\n if (map) callback();\n }, [map, callback]);\n};\n\n/** The entries of an object whose value is defined (`exactOptionalPropertyTypes`) */\nexport const defined = <T extends object>(object: T): { [K in keyof T]?: Exclude<T[K], undefined> } =>\n Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined)) as {\n [K in keyof T]?: Exclude<T[K], undefined>;\n };\n\n/** Numbers standing for the values compared by identity in `propsKey`, the next one */\nconst identities = { of: new WeakMap<object, number>(), next: 1 };\n\n/** The number of a value compared by identity */\nconst identity = (value: object): number => {\n const known = identities.of.get(value);\n\n if (known !== undefined) return known;\n identities.of.set(value, identities.next);\n identities.next += 1;\n\n return identities.next - 1;\n};\n\n/** A value compared by identity: a function, a class's instance (a layer), binary data */\nconst byIdentity = (value: unknown): value is object =>\n typeof value === 'function' ||\n (typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) !== Object.prototype &&\n Object.getPrototypeOf(value) !== null);\n\n/** A key of options that changes when they do: plain data compared by value (an object written inline in the JSX\n * keeps its key), functions, layers and binary data by identity */\nexport const propsKey = (options: object): string =>\n JSON.stringify(options, (_, value: unknown) => (byIdentity(value) ? `#${identity(value)}` : value));\n\n/** The same options while their `propsKey` stays: an object written inline in the JSX, the same from render to render\n * until a value in it changes */\nexport const useStableOptions = <T extends object>(options: T): T => {\n const kept = useRef({ key: propsKey(options), options });\n const key = propsKey(options);\n\n if (kept.current.key !== key) kept.current = { key, options };\n\n return kept.current.options;\n};\n"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -22,13 +22,16 @@ export type { Camera } from '../wasm/isard_webgpu.js';
|
|
|
22
22
|
export { ClipVolume, type ClipVolumeProps } from './ClipVolume.tsx';
|
|
23
23
|
export { Control, type ControlProps } from './Control.tsx';
|
|
24
24
|
export { CustomLayer, type CustomLayerProps } from './CustomLayer.tsx';
|
|
25
|
-
export { useMap, useMapEvent } from './context.ts';
|
|
25
|
+
export { useMap, useMapEvent, useMapRedraw } from './context.ts';
|
|
26
|
+
export { Ifc, type IfcProps, useFrame } from './Ifc.tsx';
|
|
26
27
|
export { IsardMap, type IsardMapProps, type MapEventProps } from './IsardMap.tsx';
|
|
27
28
|
export { Layer, type LayerHandles, type LayerProps, type LayerType } from './Layer.tsx';
|
|
28
29
|
export { Marker, type MarkerProps } from './Marker.tsx';
|
|
29
30
|
export { Panel, type PanelProps } from './Panel.tsx';
|
|
30
31
|
export { Popup, type PopupProps } from './Popup.tsx';
|
|
32
|
+
export { FirstPerson, Panoramas, type PanoramasProps, Photo, type PhotoProps } from './Street.tsx';
|
|
31
33
|
export { Swipe, SwipeOther, type SwipeProps } from './Swipe.tsx';
|
|
34
|
+
export { collada, geojson, gpx, type Source, source, useLoaded, useLoadedEvent, useSource, wfs, windGrids, } from './sources.ts';
|
|
32
35
|
export { Geometry, type GeometryProps, VectorGeometry, type VectorGeometryProps } from './VectorGeometry.tsx';
|
|
33
|
-
export { Extrusions, Furniture, Labels, Lamps, Raster, Trees, useVectorTiles, VectorTiles, type VectorTilesProps, } from './VectorTiles.tsx';
|
|
36
|
+
export { Extrusions, Furniture, Labels, Lamps, Raster, StreetWind, Trees, useVectorTiles, VectorTiles, type VectorTilesProps, } from './VectorTiles.tsx';
|
|
34
37
|
export { Viewshed } from './Viewshed.tsx';
|
package/dist/react/index.js
CHANGED
|
@@ -17,14 +17,17 @@
|
|
|
17
17
|
export { ClipVolume } from "./ClipVolume.js";
|
|
18
18
|
export { Control } from "./Control.js";
|
|
19
19
|
export { CustomLayer } from "./CustomLayer.js";
|
|
20
|
-
export { useMap, useMapEvent } from "./context.js";
|
|
20
|
+
export { useMap, useMapEvent, useMapRedraw } from "./context.js";
|
|
21
|
+
export { Ifc, useFrame } from "./Ifc.js";
|
|
21
22
|
export { IsardMap } from "./IsardMap.js";
|
|
22
23
|
export { Layer } from "./Layer.js";
|
|
23
24
|
export { Marker } from "./Marker.js";
|
|
24
25
|
export { Panel } from "./Panel.js";
|
|
25
26
|
export { Popup } from "./Popup.js";
|
|
27
|
+
export { FirstPerson, Panoramas, Photo } from "./Street.js";
|
|
26
28
|
export { Swipe, SwipeOther } from "./Swipe.js";
|
|
29
|
+
export { collada, geojson, gpx, source, useLoaded, useLoadedEvent, useSource, wfs, windGrids, } from "./sources.js";
|
|
27
30
|
export { Geometry, VectorGeometry } from "./VectorGeometry.js";
|
|
28
|
-
export { Extrusions, Furniture, Labels, Lamps, Raster, Trees, useVectorTiles, VectorTiles, } from "./VectorTiles.js";
|
|
31
|
+
export { Extrusions, Furniture, Labels, Lamps, Raster, StreetWind, Trees, useVectorTiles, VectorTiles, } from "./VectorTiles.js";
|
|
29
32
|
export { Viewshed } from "./Viewshed.js";
|
|
30
33
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAkB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAqB,MAAM,cAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAmB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAkB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAqB,MAAM,cAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAmB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,GAAG,EAAiB,QAAQ,EAAE,MAAM,UAAW,CAAC;AACzD,OAAO,EAAE,QAAQ,EAA0C,MAAM,eAAgB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAsD,MAAM,YAAa,CAAC;AACxF,OAAO,EAAE,MAAM,EAAoB,MAAM,aAAc,CAAC;AACxD,OAAO,EAAE,KAAK,EAAmB,MAAM,YAAa,CAAC;AACrD,OAAO,EAAE,KAAK,EAAmB,MAAM,YAAa,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAuB,KAAK,EAAmB,MAAM,aAAc,CAAC;AACnG,OAAO,EAAE,KAAK,EAAE,UAAU,EAAmB,MAAM,YAAa,CAAC;AACjE,OAAO,EACL,OAAO,EACP,OAAO,EACP,GAAG,EAEH,MAAM,EACN,SAAS,EACT,cAAc,EACd,SAAS,EACT,GAAG,EACH,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAsB,cAAc,EAA4B,MAAM,qBAAsB,CAAC;AAC9G,OAAO,EACL,UAAU,EACV,SAAS,EACT,MAAM,EACN,KAAK,EACL,MAAM,EACN,UAAU,EACV,KAAK,EACL,cAAc,EACd,WAAW,GAEZ,MAAM,kBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAgB,CAAC","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 * <VectorTiles style={OPENFREEMAP_STYLES.liberty}>\n * <Geometry roofs=\"generated\" />\n * <Trees />\n * <Labels />\n * </VectorTiles>\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\nexport type { Layer as LayerHandle } from '../layers/layers.ts';\nexport type { GeoMap, LayerSpec } from '../map.ts';\nexport type { LonLat } from '../types.ts';\nexport type { Marker as MarkerHandle, Panel as PanelHandle, Popup as PopupHandle } from '../ui/overlay.ts';\nexport type { Camera } from '../wasm/isard_webgpu.js';\nexport { ClipVolume, type ClipVolumeProps } from './ClipVolume.tsx';\nexport { Control, type ControlProps } from './Control.tsx';\nexport { CustomLayer, type CustomLayerProps } from './CustomLayer.tsx';\nexport { useMap, useMapEvent, useMapRedraw } from './context.ts';\nexport { Ifc, type IfcProps, useFrame } from './Ifc.tsx';\nexport { IsardMap, type IsardMapProps, type MapEventProps } from './IsardMap.tsx';\nexport { Layer, type LayerHandles, type LayerProps, type LayerType } from './Layer.tsx';\nexport { Marker, type MarkerProps } from './Marker.tsx';\nexport { Panel, type PanelProps } from './Panel.tsx';\nexport { Popup, type PopupProps } from './Popup.tsx';\nexport { FirstPerson, Panoramas, type PanoramasProps, Photo, type PhotoProps } from './Street.tsx';\nexport { Swipe, SwipeOther, type SwipeProps } from './Swipe.tsx';\nexport {\n collada,\n geojson,\n gpx,\n type Source,\n source,\n useLoaded,\n useLoadedEvent,\n useSource,\n wfs,\n windGrids,\n} from './sources.ts';\nexport { Geometry, type GeometryProps, VectorGeometry, type VectorGeometryProps } from './VectorGeometry.tsx';\nexport {\n Extrusions,\n Furniture,\n Labels,\n Lamps,\n Raster,\n StreetWind,\n Trees,\n useVectorTiles,\n VectorTiles,\n type VectorTilesProps,\n} from './VectorTiles.tsx';\nexport { Viewshed } from './Viewshed.tsx';\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a component loads, declared rather than written as an effect: `useSource(wfs(typeName, box))` gives the
|
|
3
|
+
* Géoplateforme's features once fetched (undefined before), to give to a `<Layer>`'s `data`; `geojson(url)`,
|
|
4
|
+
* `gpx(url)`, `collada(url)`, `windGrids(url)` the same, `useLoaded(key, load)` anything else. Loaded again only when
|
|
5
|
+
* the source changes (its key), given up when the component goes; React's strict mode, mounting twice, loads once what
|
|
6
|
+
* it keeps. A source is plain data compared by its key: written inline at each render, it loads nothing again.
|
|
7
|
+
*/
|
|
8
|
+
import type * as Grids from '../grids.ts';
|
|
9
|
+
import type { FeatureCollection, Properties } from '../layers/features.ts';
|
|
10
|
+
import type { TimedField } from '../layers/flow.ts';
|
|
11
|
+
import type { GeoMap } from '../map.ts';
|
|
12
|
+
/** Something a component loads: `$source` tells two sources apart (the same key, the same thing loaded), `load` loads
|
|
13
|
+
* it, given up when `signal` aborts; `map` the map it needs (loaded inside an `<IsardMap>` then, once the map is there) */
|
|
14
|
+
export interface Source<T> {
|
|
15
|
+
readonly $source: string;
|
|
16
|
+
readonly map?: true;
|
|
17
|
+
load(signal: AbortSignal, map: GeoMap | undefined): Promise<T>;
|
|
18
|
+
}
|
|
19
|
+
/** A source of its own: `key` the same for the same thing loaded (its URL and options, say) */
|
|
20
|
+
export declare const source: <T>(key: string, load: (signal: AbortSignal) => Promise<T>) => Source<T>;
|
|
21
|
+
/** A GeoJSON file */
|
|
22
|
+
export declare const geojson: <P = Properties>(url: string) => Source<FeatureCollection<P>>;
|
|
23
|
+
/** A GPX file's tracks, routes and waypoints (`parseGpx`) */
|
|
24
|
+
export declare const gpx: <P = Properties>(url: string) => Source<FeatureCollection<P>>;
|
|
25
|
+
/** The Géoplateforme's features of a type in a box (`geoplateformeWfs`) */
|
|
26
|
+
export declare const wfs: <P = Properties>(typeName: string, box: [west: number, south: number, east: number, north: number], options?: {
|
|
27
|
+
count?: number;
|
|
28
|
+
srsName?: string;
|
|
29
|
+
}) => Source<FeatureCollection<P>>;
|
|
30
|
+
/** A COLLADA model read into a glTF binary (`loadCollada`): a models layer's `data` */
|
|
31
|
+
export declare const collada: (url: string) => Source<Uint8Array<ArrayBuffer>>;
|
|
32
|
+
/** The wind of a GRIB2 or NetCDF file (`map.readGrids`, `windFields`): a flow layer's `fields`, read by the map's
|
|
33
|
+
* workers (inside an `<IsardMap>`) */
|
|
34
|
+
export declare const windGrids: (url: string, options?: Parameters<typeof Grids.windFields>[1]) => Source<TimedField[]>;
|
|
35
|
+
/**
|
|
36
|
+
* What `load` gives, once loaded (undefined before, or when it failed: `onError`). Loaded again when `key` changes (a
|
|
37
|
+
* string, or an object compared by identity: `useStableOptions`'), given up when the component goes (its signal
|
|
38
|
+
* aborted), what it gave let go then by `dispose`; nothing loaded while `key` is undefined
|
|
39
|
+
*/
|
|
40
|
+
export declare const useLoaded: <T>(key: unknown, load: (signal: AbortSignal) => Promise<T>, { onError, dispose }?: {
|
|
41
|
+
onError?: (error: unknown) => void;
|
|
42
|
+
dispose?: (value: T) => void;
|
|
43
|
+
}) => T | undefined;
|
|
44
|
+
/** What a source loads, once loaded (undefined before; none without a source). A source needing the map takes the one
|
|
45
|
+
* around, or `map` (outside an `<IsardMap>`: the one its `onLoad` gave); a failure given to `onError` */
|
|
46
|
+
export declare const useSource: <T>(source: Source<T> | undefined, options?: {
|
|
47
|
+
map?: GeoMap | undefined;
|
|
48
|
+
onError?: (error: unknown) => void;
|
|
49
|
+
}) => T | undefined;
|
|
50
|
+
/** Calls `listener` once with each value loaded (the listener may change at each render) */
|
|
51
|
+
export declare const useLoadedEvent: <T>(value: T | undefined, listener: ((value: T) => void) | undefined) => void;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a component loads, declared rather than written as an effect: `useSource(wfs(typeName, box))` gives the
|
|
3
|
+
* Géoplateforme's features once fetched (undefined before), to give to a `<Layer>`'s `data`; `geojson(url)`,
|
|
4
|
+
* `gpx(url)`, `collada(url)`, `windGrids(url)` the same, `useLoaded(key, load)` anything else. Loaded again only when
|
|
5
|
+
* the source changes (its key), given up when the component goes; React's strict mode, mounting twice, loads once what
|
|
6
|
+
* it keeps. A source is plain data compared by its key: written inline at each render, it loads nothing again.
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect, useRef, useState } from 'react';
|
|
9
|
+
import { useMap } from "./context.js";
|
|
10
|
+
/** A source of its own: `key` the same for the same thing loaded (its URL and options, say) */
|
|
11
|
+
export const source = (key, load) => ({
|
|
12
|
+
$source: key,
|
|
13
|
+
load,
|
|
14
|
+
});
|
|
15
|
+
/** A GeoJSON file */
|
|
16
|
+
export const geojson = (url) => source(`geojson:${url}`, async (signal) => {
|
|
17
|
+
const response = await fetch(url, { signal });
|
|
18
|
+
if (!response.ok)
|
|
19
|
+
throw new Error(`${url}: ${response.status}`);
|
|
20
|
+
return response.json();
|
|
21
|
+
});
|
|
22
|
+
/** A GPX file's tracks, routes and waypoints (`parseGpx`) */
|
|
23
|
+
export const gpx = (url) => source(`gpx:${url}`, async (signal) => {
|
|
24
|
+
const [{ parseGpx }, response] = await Promise.all([import("../data/gpx.js"), fetch(url, { signal })]);
|
|
25
|
+
if (!response.ok)
|
|
26
|
+
throw new Error(`${url}: ${response.status}`);
|
|
27
|
+
return parseGpx(await response.text());
|
|
28
|
+
});
|
|
29
|
+
/** The Géoplateforme's features of a type in a box (`geoplateformeWfs`) */
|
|
30
|
+
export const wfs = (typeName, box, options = {}) => source(`wfs:${JSON.stringify([typeName, box, options])}`, async (signal) => {
|
|
31
|
+
const { geoplateformeWfs } = await import("../geoplateforme.js");
|
|
32
|
+
return geoplateformeWfs(typeName, box, { ...options, signal });
|
|
33
|
+
});
|
|
34
|
+
/** A COLLADA model read into a glTF binary (`loadCollada`): a models layer's `data` */
|
|
35
|
+
export const collada = (url) => source(`collada:${url}`, async (signal) => {
|
|
36
|
+
const { loadCollada } = await import("../data/collada.js");
|
|
37
|
+
return loadCollada(url, { signal });
|
|
38
|
+
});
|
|
39
|
+
/** The wind of a GRIB2 or NetCDF file (`map.readGrids`, `windFields`): a flow layer's `fields`, read by the map's
|
|
40
|
+
* workers (inside an `<IsardMap>`) */
|
|
41
|
+
export const windGrids = (url, options = {}) => ({
|
|
42
|
+
$source: `wind:${url}:${JSON.stringify(options)}`,
|
|
43
|
+
map: true,
|
|
44
|
+
load: async (_, map) => {
|
|
45
|
+
if (!map)
|
|
46
|
+
throw new Error('windGrids: no map');
|
|
47
|
+
const { windFields } = await import("../grids.js");
|
|
48
|
+
return windFields(await map.readGrids(url), options);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* What `load` gives, once loaded (undefined before, or when it failed: `onError`). Loaded again when `key` changes (a
|
|
53
|
+
* string, or an object compared by identity: `useStableOptions`'), given up when the component goes (its signal
|
|
54
|
+
* aborted), what it gave let go then by `dispose`; nothing loaded while `key` is undefined
|
|
55
|
+
*/
|
|
56
|
+
export const useLoaded = (key, load, { onError = console.error, dispose } = {}) => {
|
|
57
|
+
const [loaded, setLoaded] = useState();
|
|
58
|
+
// Read when the key changes only: inline functions at each render load nothing again
|
|
59
|
+
const latest = useRef({ load, onError, dispose });
|
|
60
|
+
latest.current = { load, onError, dispose };
|
|
61
|
+
// Loading while mounted and the key the same; a strict mode's first mount aborted, what it gave let go
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (key === undefined)
|
|
64
|
+
return;
|
|
65
|
+
const controller = new AbortController();
|
|
66
|
+
const kept = {};
|
|
67
|
+
const { load, onError, dispose } = latest.current;
|
|
68
|
+
load(controller.signal).then((value) => {
|
|
69
|
+
if (controller.signal.aborted) {
|
|
70
|
+
dispose?.(value);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
kept.value = value;
|
|
74
|
+
setLoaded({ key, value });
|
|
75
|
+
}, (error) => {
|
|
76
|
+
if (!controller.signal.aborted)
|
|
77
|
+
onError(error);
|
|
78
|
+
});
|
|
79
|
+
return () => {
|
|
80
|
+
controller.abort();
|
|
81
|
+
if (kept.value !== undefined)
|
|
82
|
+
dispose?.(kept.value);
|
|
83
|
+
};
|
|
84
|
+
}, [key]);
|
|
85
|
+
return loaded && loaded.key === key ? loaded.value : undefined;
|
|
86
|
+
};
|
|
87
|
+
/** What a source loads, once loaded (undefined before; none without a source). A source needing the map takes the one
|
|
88
|
+
* around, or `map` (outside an `<IsardMap>`: the one its `onLoad` gave); a failure given to `onError` */
|
|
89
|
+
export const useSource = (source, options = {}) => {
|
|
90
|
+
const around = useMap();
|
|
91
|
+
const map = options.map ?? around;
|
|
92
|
+
// A source needing the map waits for it
|
|
93
|
+
return useLoaded(source && (!source.map || map) ? source.$source : undefined, (signal) => source?.load(signal, map) ?? Promise.reject(new Error('no source')), options.onError ? { onError: options.onError } : {});
|
|
94
|
+
};
|
|
95
|
+
/** Calls `listener` once with each value loaded (the listener may change at each render) */
|
|
96
|
+
export const useLoadedEvent = (value, listener) => {
|
|
97
|
+
const latest = useRef(listener);
|
|
98
|
+
latest.current = listener;
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (value !== undefined)
|
|
101
|
+
latest.current?.(value);
|
|
102
|
+
}, [value]);
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/react/sources.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a component loads, declared rather than written as an effect: `useSource(wfs(typeName, box))` gives the\n * Géoplateforme's features once fetched (undefined before), to give to a `<Layer>`'s `data`; `geojson(url)`,\n * `gpx(url)`, `collada(url)`, `windGrids(url)` the same, `useLoaded(key, load)` anything else. Loaded again only when\n * the source changes (its key), given up when the component goes; React's strict mode, mounting twice, loads once what\n * it keeps. A source is plain data compared by its key: written inline at each render, it loads nothing again.\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport type * as Grids from '../grids.ts';\nimport type { FeatureCollection, Properties } from '../layers/features.ts';\nimport type { TimedField } from '../layers/flow.ts';\nimport type { GeoMap } from '../map.ts';\nimport { useMap } from './context.ts';\n\n/** Something a component loads: `$source` tells two sources apart (the same key, the same thing loaded), `load` loads\n * it, given up when `signal` aborts; `map` the map it needs (loaded inside an `<IsardMap>` then, once the map is there) */\nexport interface Source<T> {\n readonly $source: string;\n readonly map?: true;\n load(signal: AbortSignal, map: GeoMap | undefined): Promise<T>;\n}\n\n/** A source of its own: `key` the same for the same thing loaded (its URL and options, say) */\nexport const source = <T>(key: string, load: (signal: AbortSignal) => Promise<T>): Source<T> => ({\n $source: key,\n load,\n});\n\n/** A GeoJSON file */\nexport const geojson = <P = Properties>(url: string): Source<FeatureCollection<P>> =>\n source(`geojson:${url}`, async (signal) => {\n const response = await fetch(url, { signal });\n\n if (!response.ok) throw new Error(`${url}: ${response.status}`);\n\n return response.json();\n });\n\n/** A GPX file's tracks, routes and waypoints (`parseGpx`) */\nexport const gpx = <P = Properties>(url: string): Source<FeatureCollection<P>> =>\n source(`gpx:${url}`, async (signal) => {\n const [{ parseGpx }, response] = await Promise.all([import('../data/gpx.ts'), fetch(url, { signal })]);\n\n if (!response.ok) throw new Error(`${url}: ${response.status}`);\n\n return parseGpx<P>(await response.text());\n });\n\n/** The Géoplateforme's features of a type in a box (`geoplateformeWfs`) */\nexport const wfs = <P = Properties>(\n typeName: string,\n box: [west: number, south: number, east: number, north: number],\n options: { count?: number; srsName?: string } = {},\n): Source<FeatureCollection<P>> =>\n source(`wfs:${JSON.stringify([typeName, box, options])}`, async (signal) => {\n const { geoplateformeWfs } = await import('../geoplateforme.ts');\n\n return geoplateformeWfs<P>(typeName, box, { ...options, signal });\n });\n\n/** A COLLADA model read into a glTF binary (`loadCollada`): a models layer's `data` */\nexport const collada = (url: string): Source<Uint8Array<ArrayBuffer>> =>\n source(`collada:${url}`, async (signal) => {\n const { loadCollada } = await import('../data/collada.ts');\n\n return loadCollada(url, { signal });\n });\n\n/** The wind of a GRIB2 or NetCDF file (`map.readGrids`, `windFields`): a flow layer's `fields`, read by the map's\n * workers (inside an `<IsardMap>`) */\nexport const windGrids = (url: string, options: Parameters<typeof Grids.windFields>[1] = {}): Source<TimedField[]> => ({\n $source: `wind:${url}:${JSON.stringify(options)}`,\n map: true,\n load: async (_, map) => {\n if (!map) throw new Error('windGrids: no map');\n\n const { windFields } = await import('../grids.ts');\n\n return windFields(await map.readGrids(url), options);\n },\n});\n\n/**\n * What `load` gives, once loaded (undefined before, or when it failed: `onError`). Loaded again when `key` changes (a\n * string, or an object compared by identity: `useStableOptions`'), given up when the component goes (its signal\n * aborted), what it gave let go then by `dispose`; nothing loaded while `key` is undefined\n */\nexport const useLoaded = <T>(\n key: unknown,\n load: (signal: AbortSignal) => Promise<T>,\n { onError = console.error, dispose }: { onError?: (error: unknown) => void; dispose?: (value: T) => void } = {},\n): T | undefined => {\n const [loaded, setLoaded] = useState<{ key: unknown; value: T }>();\n // Read when the key changes only: inline functions at each render load nothing again\n const latest = useRef({ load, onError, dispose });\n\n latest.current = { load, onError, dispose };\n // Loading while mounted and the key the same; a strict mode's first mount aborted, what it gave let go\n useEffect(() => {\n if (key === undefined) return;\n\n const controller = new AbortController();\n const kept: { value?: T } = {};\n const { load, onError, dispose } = latest.current;\n\n load(controller.signal).then(\n (value) => {\n if (controller.signal.aborted) {\n dispose?.(value);\n\n return;\n }\n kept.value = value;\n setLoaded({ key, value });\n },\n (error: unknown) => {\n if (!controller.signal.aborted) onError(error);\n },\n );\n\n return () => {\n controller.abort();\n if (kept.value !== undefined) dispose?.(kept.value);\n };\n }, [key]);\n\n return loaded && loaded.key === key ? loaded.value : undefined;\n};\n\n/** What a source loads, once loaded (undefined before; none without a source). A source needing the map takes the one\n * around, or `map` (outside an `<IsardMap>`: the one its `onLoad` gave); a failure given to `onError` */\nexport const useSource = <T>(\n source: Source<T> | undefined,\n options: { map?: GeoMap | undefined; onError?: (error: unknown) => void } = {},\n): T | undefined => {\n const around = useMap();\n const map = options.map ?? around;\n\n // A source needing the map waits for it\n return useLoaded(\n source && (!source.map || map) ? source.$source : undefined,\n (signal) => source?.load(signal, map) ?? Promise.reject(new Error('no source')),\n options.onError ? { onError: options.onError } : {},\n );\n};\n\n/** Calls `listener` once with each value loaded (the listener may change at each render) */\nexport const useLoadedEvent = <T>(value: T | undefined, listener: ((value: T) => void) | undefined): void => {\n const latest = useRef(listener);\n\n latest.current = listener;\n useEffect(() => {\n if (value !== undefined) latest.current?.(value);\n }, [value]);\n};\n"]}
|
package/package.json
CHANGED