@osm-editor-kit/street-imagery-react 0.1.0-alpha.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/README.md +47 -0
- package/dist/MapillaryPanel-QX6NESX7.js +2 -0
- package/dist/PanoramaxPanel-J6ANR6LB.js +2 -0
- package/dist/StreetLevelImagerySelectionOverlay.d.ts +9 -0
- package/dist/StreetLevelImagerySourcesAndLayers.d.ts +33 -0
- package/dist/StreetLevelImageryViewCone.d.ts +11 -0
- package/dist/StreetLevelImageryViewer.d.ts +10 -0
- package/dist/chunk-25UGGXIE.js +25 -0
- package/dist/chunk-BS6LPDMY.js +143 -0
- package/dist/chunk-ZH3TPW4D.js +165 -0
- package/dist/hooks/useAllProviderMapFeatures.d.ts +8 -0
- package/dist/hooks/useAllProviderPhotos.d.ts +9 -0
- package/dist/hooks/useMapViewportBbox.d.ts +9 -0
- package/dist/hooks/usePhotoThumbnails.d.ts +3 -0
- package/dist/hooks/useProviderData.d.ts +5 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +659 -0
- package/dist/panels/MapillaryPanel.d.ts +11 -0
- package/dist/panels/PanoramaxPanel.d.ts +11 -0
- package/dist/streetImageryClick.d.ts +13 -0
- package/dist/types.d.ts +6 -0
- package/dist/useViewerStore.d.ts +13 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# `@osm-editor-kit/street-imagery-react`
|
|
2
|
+
|
|
3
|
+
**Status:** First npm **alpha** (`0.1.0-alpha.0`). Publish with `bun run packages:release -- --publish-only` after `npm login`.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
React MapLibre bindings for street-level imagery: map sources/layers, photo click handling, TanStack Query provider hooks, and lazy Mapillary/Panoramax viewer panels. Data adapters and GeoJSON helpers live in `@osm-editor-kit/street-imagery`.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import {
|
|
13
|
+
StreetLevelImagerySourcesAndLayers,
|
|
14
|
+
StreetLevelImageryViewer,
|
|
15
|
+
useMapViewportBbox,
|
|
16
|
+
queryStreetImageryFeatures,
|
|
17
|
+
streetImageryInteractiveLayerIds,
|
|
18
|
+
} from '@osm-editor-kit/street-imagery-react'
|
|
19
|
+
import { createStreetImageryConfig } from '@osm-editor-kit/street-imagery'
|
|
20
|
+
|
|
21
|
+
const bbox = useMapViewportBbox('main', map)
|
|
22
|
+
|
|
23
|
+
<StreetLevelImagerySourcesAndLayers
|
|
24
|
+
providers={['mapillary', 'panoramax']}
|
|
25
|
+
bbox={bbox}
|
|
26
|
+
zoom={map.zoom}
|
|
27
|
+
filter={{ photoTypes: ['flat', 'pano'] }}
|
|
28
|
+
options={{
|
|
29
|
+
config: createStreetImageryConfig({ mapillaryToken: '…' }),
|
|
30
|
+
showSequences: true,
|
|
31
|
+
showViewfields: true,
|
|
32
|
+
selectedPhoto,
|
|
33
|
+
photoCircleColor: '#3b82f6',
|
|
34
|
+
mapFeatureCircleColor: '#94a3b8',
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
// interactiveLayerIds={streetImageryInteractiveLayerIds(providers)}
|
|
39
|
+
const hits = queryStreetImageryFeatures(event)
|
|
40
|
+
|
|
41
|
+
<StreetLevelImageryViewer photo={selectedPhoto} groupPhotos={sequencePhotos}
|
|
42
|
+
onPhotoSelected={setSelection} onEaseMapToPoint={(lng, lat) => map.easeTo({ center: [lng, lat] })} />
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Mapillary:** `createStreetImageryConfig({ mapillaryToken })` or `setStreetImageryConfig` at boot (`@osm-editor-kit/street-imagery`). **Panoramax + Vite:** see `app/vite.config.ts` for the consuming-app setup.
|
|
46
|
+
|
|
47
|
+
Also: `useAllProviderPhotos`, `useProviderPhotos` / `useProviderSequences` / `useProviderMapFeatures`, `usePhotoThumbnails`, `resolveSelectedSequence`, `StreetLevelImageryViewCone`, `useViewerBearing` / `useViewerActions`, `MapillaryPanel`, `PanoramaxPanel`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Bbox, NormalizedPhoto, NormalizedSequence } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
export type StreetLevelImagerySelectionOverlayProps = {
|
|
3
|
+
selectedPhoto?: NormalizedPhoto | null;
|
|
4
|
+
selectedSequence?: NormalizedSequence | null;
|
|
5
|
+
};
|
|
6
|
+
export declare const StreetLevelImagerySelectionOverlay: ({ selectedPhoto, selectedSequence, }: StreetLevelImagerySelectionOverlayProps) => import("react").JSX.Element | null;
|
|
7
|
+
/** Resolve sequence geometry for highlight when only photo + fetched sequences are available. */
|
|
8
|
+
export declare const resolveSelectedSequence: (selectedPhoto: NormalizedPhoto | null | undefined, sequences: NormalizedSequence[], sequenceId: string | null | undefined) => NormalizedSequence | null;
|
|
9
|
+
export type SequenceHighlightBbox = Bbox | null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type StreetImageryConfig } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import { type DateRange, type PhotoTypeFilter } from '@osm-editor-kit/street-imagery';
|
|
3
|
+
import type { Bbox, NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
4
|
+
import { type ProviderId } from '@osm-editor-kit/street-imagery';
|
|
5
|
+
import type { DataDrivenPropertyValueSpecification } from 'maplibre-gl';
|
|
6
|
+
export type PhotoFilter = {
|
|
7
|
+
photoTypes?: PhotoTypeFilter[];
|
|
8
|
+
date?: DateRange;
|
|
9
|
+
};
|
|
10
|
+
export type StreetLevelImagerySourcesAndLayersProps = {
|
|
11
|
+
providers: ProviderId[];
|
|
12
|
+
filter?: PhotoFilter;
|
|
13
|
+
bbox: Bbox | null;
|
|
14
|
+
zoom: number;
|
|
15
|
+
options: {
|
|
16
|
+
config?: StreetImageryConfig;
|
|
17
|
+
showSequences?: boolean;
|
|
18
|
+
/** Always-on heading wedges / 360° disks for every photo. Default true. */
|
|
19
|
+
showViewfields?: boolean;
|
|
20
|
+
showViewCone?: boolean;
|
|
21
|
+
showSelectionHighlight?: boolean;
|
|
22
|
+
selectedPhoto?: NormalizedPhoto | null;
|
|
23
|
+
selectedSequenceId?: string | null;
|
|
24
|
+
viewerPov?: {
|
|
25
|
+
bearing?: number | null;
|
|
26
|
+
hfov?: number | null;
|
|
27
|
+
lngLat?: [number, number] | null;
|
|
28
|
+
} | null;
|
|
29
|
+
photoCircleColor: DataDrivenPropertyValueSpecification<string>;
|
|
30
|
+
mapFeatureCircleColor: DataDrivenPropertyValueSpecification<string>;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export declare const StreetLevelImagerySourcesAndLayers: ({ providers, filter, bbox, zoom, options, }: StreetLevelImagerySourcesAndLayersProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
export type StreetLevelImageryViewConeProps = {
|
|
3
|
+
selectedPhoto: NormalizedPhoto;
|
|
4
|
+
zoom: number;
|
|
5
|
+
viewerPov?: {
|
|
6
|
+
bearing?: number | null;
|
|
7
|
+
hfov?: number | null;
|
|
8
|
+
lngLat?: [number, number] | null;
|
|
9
|
+
} | null;
|
|
10
|
+
};
|
|
11
|
+
export declare const StreetLevelImageryViewCone: ({ selectedPhoto, zoom, viewerPov, }: StreetLevelImageryViewConeProps) => import("react").JSX.Element | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import type { StreetImageryPhotoSelection } from './types';
|
|
3
|
+
type StreetLevelImageryViewerProps = {
|
|
4
|
+
photo: NormalizedPhoto;
|
|
5
|
+
groupPhotos: NormalizedPhoto[];
|
|
6
|
+
onPhotoSelected: (selection: StreetImageryPhotoSelection) => void;
|
|
7
|
+
onEaseMapToPoint: (lng: number, lat: number) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const StreetLevelImageryViewer: ({ photo, groupPhotos, onPhotoSelected, onEaseMapToPoint, }: StreetLevelImageryViewerProps) => import("react").JSX.Element | null;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
// src/useViewerStore.ts
|
|
4
|
+
var initialState = {
|
|
5
|
+
bearing: null,
|
|
6
|
+
hfov: null,
|
|
7
|
+
lngLat: null
|
|
8
|
+
};
|
|
9
|
+
var useViewerStore = create()((set) => ({
|
|
10
|
+
...initialState,
|
|
11
|
+
actions: {
|
|
12
|
+
setPov: (partial) => set((state) => ({
|
|
13
|
+
bearing: partial.bearing !== void 0 ? partial.bearing : state.bearing,
|
|
14
|
+
hfov: partial.hfov !== void 0 ? partial.hfov : state.hfov,
|
|
15
|
+
lngLat: partial.lngLat !== void 0 ? partial.lngLat : state.lngLat
|
|
16
|
+
})),
|
|
17
|
+
reset: () => set(initialState)
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
var useViewerBearing = () => useViewerStore((state) => state.bearing);
|
|
21
|
+
var useViewerHfov = () => useViewerStore((state) => state.hfov);
|
|
22
|
+
var useViewerLngLat = () => useViewerStore((state) => state.lngLat);
|
|
23
|
+
var useViewerActions = () => useViewerStore((state) => state.actions);
|
|
24
|
+
|
|
25
|
+
export { useViewerActions, useViewerBearing, useViewerHfov, useViewerLngLat };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { useViewerActions } from './chunk-25UGGXIE.js';
|
|
2
|
+
import { getStreetImageryConfig } from '@osm-editor-kit/street-imagery';
|
|
3
|
+
import 'mapillary-js/dist/mapillary.css';
|
|
4
|
+
import { Viewer } from 'mapillary-js';
|
|
5
|
+
import { useRef, useEffect } from 'react';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
var MapillaryPanel = ({
|
|
9
|
+
photo,
|
|
10
|
+
onPhotoSelected,
|
|
11
|
+
onEaseMapToPoint
|
|
12
|
+
}) => {
|
|
13
|
+
const containerRef = useRef(null);
|
|
14
|
+
const viewerRef = useRef(null);
|
|
15
|
+
const navigableRef = useRef(false);
|
|
16
|
+
const pendingImageIdRef = useRef(null);
|
|
17
|
+
const lastViewerPhotoIdRef = useRef(null);
|
|
18
|
+
const bearingRafRef = useRef(null);
|
|
19
|
+
const pendingBearingRef = useRef(null);
|
|
20
|
+
const actions = useViewerActions();
|
|
21
|
+
const initialPhotoIdRef = useRef(photo.photoId);
|
|
22
|
+
useEffect(
|
|
23
|
+
function resetViewerStoreOnProviderChange() {
|
|
24
|
+
return () => {
|
|
25
|
+
actions.reset();
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
[actions, photo.providerId]
|
|
29
|
+
);
|
|
30
|
+
useEffect(
|
|
31
|
+
function mountMapillaryViewer() {
|
|
32
|
+
const container = containerRef.current;
|
|
33
|
+
if (!container) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const viewer = new Viewer({
|
|
37
|
+
accessToken: getStreetImageryConfig().mapillaryToken,
|
|
38
|
+
container,
|
|
39
|
+
imageId: initialPhotoIdRef.current,
|
|
40
|
+
component: {
|
|
41
|
+
cover: false,
|
|
42
|
+
sequence: { visible: true }
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
viewerRef.current = viewer;
|
|
46
|
+
lastViewerPhotoIdRef.current = initialPhotoIdRef.current;
|
|
47
|
+
const flushPendingMove = () => {
|
|
48
|
+
const pendingId = pendingImageIdRef.current;
|
|
49
|
+
if (!pendingId) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
pendingImageIdRef.current = null;
|
|
53
|
+
void viewer.moveTo(pendingId).catch(() => {
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const onNavigable = (event) => {
|
|
57
|
+
navigableRef.current = event.navigable;
|
|
58
|
+
if (event.navigable) {
|
|
59
|
+
flushPendingMove();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const onImage = (event) => {
|
|
63
|
+
const { image } = event;
|
|
64
|
+
lastViewerPhotoIdRef.current = image.id;
|
|
65
|
+
onPhotoSelected({
|
|
66
|
+
provider: "mapillary",
|
|
67
|
+
sequenceId: image.sequenceId,
|
|
68
|
+
photoId: image.id
|
|
69
|
+
});
|
|
70
|
+
const position = image.lngLat ?? image.originalLngLat;
|
|
71
|
+
if (!position) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
actions.setPov({ lngLat: [position.lng, position.lat] });
|
|
75
|
+
onEaseMapToPoint(position.lng, position.lat);
|
|
76
|
+
};
|
|
77
|
+
const flushBearing = () => {
|
|
78
|
+
bearingRafRef.current = null;
|
|
79
|
+
if (pendingBearingRef.current != null) {
|
|
80
|
+
actions.setPov({ bearing: pendingBearingRef.current });
|
|
81
|
+
pendingBearingRef.current = null;
|
|
82
|
+
}
|
|
83
|
+
void viewer.getFieldOfView().then((fov) => {
|
|
84
|
+
actions.setPov({ hfov: fov });
|
|
85
|
+
}).catch(() => {
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
const onBearing = (event) => {
|
|
89
|
+
pendingBearingRef.current = event.bearing;
|
|
90
|
+
if (bearingRafRef.current == null) {
|
|
91
|
+
bearingRafRef.current = requestAnimationFrame(flushBearing);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
viewer.on("navigable", onNavigable);
|
|
95
|
+
viewer.on("image", onImage);
|
|
96
|
+
viewer.on("bearing", onBearing);
|
|
97
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
98
|
+
viewer.resize();
|
|
99
|
+
});
|
|
100
|
+
resizeObserver.observe(container);
|
|
101
|
+
return () => {
|
|
102
|
+
if (bearingRafRef.current != null) {
|
|
103
|
+
cancelAnimationFrame(bearingRafRef.current);
|
|
104
|
+
bearingRafRef.current = null;
|
|
105
|
+
}
|
|
106
|
+
resizeObserver.disconnect();
|
|
107
|
+
viewer.off("navigable", onNavigable);
|
|
108
|
+
viewer.off("image", onImage);
|
|
109
|
+
viewer.off("bearing", onBearing);
|
|
110
|
+
viewer.remove();
|
|
111
|
+
viewerRef.current = null;
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
[actions, onEaseMapToPoint, onPhotoSelected]
|
|
115
|
+
);
|
|
116
|
+
useEffect(
|
|
117
|
+
function syncExternalPhotoSelection() {
|
|
118
|
+
const viewer = viewerRef.current;
|
|
119
|
+
if (!viewer || photo.photoId === lastViewerPhotoIdRef.current) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
actions.reset();
|
|
123
|
+
lastViewerPhotoIdRef.current = photo.photoId;
|
|
124
|
+
if (navigableRef.current) {
|
|
125
|
+
void viewer.moveTo(photo.photoId).catch(() => {
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
pendingImageIdRef.current = photo.photoId;
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
[actions, photo.photoId]
|
|
132
|
+
);
|
|
133
|
+
return /* @__PURE__ */ jsx(
|
|
134
|
+
"div",
|
|
135
|
+
{
|
|
136
|
+
ref: containerRef,
|
|
137
|
+
className: "min-h-48 overflow-hidden rounded-lg border border-slate-200 bg-slate-900",
|
|
138
|
+
style: { aspectRatio: "4 / 3" }
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export { MapillaryPanel };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { useViewerActions } from './chunk-25UGGXIE.js';
|
|
2
|
+
import '@panoramax/web-viewer';
|
|
3
|
+
import { getStreetImageryConfig } from '@osm-editor-kit/street-imagery';
|
|
4
|
+
import { useRef, useEffect } from 'react';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var panoramaxApiEndpoint = () => `${getStreetImageryConfig().panoramaxApiBase}/api`;
|
|
8
|
+
var normalizeBearing = (degrees) => (degrees % 360 + 360) % 360;
|
|
9
|
+
var PanoramaxPanel = ({
|
|
10
|
+
photo,
|
|
11
|
+
onPhotoSelected,
|
|
12
|
+
onEaseMapToPoint
|
|
13
|
+
}) => {
|
|
14
|
+
const containerRef = useRef(null);
|
|
15
|
+
const viewerRef = useRef(null);
|
|
16
|
+
const lastViewerPhotoIdRef = useRef(null);
|
|
17
|
+
const readyRef = useRef(false);
|
|
18
|
+
const pendingSelectRef = useRef(null);
|
|
19
|
+
const photoRef = useRef(photo);
|
|
20
|
+
const bearingRafRef = useRef(null);
|
|
21
|
+
const pendingBearingRef = useRef(null);
|
|
22
|
+
const pendingHfovRef = useRef(null);
|
|
23
|
+
const actions = useViewerActions();
|
|
24
|
+
useEffect(
|
|
25
|
+
function syncPhotoRef() {
|
|
26
|
+
photoRef.current = photo;
|
|
27
|
+
},
|
|
28
|
+
[photo]
|
|
29
|
+
);
|
|
30
|
+
useEffect(
|
|
31
|
+
function resetViewerStoreOnProviderChange() {
|
|
32
|
+
return () => {
|
|
33
|
+
actions.reset();
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
[actions, photo.providerId]
|
|
37
|
+
);
|
|
38
|
+
useEffect(
|
|
39
|
+
function mountPanoramaxViewer() {
|
|
40
|
+
const viewer = viewerRef.current;
|
|
41
|
+
const container = containerRef.current;
|
|
42
|
+
if (!viewer || !container) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
lastViewerPhotoIdRef.current = photoRef.current.photoId;
|
|
46
|
+
const flushBearing = () => {
|
|
47
|
+
bearingRafRef.current = null;
|
|
48
|
+
if (pendingBearingRef.current != null) {
|
|
49
|
+
actions.setPov({ bearing: pendingBearingRef.current });
|
|
50
|
+
pendingBearingRef.current = null;
|
|
51
|
+
}
|
|
52
|
+
if (pendingHfovRef.current != null) {
|
|
53
|
+
actions.setPov({ hfov: pendingHfovRef.current });
|
|
54
|
+
pendingHfovRef.current = null;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const onSelect = (event) => {
|
|
58
|
+
const { seqId, picId } = event.detail;
|
|
59
|
+
if (!picId) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
lastViewerPhotoIdRef.current = picId;
|
|
63
|
+
const currentPhoto = photoRef.current;
|
|
64
|
+
const sequenceId = seqId ?? (picId === currentPhoto.photoId && currentPhoto.sequenceId ? currentPhoto.sequenceId : `photo:${picId}`);
|
|
65
|
+
onPhotoSelected({
|
|
66
|
+
provider: "panoramax",
|
|
67
|
+
sequenceId,
|
|
68
|
+
photoId: picId
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
const onPictureLoaded = (event) => {
|
|
72
|
+
const detail = event.detail;
|
|
73
|
+
if (detail.lon == null || detail.lat == null) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
actions.setPov({ lngLat: [detail.lon, detail.lat] });
|
|
77
|
+
if (detail.x != null) {
|
|
78
|
+
actions.setPov({ bearing: normalizeBearing(detail.x) });
|
|
79
|
+
}
|
|
80
|
+
const psv = viewer.psv;
|
|
81
|
+
if (psv && detail.z != null) {
|
|
82
|
+
actions.setPov({ hfov: psv.dataHelper.zoomLevelToFov(detail.z) });
|
|
83
|
+
}
|
|
84
|
+
onEaseMapToPoint(detail.lon, detail.lat);
|
|
85
|
+
};
|
|
86
|
+
const onViewRotated = (event) => {
|
|
87
|
+
const detail = event.detail;
|
|
88
|
+
pendingBearingRef.current = normalizeBearing(detail.x);
|
|
89
|
+
const psv = viewer.psv;
|
|
90
|
+
if (psv && detail.z != null) {
|
|
91
|
+
pendingHfovRef.current = psv.dataHelper.zoomLevelToFov(detail.z);
|
|
92
|
+
}
|
|
93
|
+
if (bearingRafRef.current == null) {
|
|
94
|
+
bearingRafRef.current = requestAnimationFrame(flushBearing);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
viewer.addEventListener("select", onSelect);
|
|
98
|
+
viewer.addEventListener("psv:picture-loaded", onPictureLoaded);
|
|
99
|
+
viewer.addEventListener("psv:view-rotated", onViewRotated);
|
|
100
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
101
|
+
viewer.psv?.resize();
|
|
102
|
+
});
|
|
103
|
+
resizeObserver.observe(container);
|
|
104
|
+
readyRef.current = true;
|
|
105
|
+
const pending = pendingSelectRef.current;
|
|
106
|
+
if (pending) {
|
|
107
|
+
pendingSelectRef.current = null;
|
|
108
|
+
if (typeof viewer.select === "function") {
|
|
109
|
+
viewer.select(pending.sequenceId ?? null, pending.photoId);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return () => {
|
|
113
|
+
readyRef.current = false;
|
|
114
|
+
if (bearingRafRef.current != null) {
|
|
115
|
+
cancelAnimationFrame(bearingRafRef.current);
|
|
116
|
+
bearingRafRef.current = null;
|
|
117
|
+
}
|
|
118
|
+
resizeObserver.disconnect();
|
|
119
|
+
viewer.removeEventListener("select", onSelect);
|
|
120
|
+
viewer.removeEventListener("psv:picture-loaded", onPictureLoaded);
|
|
121
|
+
viewer.removeEventListener("psv:view-rotated", onViewRotated);
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
[actions, onEaseMapToPoint, onPhotoSelected]
|
|
125
|
+
);
|
|
126
|
+
useEffect(
|
|
127
|
+
function syncExternalPhotoSelection() {
|
|
128
|
+
const viewer = viewerRef.current;
|
|
129
|
+
if (!viewer || photo.photoId === lastViewerPhotoIdRef.current) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
actions.reset();
|
|
133
|
+
lastViewerPhotoIdRef.current = photo.photoId;
|
|
134
|
+
if (!readyRef.current) {
|
|
135
|
+
pendingSelectRef.current = { sequenceId: photo.sequenceId, photoId: photo.photoId };
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (typeof viewer.select === "function") {
|
|
139
|
+
viewer.select(photo.sequenceId ?? null, photo.photoId);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
[actions, photo.photoId, photo.sequenceId]
|
|
143
|
+
);
|
|
144
|
+
return /* @__PURE__ */ jsx(
|
|
145
|
+
"div",
|
|
146
|
+
{
|
|
147
|
+
ref: containerRef,
|
|
148
|
+
className: "min-h-48 overflow-hidden rounded-lg border border-slate-200 bg-slate-900",
|
|
149
|
+
style: { aspectRatio: "4 / 3" },
|
|
150
|
+
children: /* @__PURE__ */ jsx(
|
|
151
|
+
"pnx-photo-viewer",
|
|
152
|
+
{
|
|
153
|
+
ref: viewerRef,
|
|
154
|
+
className: "block h-full w-full",
|
|
155
|
+
endpoint: panoramaxApiEndpoint(),
|
|
156
|
+
"url-parameters": "false",
|
|
157
|
+
picture: photo.photoId,
|
|
158
|
+
sequence: photo.sequenceId ?? void 0
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export { PanoramaxPanel };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DateRange } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import type { Bbox, NormalizedMapFeature } from '@osm-editor-kit/street-imagery';
|
|
3
|
+
import { type ProviderId } from '@osm-editor-kit/street-imagery';
|
|
4
|
+
export declare const useAllProviderMapFeatures: (providerIds: ProviderId[], bbox: Bbox | null, zoom: number, date?: DateRange) => NormalizedMapFeature[];
|
|
5
|
+
export declare const useAllProviderMapFeaturesLoading: (providerIds: ProviderId[], bbox: Bbox | null, zoom: number) => {
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isFetching: boolean;
|
|
8
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DateRange, PhotoTypeFilter } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import type { Bbox, NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
3
|
+
import { type ProviderId } from '@osm-editor-kit/street-imagery';
|
|
4
|
+
export type AllProviderPhotosResult = {
|
|
5
|
+
photos: NormalizedPhoto[];
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isFetching: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const useAllProviderPhotos: (providerIds: ProviderId[], bbox: Bbox | null, zoom: number, photoTypes?: PhotoTypeFilter[], date?: DateRange) => AllProviderPhotosResult;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Bbox } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
/**
|
|
3
|
+
* Live WGS84 bbox from a react-map-gl map instance.
|
|
4
|
+
* @param mapId - react-map-gl `<Map id=…>`
|
|
5
|
+
* @param viewportTrigger - optional value that changes when the camera moves
|
|
6
|
+
* (e.g. URL map param) so the bbox recomputes after pan/zoom without relying
|
|
7
|
+
* on MapLibre events.
|
|
8
|
+
*/
|
|
9
|
+
export declare const useMapViewportBbox: (mapId: string, viewportTrigger?: unknown) => Bbox | null;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
export declare const usePhotoFullUrl: (photo: NormalizedPhoto | null) => import("@tanstack/react-query").UseQueryResult<string | null, Error>;
|
|
3
|
+
export declare const usePhotoThumbnail: (photo: NormalizedPhoto | null) => import("@tanstack/react-query").UseQueryResult<string | null, Error>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Bbox } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import { type ProviderId } from '@osm-editor-kit/street-imagery';
|
|
3
|
+
export declare const useProviderPhotos: (providerId: ProviderId, bbox: Bbox | null, zoom: number) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedPhoto[]>, Error>;
|
|
4
|
+
export declare const useProviderSequences: (providerId: ProviderId, bbox: Bbox | null, zoom: number) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedSequence[]>, Error>;
|
|
5
|
+
export declare const useProviderMapFeatures: (providerId: ProviderId, bbox: Bbox | null, zoom: number) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").NormalizedMapFeature[]>, Error>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { StreetLevelImagerySelectionOverlay, resolveSelectedSequence, } from './StreetLevelImagerySelectionOverlay';
|
|
2
|
+
export { StreetLevelImagerySourcesAndLayers, type PhotoFilter, } from './StreetLevelImagerySourcesAndLayers';
|
|
3
|
+
export { StreetLevelImageryViewCone } from './StreetLevelImageryViewCone';
|
|
4
|
+
export { StreetLevelImageryViewer } from './StreetLevelImageryViewer';
|
|
5
|
+
export type { StreetImageryPhotoSelection } from './types';
|
|
6
|
+
export { queryStreetImageryFeatures, streetImageryInteractiveLayerIds, type StreetImageryClickFeature, } from './streetImageryClick';
|
|
7
|
+
export * from './hooks/useAllProviderMapFeatures';
|
|
8
|
+
export * from './hooks/useAllProviderPhotos';
|
|
9
|
+
export * from './hooks/useMapViewportBbox';
|
|
10
|
+
export * from './hooks/useProviderData';
|
|
11
|
+
export * from './hooks/usePhotoThumbnails';
|
|
12
|
+
export { MapillaryPanel } from './panels/MapillaryPanel';
|
|
13
|
+
export { PanoramaxPanel } from './panels/PanoramaxPanel';
|
|
14
|
+
export * from './useViewerStore';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
export { MapillaryPanel } from './chunk-BS6LPDMY.js';
|
|
2
|
+
export { PanoramaxPanel } from './chunk-ZH3TPW4D.js';
|
|
3
|
+
export { useViewerActions, useViewerBearing, useViewerHfov, useViewerLngLat } from './chunk-25UGGXIE.js';
|
|
4
|
+
import { photosToFeatureCollection, emptyPointCollection, sequencesToFeatureCollection, emptyLineCollection, adapterById, viewConeGeoJson, coneRadiusMeters, setStreetImageryConfig, photoLayerId, viewfieldLayerId, featureLayerId, PROVIDER_IDS, mapFeatureMatchesDateRange, isBrowserAvailableProvider, photoMatchesFilters, resolvePhotoPanoramaUrl, photoFullUrlQueryKey, resolvePhotoThumbnailUrl, photoThumbnailQueryKey, mapFeaturesToFeatureCollection, buildMapFeatureLayerFilter, featureSourceId, photosToViewfieldsFeatureCollection, emptyPolygonCollection, buildPhotoLayerFilter, photoSourceId, viewfieldSourceId, sequenceSourceId, sequenceLayerId, viewfieldLineLayerId } from '@osm-editor-kit/street-imagery';
|
|
5
|
+
import { Source, Layer, useMap } from 'react-map-gl/maplibre';
|
|
6
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
7
|
+
import { lazy, useEffect, Fragment as Fragment$1, Suspense, useState } from 'react';
|
|
8
|
+
import { useQuery } from '@tanstack/react-query';
|
|
9
|
+
|
|
10
|
+
var HIGHLIGHT_SOURCE_ID = "selection-highlight";
|
|
11
|
+
var HIGHLIGHT_LAYER_ID = "selection-highlight-layer";
|
|
12
|
+
var SEQUENCE_HIGHLIGHT_SOURCE_ID = "sequence-highlight";
|
|
13
|
+
var SEQUENCE_HIGHLIGHT_LAYER_ID = "sequence-highlight-layer";
|
|
14
|
+
var StreetLevelImagerySelectionOverlay = ({
|
|
15
|
+
selectedPhoto,
|
|
16
|
+
selectedSequence
|
|
17
|
+
}) => {
|
|
18
|
+
const highlightCollection = selectedPhoto ? photosToFeatureCollection([selectedPhoto]) : emptyPointCollection();
|
|
19
|
+
const sequenceHighlightCollection = selectedSequence ? sequencesToFeatureCollection([selectedSequence]) : emptyLineCollection();
|
|
20
|
+
if (!selectedPhoto && sequenceHighlightCollection.features.length === 0) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
24
|
+
selectedPhoto ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx(Source, { id: HIGHLIGHT_SOURCE_ID, type: "geojson", data: highlightCollection }),
|
|
26
|
+
/* @__PURE__ */ jsx(
|
|
27
|
+
Layer,
|
|
28
|
+
{
|
|
29
|
+
id: HIGHLIGHT_LAYER_ID,
|
|
30
|
+
type: "circle",
|
|
31
|
+
source: HIGHLIGHT_SOURCE_ID,
|
|
32
|
+
paint: {
|
|
33
|
+
"circle-radius": 10,
|
|
34
|
+
"circle-color": "#ffffff",
|
|
35
|
+
"circle-stroke-width": 3,
|
|
36
|
+
"circle-stroke-color": "#0f172a"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
] }) : null,
|
|
41
|
+
sequenceHighlightCollection.features.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42
|
+
/* @__PURE__ */ jsx(
|
|
43
|
+
Source,
|
|
44
|
+
{
|
|
45
|
+
id: SEQUENCE_HIGHLIGHT_SOURCE_ID,
|
|
46
|
+
type: "geojson",
|
|
47
|
+
data: sequenceHighlightCollection
|
|
48
|
+
}
|
|
49
|
+
),
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
Layer,
|
|
52
|
+
{
|
|
53
|
+
id: SEQUENCE_HIGHLIGHT_LAYER_ID,
|
|
54
|
+
type: "line",
|
|
55
|
+
source: SEQUENCE_HIGHLIGHT_SOURCE_ID,
|
|
56
|
+
paint: {
|
|
57
|
+
"line-color": "#0f172a",
|
|
58
|
+
"line-width": 4,
|
|
59
|
+
"line-opacity": 0.75
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
] }) : null
|
|
64
|
+
] });
|
|
65
|
+
};
|
|
66
|
+
var resolveSelectedSequence = (selectedPhoto, sequences, sequenceId) => {
|
|
67
|
+
if (!selectedPhoto || !sequenceId) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return sequences.find(
|
|
71
|
+
(sequence) => sequence.providerId === selectedPhoto.providerId && sequence.sequenceId === sequenceId
|
|
72
|
+
) ?? null;
|
|
73
|
+
};
|
|
74
|
+
var bboxKey = (bbox) => bbox ? `${bbox[0]},${bbox[1]},${bbox[2]},${bbox[3]}` : "none";
|
|
75
|
+
var withinBbox = (bbox) => (item) => {
|
|
76
|
+
const [lng, lat] = item.lngLat;
|
|
77
|
+
return lng >= bbox[0] && lng <= bbox[2] && lat >= bbox[1] && lat <= bbox[3];
|
|
78
|
+
};
|
|
79
|
+
var useProviderPhotos = (providerId, bbox, zoom) => {
|
|
80
|
+
const adapter = adapterById[providerId];
|
|
81
|
+
const enabled = adapter.kind === "photo" && bbox != null && zoom >= adapter.minZoom && adapter.fetchPhotos != null;
|
|
82
|
+
return useQuery({
|
|
83
|
+
queryKey: ["provider-photos", providerId, bboxKey(bbox), zoom],
|
|
84
|
+
queryFn: async ({ signal }) => {
|
|
85
|
+
const photos = await adapter.fetchPhotos(bbox, zoom, signal);
|
|
86
|
+
return photos.filter(withinBbox(bbox));
|
|
87
|
+
},
|
|
88
|
+
enabled,
|
|
89
|
+
placeholderData: (previous) => previous
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
var useProviderSequences = (providerId, bbox, zoom) => {
|
|
93
|
+
const adapter = adapterById[providerId];
|
|
94
|
+
const sequencesMinZoom = adapter.sequencesMinZoom ?? adapter.minZoom;
|
|
95
|
+
const enabled = adapter.kind === "photo" && bbox != null && zoom >= sequencesMinZoom && adapter.fetchSequences != null;
|
|
96
|
+
return useQuery({
|
|
97
|
+
queryKey: ["provider-sequences", providerId, bboxKey(bbox), zoom],
|
|
98
|
+
queryFn: ({ signal }) => adapter.fetchSequences?.(bbox, zoom, signal) ?? [],
|
|
99
|
+
enabled,
|
|
100
|
+
placeholderData: (previous) => previous
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
var useProviderMapFeatures = (providerId, bbox, zoom) => {
|
|
104
|
+
const adapter = adapterById[providerId];
|
|
105
|
+
const enabled = adapter.kind === "mapFeature" && bbox != null && zoom >= adapter.minZoom && adapter.fetchMapFeatures != null;
|
|
106
|
+
return useQuery({
|
|
107
|
+
queryKey: ["provider-map-features", providerId, bboxKey(bbox), zoom],
|
|
108
|
+
queryFn: async ({ signal }) => {
|
|
109
|
+
const features = await adapter.fetchMapFeatures(bbox, zoom, signal);
|
|
110
|
+
return features.filter(withinBbox(bbox));
|
|
111
|
+
},
|
|
112
|
+
enabled,
|
|
113
|
+
placeholderData: (previous) => previous
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
var CONE_SOURCE_ID = "view-direction-cone";
|
|
117
|
+
var CONE_FILL_LAYER_ID = "view-direction-cone-fill";
|
|
118
|
+
var CONE_LINE_LAYER_ID = "view-direction-cone-line";
|
|
119
|
+
var INTERACTIVE_PANO_PROVIDERS = /* @__PURE__ */ new Set([
|
|
120
|
+
"mapillary",
|
|
121
|
+
"panoramax",
|
|
122
|
+
"streetside",
|
|
123
|
+
"kartaview",
|
|
124
|
+
"mapilio",
|
|
125
|
+
"vegbilder"
|
|
126
|
+
]);
|
|
127
|
+
var StreetLevelImageryViewCone = ({
|
|
128
|
+
selectedPhoto,
|
|
129
|
+
zoom,
|
|
130
|
+
viewerPov
|
|
131
|
+
}) => {
|
|
132
|
+
const apex = viewerPov?.lngLat ?? selectedPhoto.lngLat;
|
|
133
|
+
const isPano = selectedPhoto.isPano === true;
|
|
134
|
+
const hasLiveBearing = isPano && INTERACTIVE_PANO_PROVIDERS.has(selectedPhoto.providerId);
|
|
135
|
+
let bearing = null;
|
|
136
|
+
let fov = 30;
|
|
137
|
+
if (isPano) {
|
|
138
|
+
bearing = hasLiveBearing ? viewerPov?.bearing ?? selectedPhoto.heading : selectedPhoto.heading;
|
|
139
|
+
fov = hasLiveBearing ? viewerPov?.hfov ?? 60 : 60;
|
|
140
|
+
} else if (selectedPhoto.providerId === "panoramax") {
|
|
141
|
+
bearing = viewerPov?.bearing ?? selectedPhoto.heading;
|
|
142
|
+
fov = 30;
|
|
143
|
+
} else {
|
|
144
|
+
bearing = selectedPhoto.heading;
|
|
145
|
+
fov = 30;
|
|
146
|
+
}
|
|
147
|
+
if (bearing == null) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const coneFeature = viewConeGeoJson(apex, bearing, fov, coneRadiusMeters(zoom));
|
|
151
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
152
|
+
/* @__PURE__ */ jsx(Source, { id: CONE_SOURCE_ID, type: "geojson", data: coneFeature }),
|
|
153
|
+
/* @__PURE__ */ jsx(
|
|
154
|
+
Layer,
|
|
155
|
+
{
|
|
156
|
+
id: CONE_FILL_LAYER_ID,
|
|
157
|
+
type: "fill",
|
|
158
|
+
source: CONE_SOURCE_ID,
|
|
159
|
+
paint: {
|
|
160
|
+
"fill-color": "#0f172a",
|
|
161
|
+
"fill-opacity": 0.15
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
),
|
|
165
|
+
/* @__PURE__ */ jsx(
|
|
166
|
+
Layer,
|
|
167
|
+
{
|
|
168
|
+
id: CONE_LINE_LAYER_ID,
|
|
169
|
+
type: "line",
|
|
170
|
+
source: CONE_SOURCE_ID,
|
|
171
|
+
paint: {
|
|
172
|
+
"line-color": "#0f172a",
|
|
173
|
+
"line-width": 1.5,
|
|
174
|
+
"line-opacity": 0.5
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
] });
|
|
179
|
+
};
|
|
180
|
+
var CIRCLE_RADIUS = [
|
|
181
|
+
"interpolate",
|
|
182
|
+
["linear"],
|
|
183
|
+
["zoom"],
|
|
184
|
+
10,
|
|
185
|
+
2,
|
|
186
|
+
14,
|
|
187
|
+
4,
|
|
188
|
+
18,
|
|
189
|
+
6
|
|
190
|
+
];
|
|
191
|
+
var FEATURE_CIRCLE_RADIUS = [
|
|
192
|
+
"interpolate",
|
|
193
|
+
["linear"],
|
|
194
|
+
["zoom"],
|
|
195
|
+
10,
|
|
196
|
+
1.5,
|
|
197
|
+
14,
|
|
198
|
+
3,
|
|
199
|
+
18,
|
|
200
|
+
4
|
|
201
|
+
];
|
|
202
|
+
var PHOTO_SORT_KEY = ["coalesce", ["get", "capturedAt"], 0];
|
|
203
|
+
var FEATURE_SORT_KEY = ["coalesce", ["get", "lastSeenAt"], 0];
|
|
204
|
+
var PhotoProviderLayer = ({
|
|
205
|
+
providerId,
|
|
206
|
+
bbox,
|
|
207
|
+
zoom,
|
|
208
|
+
filter,
|
|
209
|
+
showSequences,
|
|
210
|
+
showViewfields,
|
|
211
|
+
photoCircleColor
|
|
212
|
+
}) => {
|
|
213
|
+
const adapter = adapterById[providerId];
|
|
214
|
+
const { data: photos = [] } = useProviderPhotos(providerId, bbox, zoom);
|
|
215
|
+
const { data: sequences = [] } = useProviderSequences(providerId, bbox, zoom);
|
|
216
|
+
const visiblePhotos = zoom >= adapter.minZoom ? photos.filter((photo) => photoMatchesFilters(photo, filter?.photoTypes, filter?.date)) : [];
|
|
217
|
+
const photoCollection = zoom >= adapter.minZoom ? photosToFeatureCollection(photos) : emptyPointCollection();
|
|
218
|
+
const viewfieldCollection = showViewfields ? photosToViewfieldsFeatureCollection(visiblePhotos, zoom) : emptyPolygonCollection();
|
|
219
|
+
const photoFilter = buildPhotoLayerFilter(filter?.photoTypes, filter?.date);
|
|
220
|
+
const photoSrcId = photoSourceId(providerId);
|
|
221
|
+
const viewfieldSrcId = viewfieldSourceId(providerId);
|
|
222
|
+
const sequenceCapturedAtById = /* @__PURE__ */ new Map();
|
|
223
|
+
for (const photo of visiblePhotos) {
|
|
224
|
+
if (photo.sequenceId == null || photo.capturedAt == null) continue;
|
|
225
|
+
const prev = sequenceCapturedAtById.get(photo.sequenceId);
|
|
226
|
+
if (prev == null || photo.capturedAt > prev) {
|
|
227
|
+
sequenceCapturedAtById.set(photo.sequenceId, photo.capturedAt);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const filteredSequences = showSequences && zoom >= (adapter.sequencesMinZoom ?? adapter.minZoom) ? sequences.map((sequence) => {
|
|
231
|
+
if (sequence.capturedAt != null) return sequence;
|
|
232
|
+
const fromPhotos = sequenceCapturedAtById.get(sequence.sequenceId);
|
|
233
|
+
return fromPhotos != null ? { ...sequence, capturedAt: fromPhotos } : sequence;
|
|
234
|
+
}).filter((sequence) => {
|
|
235
|
+
const asPhoto = {
|
|
236
|
+
providerId: sequence.providerId,
|
|
237
|
+
photoId: sequence.sequenceId,
|
|
238
|
+
sequenceId: sequence.sequenceId,
|
|
239
|
+
capturedAt: sequence.capturedAt,
|
|
240
|
+
isPano: sequence.isPano,
|
|
241
|
+
heading: null,
|
|
242
|
+
lngLat: [0, 0]
|
|
243
|
+
};
|
|
244
|
+
return photoMatchesFilters(asPhoto, filter?.photoTypes, filter?.date);
|
|
245
|
+
}) : [];
|
|
246
|
+
const sequenceCollection = filteredSequences.length > 0 ? sequencesToFeatureCollection(filteredSequences) : emptyLineCollection();
|
|
247
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
248
|
+
adapter.fetchSequences && showSequences ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
249
|
+
/* @__PURE__ */ jsx(
|
|
250
|
+
Source,
|
|
251
|
+
{
|
|
252
|
+
id: sequenceSourceId(providerId),
|
|
253
|
+
type: "geojson",
|
|
254
|
+
data: sequenceCollection
|
|
255
|
+
},
|
|
256
|
+
sequenceSourceId(providerId)
|
|
257
|
+
),
|
|
258
|
+
/* @__PURE__ */ jsx(
|
|
259
|
+
Layer,
|
|
260
|
+
{
|
|
261
|
+
id: sequenceLayerId(providerId),
|
|
262
|
+
type: "line",
|
|
263
|
+
source: sequenceSourceId(providerId),
|
|
264
|
+
paint: {
|
|
265
|
+
"line-color": adapter.color,
|
|
266
|
+
"line-width": 2,
|
|
267
|
+
"line-opacity": 0.35
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
] }) : null,
|
|
272
|
+
showViewfields ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
273
|
+
/* @__PURE__ */ jsx(
|
|
274
|
+
Source,
|
|
275
|
+
{
|
|
276
|
+
id: viewfieldSrcId,
|
|
277
|
+
type: "geojson",
|
|
278
|
+
data: viewfieldCollection
|
|
279
|
+
},
|
|
280
|
+
viewfieldSrcId
|
|
281
|
+
),
|
|
282
|
+
/* @__PURE__ */ jsx(
|
|
283
|
+
Layer,
|
|
284
|
+
{
|
|
285
|
+
id: viewfieldLayerId(providerId),
|
|
286
|
+
type: "fill",
|
|
287
|
+
source: viewfieldSrcId,
|
|
288
|
+
paint: {
|
|
289
|
+
"fill-color": photoCircleColor,
|
|
290
|
+
"fill-opacity": 0.22
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
),
|
|
294
|
+
/* @__PURE__ */ jsx(
|
|
295
|
+
Layer,
|
|
296
|
+
{
|
|
297
|
+
id: viewfieldLineLayerId(providerId),
|
|
298
|
+
type: "line",
|
|
299
|
+
source: viewfieldSrcId,
|
|
300
|
+
paint: {
|
|
301
|
+
"line-color": photoCircleColor,
|
|
302
|
+
"line-width": 1,
|
|
303
|
+
"line-opacity": 0.45
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
] }) : null,
|
|
308
|
+
/* @__PURE__ */ jsx(
|
|
309
|
+
Source,
|
|
310
|
+
{
|
|
311
|
+
id: photoSrcId,
|
|
312
|
+
type: "geojson",
|
|
313
|
+
data: photoCollection,
|
|
314
|
+
promoteId: "photoId"
|
|
315
|
+
},
|
|
316
|
+
photoSrcId
|
|
317
|
+
),
|
|
318
|
+
/* @__PURE__ */ jsx(
|
|
319
|
+
Layer,
|
|
320
|
+
{
|
|
321
|
+
id: photoLayerId(providerId),
|
|
322
|
+
type: "circle",
|
|
323
|
+
source: photoSrcId,
|
|
324
|
+
filter: photoFilter,
|
|
325
|
+
layout: { "circle-sort-key": PHOTO_SORT_KEY },
|
|
326
|
+
paint: {
|
|
327
|
+
"circle-radius": CIRCLE_RADIUS,
|
|
328
|
+
"circle-color": photoCircleColor,
|
|
329
|
+
"circle-stroke-width": 1,
|
|
330
|
+
"circle-stroke-color": "#ffffff"
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
] });
|
|
335
|
+
};
|
|
336
|
+
var MapFeatureProviderLayer = ({
|
|
337
|
+
providerId,
|
|
338
|
+
bbox,
|
|
339
|
+
zoom,
|
|
340
|
+
filter,
|
|
341
|
+
mapFeatureCircleColor
|
|
342
|
+
}) => {
|
|
343
|
+
const adapter = adapterById[providerId];
|
|
344
|
+
const { data: features = [] } = useProviderMapFeatures(providerId, bbox, zoom);
|
|
345
|
+
const featureCollection = zoom >= adapter.minZoom ? mapFeaturesToFeatureCollection(features) : emptyPointCollection();
|
|
346
|
+
const featureFilter = buildMapFeatureLayerFilter(filter?.date);
|
|
347
|
+
const featureSrcId = featureSourceId(providerId);
|
|
348
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
349
|
+
/* @__PURE__ */ jsx(
|
|
350
|
+
Source,
|
|
351
|
+
{
|
|
352
|
+
id: featureSrcId,
|
|
353
|
+
type: "geojson",
|
|
354
|
+
data: featureCollection,
|
|
355
|
+
promoteId: "featureId"
|
|
356
|
+
},
|
|
357
|
+
featureSrcId
|
|
358
|
+
),
|
|
359
|
+
/* @__PURE__ */ jsx(
|
|
360
|
+
Layer,
|
|
361
|
+
{
|
|
362
|
+
id: featureLayerId(providerId),
|
|
363
|
+
type: "circle",
|
|
364
|
+
source: featureSrcId,
|
|
365
|
+
filter: featureFilter,
|
|
366
|
+
layout: { "circle-sort-key": FEATURE_SORT_KEY },
|
|
367
|
+
paint: {
|
|
368
|
+
"circle-radius": FEATURE_CIRCLE_RADIUS,
|
|
369
|
+
"circle-color": mapFeatureCircleColor,
|
|
370
|
+
"circle-stroke-width": 1,
|
|
371
|
+
"circle-stroke-color": "#ffffff"
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
] });
|
|
376
|
+
};
|
|
377
|
+
var ProviderLayer = (props) => {
|
|
378
|
+
const adapter = adapterById[props.providerId];
|
|
379
|
+
return adapter.kind === "mapFeature" ? /* @__PURE__ */ jsx(MapFeatureProviderLayer, { ...props }) : /* @__PURE__ */ jsx(PhotoProviderLayer, { ...props });
|
|
380
|
+
};
|
|
381
|
+
var StreetLevelImagerySourcesAndLayers = ({
|
|
382
|
+
providers,
|
|
383
|
+
filter,
|
|
384
|
+
bbox,
|
|
385
|
+
zoom,
|
|
386
|
+
options
|
|
387
|
+
}) => {
|
|
388
|
+
const {
|
|
389
|
+
config,
|
|
390
|
+
showSequences = true,
|
|
391
|
+
showViewfields = true,
|
|
392
|
+
showViewCone = false,
|
|
393
|
+
showSelectionHighlight = false,
|
|
394
|
+
selectedPhoto,
|
|
395
|
+
selectedSequenceId,
|
|
396
|
+
viewerPov,
|
|
397
|
+
photoCircleColor,
|
|
398
|
+
mapFeatureCircleColor
|
|
399
|
+
} = options;
|
|
400
|
+
useEffect(
|
|
401
|
+
function applyStreetImageryConfig() {
|
|
402
|
+
if (config) {
|
|
403
|
+
setStreetImageryConfig(config);
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
[config]
|
|
407
|
+
);
|
|
408
|
+
const selectedProviderId = selectedPhoto?.providerId ?? null;
|
|
409
|
+
const { data: sequences = [] } = useProviderSequences(
|
|
410
|
+
selectedProviderId ?? "mapillary",
|
|
411
|
+
showSelectionHighlight && selectedProviderId ? bbox : null,
|
|
412
|
+
zoom
|
|
413
|
+
);
|
|
414
|
+
const selectedSequence = resolveSelectedSequence(
|
|
415
|
+
selectedPhoto,
|
|
416
|
+
sequences,
|
|
417
|
+
selectedSequenceId ?? selectedPhoto?.sequenceId
|
|
418
|
+
);
|
|
419
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
420
|
+
providers.map((providerId) => /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
|
|
421
|
+
ProviderLayer,
|
|
422
|
+
{
|
|
423
|
+
bbox,
|
|
424
|
+
filter,
|
|
425
|
+
mapFeatureCircleColor,
|
|
426
|
+
photoCircleColor,
|
|
427
|
+
providerId,
|
|
428
|
+
showSequences,
|
|
429
|
+
showViewfields,
|
|
430
|
+
zoom
|
|
431
|
+
}
|
|
432
|
+
) }, providerId)),
|
|
433
|
+
showViewCone && selectedPhoto ? /* @__PURE__ */ jsx(
|
|
434
|
+
StreetLevelImageryViewCone,
|
|
435
|
+
{
|
|
436
|
+
selectedPhoto,
|
|
437
|
+
viewerPov,
|
|
438
|
+
zoom
|
|
439
|
+
}
|
|
440
|
+
) : null,
|
|
441
|
+
showSelectionHighlight ? /* @__PURE__ */ jsx(
|
|
442
|
+
StreetLevelImagerySelectionOverlay,
|
|
443
|
+
{
|
|
444
|
+
selectedPhoto,
|
|
445
|
+
selectedSequence
|
|
446
|
+
}
|
|
447
|
+
) : null
|
|
448
|
+
] });
|
|
449
|
+
};
|
|
450
|
+
var MapillaryPanel2 = lazy(
|
|
451
|
+
() => import('./MapillaryPanel-QX6NESX7.js').then((module) => ({
|
|
452
|
+
default: module.MapillaryPanel
|
|
453
|
+
}))
|
|
454
|
+
);
|
|
455
|
+
var PanoramaxPanel2 = lazy(
|
|
456
|
+
() => import('./PanoramaxPanel-J6ANR6LB.js').then((module) => ({
|
|
457
|
+
default: module.PanoramaxPanel
|
|
458
|
+
}))
|
|
459
|
+
);
|
|
460
|
+
var ViewerPanelPlaceholder = () => /* @__PURE__ */ jsx("div", { className: "flex min-h-48 animate-pulse items-center justify-center rounded-lg border border-slate-200 bg-slate-100", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: "Loading viewer\u2026" }) });
|
|
461
|
+
var StreetLevelImageryViewer = ({
|
|
462
|
+
photo,
|
|
463
|
+
groupPhotos,
|
|
464
|
+
onPhotoSelected,
|
|
465
|
+
onEaseMapToPoint
|
|
466
|
+
}) => {
|
|
467
|
+
if (photo.providerId === "mapillary") {
|
|
468
|
+
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerPanelPlaceholder, {}), children: /* @__PURE__ */ jsx(
|
|
469
|
+
MapillaryPanel2,
|
|
470
|
+
{
|
|
471
|
+
groupPhotos,
|
|
472
|
+
onEaseMapToPoint,
|
|
473
|
+
onPhotoSelected,
|
|
474
|
+
photo
|
|
475
|
+
}
|
|
476
|
+
) });
|
|
477
|
+
}
|
|
478
|
+
if (photo.providerId === "panoramax") {
|
|
479
|
+
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerPanelPlaceholder, {}), children: /* @__PURE__ */ jsx(
|
|
480
|
+
PanoramaxPanel2,
|
|
481
|
+
{
|
|
482
|
+
groupPhotos,
|
|
483
|
+
onEaseMapToPoint,
|
|
484
|
+
onPhotoSelected,
|
|
485
|
+
photo
|
|
486
|
+
}
|
|
487
|
+
) });
|
|
488
|
+
}
|
|
489
|
+
return null;
|
|
490
|
+
};
|
|
491
|
+
var streetImageryInteractiveLayerIds = (providers) => [
|
|
492
|
+
...providers.map((providerId) => photoLayerId(providerId)),
|
|
493
|
+
...providers.map((providerId) => viewfieldLayerId(providerId)),
|
|
494
|
+
...providers.map((providerId) => featureLayerId(providerId))
|
|
495
|
+
];
|
|
496
|
+
var parseProviderIdFromLayerId = (layerId) => {
|
|
497
|
+
const photoMatch = layerId.match(/^photos-(.+)$/);
|
|
498
|
+
if (photoMatch) {
|
|
499
|
+
return photoMatch[1];
|
|
500
|
+
}
|
|
501
|
+
const viewfieldMatch = layerId.match(/^viewfields-(.+)$/);
|
|
502
|
+
if (viewfieldMatch) {
|
|
503
|
+
return viewfieldMatch[1];
|
|
504
|
+
}
|
|
505
|
+
const featureMatch = layerId.match(/^features-(.+)$/);
|
|
506
|
+
if (featureMatch) {
|
|
507
|
+
return featureMatch[1];
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
};
|
|
511
|
+
var queryStreetImageryFeatures = (event) => {
|
|
512
|
+
const features = event.features ?? [];
|
|
513
|
+
const results = [];
|
|
514
|
+
const seenPhotoKeys = /* @__PURE__ */ new Set();
|
|
515
|
+
for (const feature of features) {
|
|
516
|
+
const layerId = feature.layer?.id;
|
|
517
|
+
if (!layerId) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
const providerId = parseProviderIdFromLayerId(layerId);
|
|
521
|
+
if (!providerId) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const props = feature.properties ?? {};
|
|
525
|
+
if (layerId.startsWith("photos-") || layerId.startsWith("viewfields-")) {
|
|
526
|
+
const photoId = props.photoId;
|
|
527
|
+
if (photoId == null) {
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const photoIdStr = String(photoId);
|
|
531
|
+
const dedupeKey = `${providerId}:${photoIdStr}`;
|
|
532
|
+
if (seenPhotoKeys.has(dedupeKey)) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
seenPhotoKeys.add(dedupeKey);
|
|
536
|
+
results.push({
|
|
537
|
+
providerId,
|
|
538
|
+
kind: "photo",
|
|
539
|
+
photoId: photoIdStr,
|
|
540
|
+
sequenceId: props.sequenceId != null ? String(props.sequenceId) : void 0
|
|
541
|
+
});
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
if (layerId.startsWith("features-")) {
|
|
545
|
+
const featureId = props.featureId;
|
|
546
|
+
if (featureId == null) {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
results.push({
|
|
550
|
+
providerId,
|
|
551
|
+
kind: "mapFeature",
|
|
552
|
+
featureId: String(featureId)
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return results;
|
|
557
|
+
};
|
|
558
|
+
var useProviderMapFeaturesMaybe = (providerId, enabled, bbox, zoom) => useProviderMapFeatures(providerId, enabled ? bbox : null, zoom);
|
|
559
|
+
var useAllProviderMapFeatures = (providerIds, bbox, zoom, date) => {
|
|
560
|
+
const enabled = new Set(providerIds);
|
|
561
|
+
const queries = PROVIDER_IDS.map((providerId) => ({
|
|
562
|
+
providerId,
|
|
563
|
+
query: useProviderMapFeaturesMaybe(providerId, enabled.has(providerId), bbox, zoom)
|
|
564
|
+
}));
|
|
565
|
+
return queries.flatMap(({ query }) => {
|
|
566
|
+
const data = query.data ?? [];
|
|
567
|
+
return data.filter((feature) => mapFeatureMatchesDateRange(feature, date));
|
|
568
|
+
});
|
|
569
|
+
};
|
|
570
|
+
var useAllProviderMapFeaturesLoading = (providerIds, bbox, zoom) => {
|
|
571
|
+
const enabled = new Set(providerIds);
|
|
572
|
+
const queries = PROVIDER_IDS.map((providerId) => ({
|
|
573
|
+
providerId,
|
|
574
|
+
query: useProviderMapFeaturesMaybe(providerId, enabled.has(providerId), bbox, zoom)
|
|
575
|
+
}));
|
|
576
|
+
const activeQueries = queries.filter(({ providerId }) => enabled.has(providerId));
|
|
577
|
+
return {
|
|
578
|
+
isLoading: activeQueries.some(({ query }) => query.isLoading),
|
|
579
|
+
isFetching: activeQueries.some(({ query }) => query.isFetching)
|
|
580
|
+
};
|
|
581
|
+
};
|
|
582
|
+
var useProviderPhotosMaybe = (providerId, enabled, bbox, zoom) => useProviderPhotos(providerId, enabled ? bbox : null, zoom);
|
|
583
|
+
var useAllProviderPhotos = (providerIds, bbox, zoom, photoTypes, date) => {
|
|
584
|
+
const enabled = new Set(providerIds);
|
|
585
|
+
const queries = PROVIDER_IDS.map((providerId) => ({
|
|
586
|
+
providerId,
|
|
587
|
+
query: useProviderPhotosMaybe(
|
|
588
|
+
providerId,
|
|
589
|
+
enabled.has(providerId) && isBrowserAvailableProvider(providerId),
|
|
590
|
+
bbox,
|
|
591
|
+
zoom
|
|
592
|
+
)
|
|
593
|
+
}));
|
|
594
|
+
const photos = queries.flatMap(({ query }) => {
|
|
595
|
+
const data = query.data ?? [];
|
|
596
|
+
return data.filter((photo) => photoMatchesFilters(photo, photoTypes, date));
|
|
597
|
+
});
|
|
598
|
+
const activeQueries = queries.filter(({ providerId }) => enabled.has(providerId));
|
|
599
|
+
const isLoading = activeQueries.some(({ query }) => query.isLoading);
|
|
600
|
+
const isFetching = activeQueries.some(({ query }) => query.isFetching);
|
|
601
|
+
return { photos, isLoading, isFetching };
|
|
602
|
+
};
|
|
603
|
+
var useMapViewportBbox = (mapId, viewportTrigger) => {
|
|
604
|
+
const { [mapId]: mapRef } = useMap();
|
|
605
|
+
const [mapLoaded, setMapLoaded] = useState(false);
|
|
606
|
+
useEffect(
|
|
607
|
+
function subscribeMapLoad() {
|
|
608
|
+
if (!mapRef) {
|
|
609
|
+
setMapLoaded(false);
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
const map = mapRef.getMap();
|
|
613
|
+
if (map.loaded()) {
|
|
614
|
+
setMapLoaded(true);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const onLoad = () => {
|
|
618
|
+
setMapLoaded(true);
|
|
619
|
+
};
|
|
620
|
+
void map.once("load", onLoad);
|
|
621
|
+
return () => {
|
|
622
|
+
map.off("load", onLoad);
|
|
623
|
+
};
|
|
624
|
+
},
|
|
625
|
+
[mapRef]
|
|
626
|
+
);
|
|
627
|
+
if (!mapRef || !mapLoaded) {
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
const bounds = mapRef.getBounds();
|
|
631
|
+
if (!bounds) {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
return [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()];
|
|
635
|
+
};
|
|
636
|
+
var usePhotoFullUrl = (photo) => useQuery({
|
|
637
|
+
queryKey: photo ? photoFullUrlQueryKey(photo) : ["photo-full-url", "none"],
|
|
638
|
+
queryFn: () => {
|
|
639
|
+
if (!photo) {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
return resolvePhotoPanoramaUrl(photo);
|
|
643
|
+
},
|
|
644
|
+
enabled: photo != null,
|
|
645
|
+
staleTime: 24 * 60 * 60 * 1e3
|
|
646
|
+
});
|
|
647
|
+
var usePhotoThumbnail = (photo) => useQuery({
|
|
648
|
+
queryKey: photo ? photoThumbnailQueryKey(photo) : ["photo-thumbnail", "none"],
|
|
649
|
+
queryFn: () => {
|
|
650
|
+
if (!photo) {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
return resolvePhotoThumbnailUrl(photo);
|
|
654
|
+
},
|
|
655
|
+
enabled: photo != null,
|
|
656
|
+
staleTime: 24 * 60 * 60 * 1e3
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
export { StreetLevelImagerySelectionOverlay, StreetLevelImagerySourcesAndLayers, StreetLevelImageryViewCone, StreetLevelImageryViewer, queryStreetImageryFeatures, resolveSelectedSequence, streetImageryInteractiveLayerIds, useAllProviderMapFeatures, useAllProviderMapFeaturesLoading, useAllProviderPhotos, useMapViewportBbox, usePhotoFullUrl, usePhotoThumbnail, useProviderMapFeatures, useProviderPhotos, useProviderSequences };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import 'mapillary-js/dist/mapillary.css';
|
|
3
|
+
import type { StreetImageryPhotoSelection } from '../types';
|
|
4
|
+
type MapillaryPanelProps = {
|
|
5
|
+
photo: NormalizedPhoto;
|
|
6
|
+
groupPhotos: NormalizedPhoto[];
|
|
7
|
+
onPhotoSelected: (selection: StreetImageryPhotoSelection) => void;
|
|
8
|
+
onEaseMapToPoint: (lng: number, lat: number) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const MapillaryPanel: ({ photo, onPhotoSelected, onEaseMapToPoint, }: MapillaryPanelProps) => import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import '@panoramax/web-viewer';
|
|
2
|
+
import type { NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
3
|
+
import type { StreetImageryPhotoSelection } from '../types';
|
|
4
|
+
type PanoramaxPanelProps = {
|
|
5
|
+
photo: NormalizedPhoto;
|
|
6
|
+
groupPhotos: NormalizedPhoto[];
|
|
7
|
+
onPhotoSelected: (selection: StreetImageryPhotoSelection) => void;
|
|
8
|
+
onEaseMapToPoint: (lng: number, lat: number) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const PanoramaxPanel: ({ photo, onPhotoSelected, onEaseMapToPoint, }: PanoramaxPanelProps) => import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ProviderId } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import type { MapGeoJSONFeature } from 'maplibre-gl';
|
|
3
|
+
export type StreetImageryClickFeature = {
|
|
4
|
+
providerId: ProviderId;
|
|
5
|
+
kind: 'photo' | 'mapFeature';
|
|
6
|
+
photoId?: string;
|
|
7
|
+
featureId?: string;
|
|
8
|
+
sequenceId?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const streetImageryInteractiveLayerIds: (providers: ProviderId[]) => string[];
|
|
11
|
+
export declare const queryStreetImageryFeatures: (event: {
|
|
12
|
+
features?: MapGeoJSONFeature[] | null;
|
|
13
|
+
}) => StreetImageryClickFeature[];
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type ViewerPovUpdate = {
|
|
2
|
+
bearing?: number | null;
|
|
3
|
+
hfov?: number | null;
|
|
4
|
+
lngLat?: [number, number] | null;
|
|
5
|
+
};
|
|
6
|
+
export declare const useViewerBearing: () => number | null;
|
|
7
|
+
export declare const useViewerHfov: () => number | null;
|
|
8
|
+
export declare const useViewerLngLat: () => [number, number] | null;
|
|
9
|
+
export declare const useViewerActions: () => {
|
|
10
|
+
setPov: (partial: ViewerPovUpdate) => void;
|
|
11
|
+
reset: () => void;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@osm-editor-kit/street-imagery-react",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "React MapLibre layers and photo viewers for street-level imagery providers.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Tobias Jordans",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/osmberlin/street-space-editor.git",
|
|
10
|
+
"directory": "packages/street-imagery-react"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public",
|
|
25
|
+
"tag": "alpha"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup && rm -f .tsbuildinfo && tsc -p tsconfig.build.json",
|
|
29
|
+
"type-check": "tsc --noEmit",
|
|
30
|
+
"test-run": "bun test --pass-with-no-tests src",
|
|
31
|
+
"prepublishOnly": "bun run build && bun ../../scripts/package-exports-for-publish.ts",
|
|
32
|
+
"postpublish": "bun ../../scripts/package-exports-restore.ts",
|
|
33
|
+
"prepack": "bun ../../scripts/package-exports-for-publish.ts",
|
|
34
|
+
"postpack": "bun ../../scripts/package-exports-restore.ts"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@panoramax/web-viewer": "^5",
|
|
38
|
+
"@tanstack/react-query": "^5.85.5",
|
|
39
|
+
"@types/geojson": "^7946.0.16",
|
|
40
|
+
"@types/react": "^19.1.10",
|
|
41
|
+
"@types/react-dom": "^19.1.7",
|
|
42
|
+
"bun-types": "^1.2.20",
|
|
43
|
+
"mapillary-js": "^4.1.2",
|
|
44
|
+
"maplibre-gl": "^5.7.1",
|
|
45
|
+
"react": "^19.1.1",
|
|
46
|
+
"react-dom": "^19.1.1",
|
|
47
|
+
"react-map-gl": "^8.0.4",
|
|
48
|
+
"tsup": "^8.5.1",
|
|
49
|
+
"typescript": "^7.0.0",
|
|
50
|
+
"zustand": "^5.0.7"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@osm-editor-kit/street-imagery": "workspace:*",
|
|
54
|
+
"@panoramax/web-viewer": "^5",
|
|
55
|
+
"@tanstack/react-query": "^5",
|
|
56
|
+
"mapillary-js": "^4",
|
|
57
|
+
"maplibre-gl": "^5",
|
|
58
|
+
"react": "^19",
|
|
59
|
+
"react-dom": "^19",
|
|
60
|
+
"react-map-gl": "^8",
|
|
61
|
+
"zustand": "^5"
|
|
62
|
+
},
|
|
63
|
+
"publishExports": {
|
|
64
|
+
".": {
|
|
65
|
+
"types": "./dist/index.d.ts",
|
|
66
|
+
"import": "./dist/index.js"
|
|
67
|
+
},
|
|
68
|
+
"./package.json": "./package.json"
|
|
69
|
+
}
|
|
70
|
+
}
|