@osm-editor-kit/street-imagery-react 0.1.0-alpha.13 → 0.1.0-alpha.14
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 +53 -1
- package/dist/MapillaryPanel-SOHGB7JA.js +3 -0
- package/dist/PanoramaxPanel-NAKCJHT5.js +3 -0
- package/dist/StreetLevelImageryViewer.d.ts +8 -2
- package/dist/{chunk-5D7YCQUN.js → chunk-BZGY6BZC.js} +93 -38
- package/dist/{chunk-QBBIFF2C.js → chunk-LZR5SZDS.js} +112 -62
- package/dist/chunk-XLBFXACK.js +56 -0
- package/dist/filters/PhotoDateRangeFilter.d.ts +4 -1
- package/dist/filters/dateSliderPointer.d.ts +21 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +915 -738
- package/dist/panels/MapillaryPanel.d.ts +7 -2
- package/dist/panels/PanoramaxPanel.d.ts +7 -2
- package/dist/panels/PanoramaxWebViewerPanel.d.ts +7 -2
- package/dist/panels/panoramaxItem.d.ts +2 -0
- package/dist/panels/photoView.d.ts +19 -0
- package/dist/panoramax-web-viewer.js +113 -51
- package/dist/viewer-panels.js +3 -2
- package/dist/viewpoints/FloatingPhotoViewer.d.ts +22 -7
- package/dist/viewpoints/floatingViewerPosition.d.ts +18 -0
- package/dist/viewpoints/usePhotosLookingAt.d.ts +26 -0
- package/package.json +2 -2
- package/dist/MapillaryPanel-FQR4TTEW.js +0 -2
- package/dist/PanoramaxPanel-GO4GSVFE.js +0 -2
package/README.md
CHANGED
|
@@ -106,6 +106,58 @@ const { suggestions } = useViewSuggestions(viewpoints, { maxAgeYears: 2 })
|
|
|
106
106
|
Render `FloatingPhotoViewer` inside a positioned map container. Photos from suggestions open in
|
|
107
107
|
`StreetLevelImageryViewer` with `lookAtBearing={suggestion.direction.bearing}`.
|
|
108
108
|
|
|
109
|
+
A host without suggested views leaves out `suggestions`, `activeDirectionKey` and `onSelectSuggestion`; the chips are then not shown. Its own buttons and links go in `titleActions`. They keep their size next to a long title and do not start a drag.
|
|
110
|
+
|
|
111
|
+
For a free window over the whole page, use the viewport as container and let the box leave it until a share of it shows:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<FloatingPhotoViewer position="fixed" minVisibleShare={0.2} onClose={close} title="Photo">
|
|
115
|
+
{viewer}
|
|
116
|
+
</FloatingPhotoViewer>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`position="fixed"` (default `absolute`) needs an ancestor without `transform` or `filter`, and a stacking order that puts the box above the page. `minVisibleShare` (0–1) never lets the box go over the top edge, because the header is what moves it. The limit also holds when the box or the container changes size, and a minimized viewer stays within reach. Without `minVisibleShare` the box keeps 8px from the right and bottom edge.
|
|
120
|
+
|
|
121
|
+
To ask "who sees this spot" instead of "what looks north here", use `usePhotosLookingAt`: flat photos within `maxDistanceMeters` (50) whose heading points at the place within `maxFlatAngleDeg` (35), and 360° photos within the distance, best first. Each candidate has `bearingToPlace`.
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
const [place, setPlace] = useState<LngLat | null>(null) // set on map click
|
|
125
|
+
const [index, setIndex] = useState(0)
|
|
126
|
+
const { candidates } = usePhotosLookingAt(place, { maxAgeYears: 2, enabled: place != null })
|
|
127
|
+
const candidate = candidates[index]
|
|
128
|
+
|
|
129
|
+
{candidate ? (
|
|
130
|
+
<FloatingPhotoViewer
|
|
131
|
+
title="Photos of this place"
|
|
132
|
+
onClose={() => setPlace(null)}
|
|
133
|
+
step={{
|
|
134
|
+
onPrevious: index > 0 ? () => setIndex(index - 1) : undefined,
|
|
135
|
+
onNext: index < candidates.length - 1 ? () => setIndex(index + 1) : undefined,
|
|
136
|
+
previousLabel: 'Previous photo',
|
|
137
|
+
nextLabel: 'Next photo',
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
<StreetLevelImageryViewer
|
|
141
|
+
photo={candidate.photo}
|
|
142
|
+
lookAtBearing={candidate.bearingToPlace}
|
|
143
|
+
… />
|
|
144
|
+
</FloatingPhotoViewer>
|
|
145
|
+
) : null}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The list needs no chips: `step` gives the `<` `>` buttons and Alt + arrow keys. `lookAtBearing` turns Mapillary 360° photos only. The pure ranking is `rankPhotosLookingAt(place, photos, options)` in the core package.
|
|
149
|
+
|
|
150
|
+
For a link that opens the same photo with the same zoom and pan, keep the view the viewer reports and pass it to `providerExternalLink`:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
const [view, setView] = useState<PhotoView | null>(null)
|
|
154
|
+
|
|
155
|
+
<StreetLevelImageryViewer onViewerView={setView} … />
|
|
156
|
+
<a href={providerExternalLink(photo, view)}>Open on Mapillary</a>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`onViewerView` runs when the view has stopped moving and when the photo changes, not during a drag, for Mapillary (`x`, `y`, `zoom`) and Panoramax (`heading`, `pitch`, `zoom`). A view of another photo is ignored by the link. What a link shows also depends on the size of the window it opens in. Panoramax in the default panel: the viewers differ, so direction and height of the view carry over, not every pixel.
|
|
160
|
+
|
|
109
161
|
**Tailwind:** components use Tailwind classes. Tailwind does not scan `node_modules`, so add to your CSS:
|
|
110
162
|
|
|
111
163
|
```css
|
|
@@ -181,7 +233,7 @@ const { photos } = useAllProviderPhotos(providers, bbox, zoom) // no date: all p
|
|
|
181
233
|
/>
|
|
182
234
|
```
|
|
183
235
|
|
|
184
|
-
A handle at its end means no limit, so the left end also shows photos older than the slider spans (`maxYears`, default 10). The scale itself is in the core package (`dateSliderPosition`, `dateSliderBins`, …) for a host that draws its own chart. The texts are in `messages.dateFilter`.
|
|
236
|
+
A click or tap on the chart moves the nearer handle to that exact place, and a press on the chart can go on as a drag. Handles on the same spot come apart in the direction of the drag. Handles stay separate targets for mouse and touch, at least 44px high and wide, and a vertical swipe on a phone scrolls the page. A handle at its end means no limit, so the left end also shows photos older than the slider spans (`maxYears`, default 10). The scale itself is in the core package (`dateSliderPosition`, `dateSliderBins`, …) for a host that draws its own chart. The texts are in `messages.dateFilter`.
|
|
185
237
|
|
|
186
238
|
## Pick a place on the map to open elsewhere
|
|
187
239
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NormalizedPhoto } from '@osm-editor-kit/street-imagery';
|
|
1
|
+
import type { NormalizedPhoto, PhotoView } from '@osm-editor-kit/street-imagery';
|
|
2
2
|
import { type ComponentType } from 'react';
|
|
3
3
|
import type { MapillaryLookAt, MapillaryViewerOutline } from './panels/mapillaryLookAt';
|
|
4
4
|
import type { PanoramaxPanelProps } from './panels/PanoramaxPanel';
|
|
@@ -10,6 +10,12 @@ type StreetLevelImageryViewerProps = {
|
|
|
10
10
|
onEaseMapToPoint: (lng: number, lat: number) => void;
|
|
11
11
|
/** Mapillary, Panoramax: full photo data (creator, licence …) for each image the viewer shows. */
|
|
12
12
|
onViewerPhoto?: (photo: NormalizedPhoto) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Mapillary, Panoramax: the view inside the shown photo (pan and zoom), after it has stopped
|
|
15
|
+
* moving and when the photo changes. Pass it to `providerExternalLink` for a link that opens the
|
|
16
|
+
* same view on the provider's site.
|
|
17
|
+
*/
|
|
18
|
+
onViewerView?: (view: PhotoView) => void;
|
|
13
19
|
/** Mapillary 360° photos: initial view bearing (e.g. a suggested view direction). */
|
|
14
20
|
lookAtBearing?: number | null;
|
|
15
21
|
/** Mapillary: turn and zoom to a place (sign, traffic light, junction); wins over `lookAtBearing`. */
|
|
@@ -28,5 +34,5 @@ type StreetLevelImageryViewerProps = {
|
|
|
28
34
|
*/
|
|
29
35
|
panoramaxPanel?: ComponentType<PanoramaxPanelProps>;
|
|
30
36
|
};
|
|
31
|
-
export declare const StreetLevelImageryViewer: ({ photo, groupPhotos, onPhotoSelected, onEaseMapToPoint, onViewerPhoto, lookAtBearing, lookAt, outlines, hideAttribution, panoramaxPanel: PanoramaxPanelOverride, }: StreetLevelImageryViewerProps) => import("react").JSX.Element | null;
|
|
37
|
+
export declare const StreetLevelImageryViewer: ({ photo, groupPhotos, onPhotoSelected, onEaseMapToPoint, onViewerPhoto, onViewerView, lookAtBearing, lookAt, outlines, hideAttribution, panoramaxPanel: PanoramaxPanelOverride, }: StreetLevelImageryViewerProps) => import("react").JSX.Element | null;
|
|
32
38
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { watchMapillaryJsView, panoramaxZoomLevel } from './chunk-XLBFXACK.js';
|
|
1
2
|
import { useViewerActions } from './chunk-GYUS2JVV.js';
|
|
2
3
|
import { c } from 'react/compiler-runtime';
|
|
3
4
|
import { providerExternalLink, getStreetImageryConfig } from '@osm-editor-kit/street-imagery';
|
|
@@ -14,6 +15,7 @@ var toMs = (iso) => {
|
|
|
14
15
|
};
|
|
15
16
|
var orientation = (item) => item.properties?.["pers:interior_orientation"];
|
|
16
17
|
var isPanoramaxItemSpherical = (item) => orientation(item)?.field_of_view === 360;
|
|
18
|
+
var panoramaxItemFieldOfView = (item) => orientation(item)?.field_of_view ?? DEFAULT_FIELD_OF_VIEW_DEG;
|
|
17
19
|
var panoramaxItemSize = (item) => orientation(item)?.sensor_array_dimensions ?? (isPanoramaxItemSpherical(item) ? [2, 1] : [4, 3]);
|
|
18
20
|
var panoramaxItemNeighbour = (item, rel) => item.links?.find((link) => link.rel === rel)?.id ?? null;
|
|
19
21
|
var SD_WIDTH = 2048;
|
|
@@ -354,17 +356,19 @@ var PanoramaxDataProvider = class extends DataProviderBase {
|
|
|
354
356
|
}
|
|
355
357
|
};
|
|
356
358
|
var PanoramaxPanel = (t0) => {
|
|
357
|
-
const $ = c(
|
|
359
|
+
const $ = c(33);
|
|
358
360
|
const {
|
|
359
361
|
photo,
|
|
360
362
|
onPhotoSelected,
|
|
361
363
|
onEaseMapToPoint,
|
|
362
364
|
onViewerPhoto,
|
|
365
|
+
onViewerView,
|
|
363
366
|
hideAttribution: t1
|
|
364
367
|
} = t0;
|
|
365
368
|
const hideAttribution = t1 === void 0 ? false : t1;
|
|
366
369
|
const containerRef = useRef(null);
|
|
367
370
|
const viewerRef = useRef(null);
|
|
371
|
+
const dataProviderRef = useRef(null);
|
|
368
372
|
const navigableRef = useRef(false);
|
|
369
373
|
const pendingImageIdRef = useRef(null);
|
|
370
374
|
const lastViewerPhotoIdRef = useRef(null);
|
|
@@ -416,6 +420,7 @@ var PanoramaxPanel = (t0) => {
|
|
|
416
420
|
const dataProvider = new PanoramaxDataProvider({
|
|
417
421
|
endpoint: `${getStreetImageryConfig().panoramaxApiBase}/api`
|
|
418
422
|
});
|
|
423
|
+
dataProviderRef.current = dataProvider;
|
|
419
424
|
const viewer = new Viewer({
|
|
420
425
|
container,
|
|
421
426
|
dataProvider,
|
|
@@ -535,70 +540,120 @@ var PanoramaxPanel = (t0) => {
|
|
|
535
540
|
}
|
|
536
541
|
useEffect(t5, t6);
|
|
537
542
|
let t7;
|
|
543
|
+
if ($[14] !== onViewerView) {
|
|
544
|
+
t7 = (view) => onViewerView?.(view);
|
|
545
|
+
$[14] = onViewerView;
|
|
546
|
+
$[15] = t7;
|
|
547
|
+
} else {
|
|
548
|
+
t7 = $[15];
|
|
549
|
+
}
|
|
550
|
+
const emitViewerView = useEffectEvent(t7);
|
|
551
|
+
const reportsView = onViewerView !== void 0;
|
|
538
552
|
let t8;
|
|
539
|
-
if ($[
|
|
540
|
-
|
|
553
|
+
if ($[16] !== emitViewerView || $[17] !== reportsView) {
|
|
554
|
+
t8 = function reportViewInPhoto() {
|
|
541
555
|
const viewer_0 = viewerRef.current;
|
|
542
|
-
if (!viewer_0 ||
|
|
556
|
+
if (!viewer_0 || !reportsView) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const read = async () => {
|
|
560
|
+
const [image_0, t92, fov_0] = await Promise.all([viewer_0.getImage(), viewer_0.getPointOfView(), viewer_0.getFieldOfView()]);
|
|
561
|
+
const {
|
|
562
|
+
bearing,
|
|
563
|
+
tilt
|
|
564
|
+
} = t92;
|
|
565
|
+
const item_0 = dataProviderRef.current?.getItem(image_0.id);
|
|
566
|
+
return item_0 ? {
|
|
567
|
+
providerId: "panoramax",
|
|
568
|
+
photoId: image_0.id,
|
|
569
|
+
heading: bearing,
|
|
570
|
+
pitch: tilt,
|
|
571
|
+
zoom: panoramaxZoomLevel(fov_0, panoramaxItemFieldOfView(item_0))
|
|
572
|
+
} : null;
|
|
573
|
+
};
|
|
574
|
+
return watchMapillaryJsView(viewer_0, read, (view_0) => emitViewerView(view_0));
|
|
575
|
+
};
|
|
576
|
+
$[16] = emitViewerView;
|
|
577
|
+
$[17] = reportsView;
|
|
578
|
+
$[18] = t8;
|
|
579
|
+
} else {
|
|
580
|
+
t8 = $[18];
|
|
581
|
+
}
|
|
582
|
+
let t9;
|
|
583
|
+
if ($[19] !== actions || $[20] !== reportsView) {
|
|
584
|
+
t9 = [actions, reportsView];
|
|
585
|
+
$[19] = actions;
|
|
586
|
+
$[20] = reportsView;
|
|
587
|
+
$[21] = t9;
|
|
588
|
+
} else {
|
|
589
|
+
t9 = $[21];
|
|
590
|
+
}
|
|
591
|
+
useEffect(t8, t9);
|
|
592
|
+
let t10;
|
|
593
|
+
let t11;
|
|
594
|
+
if ($[22] !== actions || $[23] !== photo.photoId) {
|
|
595
|
+
t10 = function syncExternalPhotoSelection() {
|
|
596
|
+
const viewer_1 = viewerRef.current;
|
|
597
|
+
if (!viewer_1 || photo.photoId === lastViewerPhotoIdRef.current) {
|
|
543
598
|
return;
|
|
544
599
|
}
|
|
545
600
|
actions.reset();
|
|
546
601
|
lastViewerPhotoIdRef.current = photo.photoId;
|
|
547
602
|
if (navigableRef.current) {
|
|
548
|
-
|
|
603
|
+
viewer_1.moveTo(photo.photoId).catch(_temp3);
|
|
549
604
|
} else {
|
|
550
605
|
pendingImageIdRef.current = photo.photoId;
|
|
551
606
|
}
|
|
552
607
|
};
|
|
553
|
-
|
|
554
|
-
$[
|
|
555
|
-
$[
|
|
556
|
-
$[
|
|
557
|
-
$[
|
|
608
|
+
t11 = [actions, photo.photoId];
|
|
609
|
+
$[22] = actions;
|
|
610
|
+
$[23] = photo.photoId;
|
|
611
|
+
$[24] = t10;
|
|
612
|
+
$[25] = t11;
|
|
558
613
|
} else {
|
|
559
|
-
|
|
560
|
-
|
|
614
|
+
t10 = $[24];
|
|
615
|
+
t11 = $[25];
|
|
561
616
|
}
|
|
562
|
-
useEffect(
|
|
617
|
+
useEffect(t10, t11);
|
|
563
618
|
const attribution = hideAttribution ? null : shownPhoto;
|
|
564
619
|
const license = attribution?.details?.license;
|
|
565
|
-
let
|
|
566
|
-
let
|
|
567
|
-
if ($[
|
|
568
|
-
|
|
620
|
+
let t12;
|
|
621
|
+
let t13;
|
|
622
|
+
if ($[26] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
623
|
+
t12 = {
|
|
569
624
|
aspectRatio: "4 / 3"
|
|
570
625
|
};
|
|
571
|
-
|
|
572
|
-
$[
|
|
573
|
-
$[
|
|
626
|
+
t13 = /* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full w-full" });
|
|
627
|
+
$[26] = t12;
|
|
628
|
+
$[27] = t13;
|
|
574
629
|
} else {
|
|
575
|
-
|
|
576
|
-
|
|
630
|
+
t12 = $[26];
|
|
631
|
+
t13 = $[27];
|
|
577
632
|
}
|
|
578
|
-
let
|
|
579
|
-
if ($[
|
|
580
|
-
|
|
633
|
+
let t14;
|
|
634
|
+
if ($[28] !== attribution || $[29] !== license) {
|
|
635
|
+
t14 = attribution ? /* @__PURE__ */ jsxs("div", { className: "absolute right-0 bottom-0 flex gap-1.5 rounded-tl bg-black/50 px-1.5 py-0.5 text-[11px] leading-4 text-white", children: [
|
|
581
636
|
/* @__PURE__ */ jsx("a", { className: "underline-offset-2 hover:underline", href: providerExternalLink(attribution), rel: "noreferrer", target: "_blank", children: ["Panoramax", attribution.creatorName].filter(Boolean).join(" \xB7 ") }),
|
|
582
637
|
license ? attribution.details?.licenseUrl ? /* @__PURE__ */ jsx("a", { className: "underline-offset-2 hover:underline", href: attribution.details.licenseUrl, rel: "noreferrer", target: "_blank", children: license }) : /* @__PURE__ */ jsx("span", { children: license }) : null
|
|
583
638
|
] }) : null;
|
|
584
|
-
$[
|
|
585
|
-
$[
|
|
586
|
-
$[
|
|
639
|
+
$[28] = attribution;
|
|
640
|
+
$[29] = license;
|
|
641
|
+
$[30] = t14;
|
|
587
642
|
} else {
|
|
588
|
-
|
|
643
|
+
t14 = $[30];
|
|
589
644
|
}
|
|
590
|
-
let
|
|
591
|
-
if ($[
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
645
|
+
let t15;
|
|
646
|
+
if ($[31] !== t14) {
|
|
647
|
+
t15 = /* @__PURE__ */ jsxs("div", { className: "relative min-h-48 overflow-hidden rounded-lg border border-slate-200 bg-slate-900", style: t12, children: [
|
|
648
|
+
t13,
|
|
649
|
+
t14
|
|
595
650
|
] });
|
|
596
|
-
$[
|
|
597
|
-
$[
|
|
651
|
+
$[31] = t14;
|
|
652
|
+
$[32] = t15;
|
|
598
653
|
} else {
|
|
599
|
-
|
|
654
|
+
t15 = $[32];
|
|
600
655
|
}
|
|
601
|
-
return
|
|
656
|
+
return t15;
|
|
602
657
|
};
|
|
603
658
|
function _temp() {
|
|
604
659
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { watchMapillaryJsView } from './chunk-XLBFXACK.js';
|
|
1
2
|
import { useViewerActions } from './chunk-GYUS2JVV.js';
|
|
2
3
|
import { c } from 'react/compiler-runtime';
|
|
3
4
|
import { viewFromOutline, SIGN_TARGET, viewFromFlatCamera, cameraPitch, panoX, bearingDeg, viewFromLocation, getStreetImageryConfig, fetchImageDetections } from '@osm-editor-kit/street-imagery';
|
|
@@ -112,12 +113,13 @@ var setMapillaryViewerOutlines = (viewer, outlines) => {
|
|
|
112
113
|
};
|
|
113
114
|
var panoCenterX = (bearing, compassAngle) => ((0.5 + (bearing - compassAngle) / 360) % 1 + 1) % 1;
|
|
114
115
|
var MapillaryPanel = (t0) => {
|
|
115
|
-
const $ = c(
|
|
116
|
+
const $ = c(49);
|
|
116
117
|
const {
|
|
117
118
|
photo,
|
|
118
119
|
onPhotoSelected,
|
|
119
120
|
onEaseMapToPoint,
|
|
120
121
|
onViewerPhoto,
|
|
122
|
+
onViewerView,
|
|
121
123
|
lookAtBearing,
|
|
122
124
|
lookAt,
|
|
123
125
|
outlines,
|
|
@@ -340,99 +342,147 @@ var MapillaryPanel = (t0) => {
|
|
|
340
342
|
t8 = $[22];
|
|
341
343
|
}
|
|
342
344
|
useEffect(t7, t8);
|
|
343
|
-
let t10;
|
|
344
345
|
let t9;
|
|
345
|
-
if ($[23] !==
|
|
346
|
-
t9 =
|
|
346
|
+
if ($[23] !== onViewerView) {
|
|
347
|
+
t9 = (view) => onViewerView?.(view);
|
|
348
|
+
$[23] = onViewerView;
|
|
349
|
+
$[24] = t9;
|
|
350
|
+
} else {
|
|
351
|
+
t9 = $[24];
|
|
352
|
+
}
|
|
353
|
+
const emitViewerView = useEffectEvent(t9);
|
|
354
|
+
const reportsView = onViewerView !== void 0;
|
|
355
|
+
let t10;
|
|
356
|
+
if ($[25] !== emitViewerView || $[26] !== reportsView) {
|
|
357
|
+
t10 = function reportViewInPhoto() {
|
|
347
358
|
const viewer_2 = viewerRef.current;
|
|
348
|
-
if (!viewer_2 ||
|
|
359
|
+
if (!viewer_2 || !reportsView) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const read = async () => {
|
|
363
|
+
const [image_1, t112, zoom] = await Promise.all([viewer_2.getImage(), viewer_2.getCenter(), viewer_2.getZoom()]);
|
|
364
|
+
const [t122, t132] = t112;
|
|
365
|
+
const x = t122 === void 0 ? 0.5 : t122;
|
|
366
|
+
const y = t132 === void 0 ? 0.5 : t132;
|
|
367
|
+
return {
|
|
368
|
+
providerId: "mapillary",
|
|
369
|
+
photoId: image_1.id,
|
|
370
|
+
x,
|
|
371
|
+
y,
|
|
372
|
+
zoom
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
return watchMapillaryJsView(viewer_2, read, (view_0) => emitViewerView(view_0));
|
|
376
|
+
};
|
|
377
|
+
$[25] = emitViewerView;
|
|
378
|
+
$[26] = reportsView;
|
|
379
|
+
$[27] = t10;
|
|
380
|
+
} else {
|
|
381
|
+
t10 = $[27];
|
|
382
|
+
}
|
|
383
|
+
let t11;
|
|
384
|
+
if ($[28] !== actions || $[29] !== reportsView) {
|
|
385
|
+
t11 = [actions, reportsView];
|
|
386
|
+
$[28] = actions;
|
|
387
|
+
$[29] = reportsView;
|
|
388
|
+
$[30] = t11;
|
|
389
|
+
} else {
|
|
390
|
+
t11 = $[30];
|
|
391
|
+
}
|
|
392
|
+
useEffect(t10, t11);
|
|
393
|
+
let t12;
|
|
394
|
+
let t13;
|
|
395
|
+
if ($[31] !== actions || $[32] !== photo.photoId) {
|
|
396
|
+
t12 = function syncExternalPhotoSelection() {
|
|
397
|
+
const viewer_3 = viewerRef.current;
|
|
398
|
+
if (!viewer_3 || photo.photoId === lastViewerPhotoIdRef.current) {
|
|
349
399
|
return;
|
|
350
400
|
}
|
|
351
401
|
actions.reset();
|
|
352
402
|
lastViewerPhotoIdRef.current = photo.photoId;
|
|
353
403
|
if (navigableRef.current) {
|
|
354
|
-
|
|
404
|
+
viewer_3.moveTo(photo.photoId).catch(_temp4);
|
|
355
405
|
} else {
|
|
356
406
|
pendingImageIdRef.current = photo.photoId;
|
|
357
407
|
}
|
|
358
408
|
};
|
|
359
|
-
|
|
360
|
-
$[
|
|
361
|
-
$[
|
|
362
|
-
$[
|
|
363
|
-
$[
|
|
409
|
+
t13 = [actions, photo.photoId];
|
|
410
|
+
$[31] = actions;
|
|
411
|
+
$[32] = photo.photoId;
|
|
412
|
+
$[33] = t12;
|
|
413
|
+
$[34] = t13;
|
|
364
414
|
} else {
|
|
365
|
-
|
|
366
|
-
|
|
415
|
+
t12 = $[33];
|
|
416
|
+
t13 = $[34];
|
|
367
417
|
}
|
|
368
|
-
useEffect(
|
|
369
|
-
let
|
|
370
|
-
if ($[
|
|
371
|
-
|
|
372
|
-
$[
|
|
373
|
-
$[
|
|
374
|
-
$[
|
|
418
|
+
useEffect(t12, t13);
|
|
419
|
+
let t14;
|
|
420
|
+
if ($[35] !== lookAt || $[36] !== lookAtBearing) {
|
|
421
|
+
t14 = lookAt ? `${lookAt.lngLat.join(",")}|${lookAt.outline?.length ?? 0}|${lookAt.value ?? ""}` : String(lookAtBearing);
|
|
422
|
+
$[35] = lookAt;
|
|
423
|
+
$[36] = lookAtBearing;
|
|
424
|
+
$[37] = t14;
|
|
375
425
|
} else {
|
|
376
|
-
|
|
426
|
+
t14 = $[37];
|
|
377
427
|
}
|
|
378
|
-
const lookAtKey =
|
|
379
|
-
let
|
|
380
|
-
if ($[
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
if (!
|
|
428
|
+
const lookAtKey = t14;
|
|
429
|
+
let t15;
|
|
430
|
+
if ($[38] !== photo.photoId || $[39] !== turnToWantedBearing) {
|
|
431
|
+
t15 = function turnToTargetOnChange() {
|
|
432
|
+
const viewer_4 = viewerRef.current;
|
|
433
|
+
if (!viewer_4 || lastViewerPhotoIdRef.current !== photo.photoId) {
|
|
384
434
|
return;
|
|
385
435
|
}
|
|
386
|
-
|
|
436
|
+
viewer_4.getImage().then((image_2) => turnToWantedBearing(viewer_4, image_2)).catch(_temp5);
|
|
387
437
|
};
|
|
388
|
-
$[
|
|
389
|
-
$[
|
|
390
|
-
$[
|
|
438
|
+
$[38] = photo.photoId;
|
|
439
|
+
$[39] = turnToWantedBearing;
|
|
440
|
+
$[40] = t15;
|
|
391
441
|
} else {
|
|
392
|
-
|
|
442
|
+
t15 = $[40];
|
|
393
443
|
}
|
|
394
|
-
let
|
|
395
|
-
if ($[
|
|
396
|
-
|
|
397
|
-
$[
|
|
398
|
-
$[
|
|
399
|
-
$[
|
|
444
|
+
let t16;
|
|
445
|
+
if ($[41] !== lookAtKey || $[42] !== photo.photoId) {
|
|
446
|
+
t16 = [lookAtKey, photo.photoId];
|
|
447
|
+
$[41] = lookAtKey;
|
|
448
|
+
$[42] = photo.photoId;
|
|
449
|
+
$[43] = t16;
|
|
400
450
|
} else {
|
|
401
|
-
|
|
451
|
+
t16 = $[43];
|
|
402
452
|
}
|
|
403
|
-
useEffect(
|
|
404
|
-
let
|
|
405
|
-
if ($[
|
|
406
|
-
|
|
407
|
-
const
|
|
408
|
-
if (
|
|
409
|
-
drawOutlines(
|
|
453
|
+
useEffect(t15, t16);
|
|
454
|
+
let t17;
|
|
455
|
+
if ($[44] !== drawOutlines) {
|
|
456
|
+
t17 = function redrawOutlinesOnChange() {
|
|
457
|
+
const viewer_5 = viewerRef.current;
|
|
458
|
+
if (viewer_5 && navigableRef.current) {
|
|
459
|
+
drawOutlines(viewer_5);
|
|
410
460
|
}
|
|
411
461
|
};
|
|
412
|
-
$[
|
|
413
|
-
$[
|
|
462
|
+
$[44] = drawOutlines;
|
|
463
|
+
$[45] = t17;
|
|
414
464
|
} else {
|
|
415
|
-
|
|
465
|
+
t17 = $[45];
|
|
416
466
|
}
|
|
417
|
-
let
|
|
418
|
-
if ($[
|
|
419
|
-
|
|
420
|
-
$[
|
|
421
|
-
$[
|
|
467
|
+
let t18;
|
|
468
|
+
if ($[46] !== outlines) {
|
|
469
|
+
t18 = [outlines];
|
|
470
|
+
$[46] = outlines;
|
|
471
|
+
$[47] = t18;
|
|
422
472
|
} else {
|
|
423
|
-
|
|
473
|
+
t18 = $[47];
|
|
424
474
|
}
|
|
425
|
-
useEffect(
|
|
426
|
-
let
|
|
427
|
-
if ($[
|
|
428
|
-
|
|
475
|
+
useEffect(t17, t18);
|
|
476
|
+
let t19;
|
|
477
|
+
if ($[48] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
478
|
+
t19 = /* @__PURE__ */ jsx("div", { ref: containerRef, className: "min-h-48 overflow-hidden rounded-lg border border-slate-200 bg-slate-900", style: {
|
|
429
479
|
aspectRatio: "4 / 3"
|
|
430
480
|
} });
|
|
431
|
-
$[
|
|
481
|
+
$[48] = t19;
|
|
432
482
|
} else {
|
|
433
|
-
|
|
483
|
+
t19 = $[48];
|
|
434
484
|
}
|
|
435
|
-
return
|
|
485
|
+
return t19;
|
|
436
486
|
};
|
|
437
487
|
function _temp() {
|
|
438
488
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/panels/photoView.ts
|
|
2
|
+
var REPORT_DELAY_MS = 200;
|
|
3
|
+
var PANORAMAX_MIN_FOV = 5;
|
|
4
|
+
var PANORAMAX_MAX_FOV = 90;
|
|
5
|
+
var tanOfHalf = (degrees) => Math.tan(degrees * Math.PI / 360);
|
|
6
|
+
var panoramaxZoomLevel = (verticalFov, horizontalFovOfPicture) => {
|
|
7
|
+
const maxFov = Math.min(horizontalFovOfPicture * 3 / 4, PANORAMAX_MAX_FOV);
|
|
8
|
+
const fov = horizontalFovOfPicture >= 360 ? verticalFov : tanOfHalf(verticalFov) / tanOfHalf(horizontalFovOfPicture) * horizontalFovOfPicture;
|
|
9
|
+
const level = (maxFov - fov) / (maxFov - PANORAMAX_MIN_FOV) * 100;
|
|
10
|
+
return Math.min(100, Math.max(0, level));
|
|
11
|
+
};
|
|
12
|
+
var viewKey = (view) => JSON.stringify(view, (_, value) => typeof value === "number" ? Number(value.toFixed(4)) : value);
|
|
13
|
+
var createPhotoViewReporter = (read, report, delayMs = REPORT_DELAY_MS) => {
|
|
14
|
+
let timer;
|
|
15
|
+
let last = null;
|
|
16
|
+
let stopped = false;
|
|
17
|
+
const send = () => {
|
|
18
|
+
void read().then((view) => {
|
|
19
|
+
if (view && !stopped && viewKey(view) !== last) {
|
|
20
|
+
last = viewKey(view);
|
|
21
|
+
report(view);
|
|
22
|
+
}
|
|
23
|
+
}).catch(() => {
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
viewChanged: () => {
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
timer = setTimeout(send, delayMs);
|
|
30
|
+
},
|
|
31
|
+
stop: () => {
|
|
32
|
+
stopped = true;
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
var watchMapillaryJsView = (viewer, read, report) => {
|
|
38
|
+
const reporter = createPhotoViewReporter(read, report);
|
|
39
|
+
const {
|
|
40
|
+
viewChanged
|
|
41
|
+
} = reporter;
|
|
42
|
+
viewer.on("image", viewChanged);
|
|
43
|
+
viewer.on("pov", viewChanged);
|
|
44
|
+
viewer.on("fov", viewChanged);
|
|
45
|
+
viewer.on("moveend", viewChanged);
|
|
46
|
+
viewChanged();
|
|
47
|
+
return () => {
|
|
48
|
+
reporter.stop();
|
|
49
|
+
viewer.off("image", viewChanged);
|
|
50
|
+
viewer.off("pov", viewChanged);
|
|
51
|
+
viewer.off("fov", viewChanged);
|
|
52
|
+
viewer.off("moveend", viewChanged);
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { createPhotoViewReporter, panoramaxZoomLevel, watchMapillaryJsView };
|
|
@@ -10,7 +10,10 @@ export type PhotoDateMarker = {
|
|
|
10
10
|
export type PhotoDateRangeFilterProps = {
|
|
11
11
|
/** The chosen range, days as `YYYY-MM-DD`. No `from` and no `to`: all dates. */
|
|
12
12
|
value: DateRange;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Called when a handle is released, the chart is clicked, a year label is clicked or a date
|
|
15
|
+
* input changes.
|
|
16
|
+
*/
|
|
14
17
|
onChange: (range: DateRange) => void;
|
|
15
18
|
/**
|
|
16
19
|
* Capture times (ms) of the photos to show as marks above the slider, usually all photos in
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DateSliderPositions } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
export type DateSliderHandle = 'from' | 'to';
|
|
3
|
+
/** Size of one step of the slider, in positions: about a day to a month, depending on the age. */
|
|
4
|
+
export declare const DATE_SLIDER_STEP = 0.001;
|
|
5
|
+
/** The handles as they show. */
|
|
6
|
+
export declare const HANDLE_PX = 16;
|
|
7
|
+
/** Position of a point `leftPx` from the start of the track, in steps like the native handles. */
|
|
8
|
+
export declare const positionOnTrack: (leftPx: number, widthPx: number) => number;
|
|
9
|
+
/**
|
|
10
|
+
* The handle a press takes. `null` when the handles overlap under the press: they can only move
|
|
11
|
+
* apart, so the direction of the drag decides (`handleOfDrag`), also when both are at one end.
|
|
12
|
+
*/
|
|
13
|
+
export declare const pressedHandle: (positions: DateSliderPositions, press: number, widthPx: number) => DateSliderHandle | null;
|
|
14
|
+
export declare const handleOfDrag: (deltaPx: number) => DateSliderHandle;
|
|
15
|
+
/** A drag keeps the distance between a grabbed handle and the pointer: the handle does not jump. */
|
|
16
|
+
export declare const dragPositions: (start: DateSliderPositions, press: number, handle: DateSliderHandle, deltaPx: number, widthPx: number) => DateSliderPositions;
|
|
17
|
+
/** A click or tap moves the nearer handle there, or does nothing on a handle itself. */
|
|
18
|
+
export declare const tapPositions: (start: DateSliderPositions, press: number, widthPx: number) => {
|
|
19
|
+
handle: DateSliderHandle;
|
|
20
|
+
positions: DateSliderPositions | null;
|
|
21
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { VIEWPOINT_DEFAULT_COLOR, VIEWPOINT_DIRECTION_LAYER_ID, ViewpointLayer,
|
|
|
15
15
|
export { viewSuggestionLabel } from './viewpoints/viewDirectionLabels';
|
|
16
16
|
export * from './viewpoints/useViewpointSessionStore';
|
|
17
17
|
export * from './viewpoints/useViewSuggestions';
|
|
18
|
+
export * from './viewpoints/usePhotosLookingAt';
|
|
18
19
|
export type { MapillaryLookAt, MapillaryViewerOutline } from './panels/mapillaryLookAt';
|
|
19
20
|
export * from './mapillary/useMapillaryFeatures';
|
|
20
21
|
export { LocationPickOnMap, type LocationPickOnMapProps } from './openers/LocationPickOnMap';
|