@osm-editor-kit/street-imagery-react 0.1.0-alpha.12 → 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.
@@ -17,9 +17,29 @@ var useViewerStore = create()((set) => ({
17
17
  reset: () => set(initialState)
18
18
  }
19
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);
20
+ var useViewerBearing = () => {
21
+ return useViewerStore(_temp);
22
+ };
23
+ var useViewerHfov = () => {
24
+ return useViewerStore(_temp2);
25
+ };
26
+ var useViewerLngLat = () => {
27
+ return useViewerStore(_temp3);
28
+ };
29
+ var useViewerActions = () => {
30
+ return useViewerStore(_temp4);
31
+ };
32
+ function _temp(state) {
33
+ return state.bearing;
34
+ }
35
+ function _temp2(state) {
36
+ return state.hfov;
37
+ }
38
+ function _temp3(state) {
39
+ return state.lngLat;
40
+ }
41
+ function _temp4(state) {
42
+ return state.actions;
43
+ }
24
44
 
25
45
  export { useViewerActions, useViewerBearing, useViewerHfov, useViewerLngLat };
@@ -0,0 +1,498 @@
1
+ import { watchMapillaryJsView } from './chunk-XLBFXACK.js';
2
+ import { useViewerActions } from './chunk-GYUS2JVV.js';
3
+ import { c } from 'react/compiler-runtime';
4
+ import { viewFromOutline, SIGN_TARGET, viewFromFlatCamera, cameraPitch, panoX, bearingDeg, viewFromLocation, getStreetImageryConfig, fetchImageDetections } from '@osm-editor-kit/street-imagery';
5
+ import 'mapillary-js/dist/mapillary.css';
6
+ import { OutlineTag, PolygonGeometry, Viewer } from 'mapillary-js';
7
+ import { useRef, useEffectEvent, useEffect } from 'react';
8
+ import { jsx } from 'react/jsx-runtime';
9
+
10
+ var isSpherical = (image) => image.cameraType === "spherical" || image.cameraType === "equirectangular";
11
+ var mapillaryViewFor = async (viewer, image, {
12
+ lngLat,
13
+ shape = SIGN_TARGET,
14
+ outline
15
+ }) => {
16
+ const spherical = isSpherical(image);
17
+ if (outline?.length) {
18
+ return viewFromOutline(outline, spherical, shape.maxZoom);
19
+ }
20
+ const position = image.lngLat ?? image.originalLngLat;
21
+ const imageLngLat = [position.lng, position.lat];
22
+ const compass = image.computedCompassAngle ?? image.compassAngle;
23
+ if (!spherical) {
24
+ const focal = image.cameraParameters?.[0];
25
+ return focal ? viewFromFlatCamera({
26
+ focal,
27
+ width: image.width,
28
+ height: image.height,
29
+ compassAngle: compass,
30
+ pitch: cameraPitch(image.rotation)
31
+ }, imageLngLat, lngLat, shape) : void 0;
32
+ }
33
+ let x = panoX(bearingDeg(imageLngLat, lngLat), compass);
34
+ try {
35
+ viewer.setCenter([x, 0.5]);
36
+ const pixel = await viewer.project({
37
+ lng: lngLat[0],
38
+ lat: lngLat[1]
39
+ });
40
+ const basic = pixel ? await viewer.unprojectToBasic(pixel) : null;
41
+ const projected = basic?.[0];
42
+ if (projected != null && projected >= 0 && projected <= 1) {
43
+ x = projected;
44
+ }
45
+ } catch {
46
+ }
47
+ return viewFromLocation(x, {
48
+ lngLat: imageLngLat,
49
+ isPano: true
50
+ }, lngLat, shape);
51
+ };
52
+ var xDistance = (a, b, spherical) => {
53
+ const distance = Math.abs(a - b);
54
+ return spherical ? Math.min(distance, 1 - distance) : distance;
55
+ };
56
+ var findOwnDetection = async (image, value, estimate) => {
57
+ const spherical = isSpherical(image);
58
+ const candidates = await fetchImageDetections(image.id, {
59
+ filter: (v) => v === value
60
+ }).catch(() => []);
61
+ const centerX = (detection) => viewFromOutline(detection.outline, spherical)?.center[0] ?? 0.5;
62
+ return candidates.sort((a, b) => estimate ? xDistance(centerX(a), estimate.center[0], spherical) - xDistance(centerX(b), estimate.center[0], spherical) : 0)[0];
63
+ };
64
+ var turnMapillaryViewerTo = async (viewer, image, lookAt) => {
65
+ let outline = lookAt.outline?.length ? lookAt.outline : void 0;
66
+ let outlineId = `look-at-${image.id}`;
67
+ let view;
68
+ if (!outline) {
69
+ view = await mapillaryViewFor(viewer, image, lookAt);
70
+ const own = lookAt.value ? await findOwnDetection(image, lookAt.value, view) : void 0;
71
+ if (own) {
72
+ outline = own.outline;
73
+ outlineId = own.id;
74
+ }
75
+ }
76
+ if (outline) {
77
+ view = viewFromOutline(outline, isSpherical(image), (lookAt.shape ?? SIGN_TARGET).maxZoom);
78
+ }
79
+ if (!view) {
80
+ return false;
81
+ }
82
+ viewer.setCenter(view.center);
83
+ viewer.setZoom(view.zoom);
84
+ return outline ? {
85
+ id: outlineId,
86
+ outline,
87
+ label: lookAt.label
88
+ } : null;
89
+ };
90
+ var setMapillaryViewerOutlines = (viewer, outlines) => {
91
+ const tags = viewer.getComponent("tag");
92
+ tags.removeAll();
93
+ tags.add(outlines.filter(({
94
+ outline
95
+ }) => outline.length >= 3).map(({
96
+ id,
97
+ outline,
98
+ label,
99
+ color = 16776960
100
+ }) => {
101
+ const first = outline[0];
102
+ const last = outline[outline.length - 1];
103
+ const closed = first[0] === last[0] && first[1] === last[1] ? outline : [...outline, first];
104
+ return new OutlineTag(id, new PolygonGeometry(closed), {
105
+ text: label,
106
+ textColor: color,
107
+ lineColor: color,
108
+ lineWidth: 2,
109
+ fillColor: color,
110
+ fillOpacity: 0.3
111
+ });
112
+ }));
113
+ };
114
+ var panoCenterX = (bearing, compassAngle) => ((0.5 + (bearing - compassAngle) / 360) % 1 + 1) % 1;
115
+ var MapillaryPanel = (t0) => {
116
+ const $ = c(49);
117
+ const {
118
+ photo,
119
+ onPhotoSelected,
120
+ onEaseMapToPoint,
121
+ onViewerPhoto,
122
+ onViewerView,
123
+ lookAtBearing,
124
+ lookAt,
125
+ outlines,
126
+ hideAttribution: t1
127
+ } = t0;
128
+ const hideAttribution = t1 === void 0 ? false : t1;
129
+ const containerRef = useRef(null);
130
+ const viewerRef = useRef(null);
131
+ const navigableRef = useRef(false);
132
+ const pendingImageIdRef = useRef(null);
133
+ const lastViewerPhotoIdRef = useRef(null);
134
+ const bearingRafRef = useRef(null);
135
+ const pendingBearingRef = useRef(null);
136
+ const actions = useViewerActions();
137
+ const initialPhotoIdRef = useRef(photo.photoId);
138
+ const initialHideAttributionRef = useRef(hideAttribution);
139
+ const emitPhotoSelected = useEffectEvent(onPhotoSelected);
140
+ const emitEaseMapToPoint = useEffectEvent(onEaseMapToPoint);
141
+ let t2;
142
+ if ($[0] !== lookAt || $[1] !== lookAtBearing || $[2] !== outlines || $[3] !== photo.photoId) {
143
+ t2 = (viewer, image) => {
144
+ if (image.id !== photo.photoId) {
145
+ return;
146
+ }
147
+ if (lookAt) {
148
+ turnMapillaryViewerTo(viewer, image, lookAt).then((aimedAt) => {
149
+ if (aimedAt && !outlines && lastViewerPhotoIdRef.current === image.id) {
150
+ setMapillaryViewerOutlines(viewer, [aimedAt]);
151
+ }
152
+ }).catch(_temp);
153
+ return;
154
+ }
155
+ const spherical = image.cameraType === "spherical" || image.cameraType === "equirectangular";
156
+ const compass = image.computedCompassAngle ?? image.compassAngle;
157
+ if (lookAtBearing != null && spherical && compass != null) {
158
+ viewer.setCenter([panoCenterX(lookAtBearing, compass), 0.5]);
159
+ }
160
+ };
161
+ $[0] = lookAt;
162
+ $[1] = lookAtBearing;
163
+ $[2] = outlines;
164
+ $[3] = photo.photoId;
165
+ $[4] = t2;
166
+ } else {
167
+ t2 = $[4];
168
+ }
169
+ const turnToWantedBearing = useEffectEvent(t2);
170
+ let t3;
171
+ if ($[5] !== outlines) {
172
+ t3 = (viewer_0) => {
173
+ setMapillaryViewerOutlines(viewer_0, outlines ?? []);
174
+ };
175
+ $[5] = outlines;
176
+ $[6] = t3;
177
+ } else {
178
+ t3 = $[6];
179
+ }
180
+ const drawOutlines = useEffectEvent(t3);
181
+ let t4;
182
+ if ($[7] !== onViewerPhoto) {
183
+ t4 = (viewerPhoto) => onViewerPhoto?.(viewerPhoto);
184
+ $[7] = onViewerPhoto;
185
+ $[8] = t4;
186
+ } else {
187
+ t4 = $[8];
188
+ }
189
+ const emitViewerPhoto = useEffectEvent(t4);
190
+ let t5;
191
+ if ($[9] !== actions) {
192
+ t5 = function resetViewerStoreOnProviderChange() {
193
+ return () => {
194
+ actions.reset();
195
+ };
196
+ };
197
+ $[9] = actions;
198
+ $[10] = t5;
199
+ } else {
200
+ t5 = $[10];
201
+ }
202
+ let t6;
203
+ if ($[11] !== actions || $[12] !== photo.providerId) {
204
+ t6 = [actions, photo.providerId];
205
+ $[11] = actions;
206
+ $[12] = photo.providerId;
207
+ $[13] = t6;
208
+ } else {
209
+ t6 = $[13];
210
+ }
211
+ useEffect(t5, t6);
212
+ let t7;
213
+ if ($[14] !== actions || $[15] !== drawOutlines || $[16] !== emitEaseMapToPoint || $[17] !== emitPhotoSelected || $[18] !== emitViewerPhoto || $[19] !== turnToWantedBearing) {
214
+ t7 = function mountMapillaryViewer() {
215
+ const container = containerRef.current;
216
+ if (!container) {
217
+ return;
218
+ }
219
+ const viewer_1 = new Viewer({
220
+ accessToken: getStreetImageryConfig().mapillaryToken,
221
+ container,
222
+ imageId: initialPhotoIdRef.current,
223
+ component: {
224
+ attribution: !initialHideAttributionRef.current,
225
+ cover: false,
226
+ sequence: {
227
+ visible: true
228
+ },
229
+ tag: true
230
+ }
231
+ });
232
+ viewerRef.current = viewer_1;
233
+ lastViewerPhotoIdRef.current = initialPhotoIdRef.current;
234
+ const flushPendingMove = () => {
235
+ const pendingId = pendingImageIdRef.current;
236
+ if (!pendingId) {
237
+ return;
238
+ }
239
+ pendingImageIdRef.current = null;
240
+ viewer_1.moveTo(pendingId).catch(_temp2);
241
+ };
242
+ const onNavigable = (event) => {
243
+ navigableRef.current = event.navigable;
244
+ if (event.navigable) {
245
+ flushPendingMove();
246
+ }
247
+ };
248
+ const onImage = (event_0) => {
249
+ const {
250
+ image: image_0
251
+ } = event_0;
252
+ lastViewerPhotoIdRef.current = image_0.id;
253
+ turnToWantedBearing(viewer_1, image_0);
254
+ drawOutlines(viewer_1);
255
+ emitPhotoSelected({
256
+ provider: "mapillary",
257
+ sequenceId: image_0.sequenceId,
258
+ photoId: image_0.id
259
+ });
260
+ const position = image_0.lngLat ?? image_0.originalLngLat;
261
+ if (!position) {
262
+ return;
263
+ }
264
+ emitViewerPhoto({
265
+ providerId: "mapillary",
266
+ photoId: image_0.id,
267
+ sequenceId: image_0.sequenceId,
268
+ capturedAt: image_0.capturedAt,
269
+ isPano: image_0.cameraType === "spherical" || image_0.cameraType === "equirectangular",
270
+ heading: image_0.computedCompassAngle ?? image_0.compassAngle,
271
+ lngLat: [position.lng, position.lat],
272
+ ...image_0.creatorUsername ? {
273
+ creatorName: image_0.creatorUsername
274
+ } : {}
275
+ });
276
+ const isFlat = image_0.cameraType !== "spherical" && image_0.cameraType !== "equirectangular";
277
+ const compass_0 = image_0.computedCompassAngle ?? image_0.compassAngle;
278
+ actions.setPov({
279
+ lngLat: [position.lng, position.lat],
280
+ ...isFlat && compass_0 != null ? {
281
+ bearing: compass_0
282
+ } : {}
283
+ });
284
+ emitEaseMapToPoint(position.lng, position.lat);
285
+ };
286
+ const flushBearing = () => {
287
+ bearingRafRef.current = null;
288
+ if (pendingBearingRef.current != null) {
289
+ actions.setPov({
290
+ bearing: pendingBearingRef.current
291
+ });
292
+ pendingBearingRef.current = null;
293
+ }
294
+ viewer_1.getFieldOfView().then((fov) => {
295
+ actions.setPov({
296
+ hfov: fov
297
+ });
298
+ }).catch(_temp3);
299
+ };
300
+ const onBearing = (event_1) => {
301
+ pendingBearingRef.current = event_1.bearing;
302
+ if (bearingRafRef.current == null) {
303
+ bearingRafRef.current = requestAnimationFrame(flushBearing);
304
+ }
305
+ };
306
+ viewer_1.on("navigable", onNavigable);
307
+ viewer_1.on("image", onImage);
308
+ viewer_1.on("bearing", onBearing);
309
+ const resizeObserver = new ResizeObserver(() => {
310
+ viewer_1.resize();
311
+ });
312
+ resizeObserver.observe(container);
313
+ return () => {
314
+ if (bearingRafRef.current != null) {
315
+ cancelAnimationFrame(bearingRafRef.current);
316
+ bearingRafRef.current = null;
317
+ }
318
+ resizeObserver.disconnect();
319
+ viewer_1.off("navigable", onNavigable);
320
+ viewer_1.off("image", onImage);
321
+ viewer_1.off("bearing", onBearing);
322
+ viewer_1.remove();
323
+ viewerRef.current = null;
324
+ };
325
+ };
326
+ $[14] = actions;
327
+ $[15] = drawOutlines;
328
+ $[16] = emitEaseMapToPoint;
329
+ $[17] = emitPhotoSelected;
330
+ $[18] = emitViewerPhoto;
331
+ $[19] = turnToWantedBearing;
332
+ $[20] = t7;
333
+ } else {
334
+ t7 = $[20];
335
+ }
336
+ let t8;
337
+ if ($[21] !== actions) {
338
+ t8 = [actions];
339
+ $[21] = actions;
340
+ $[22] = t8;
341
+ } else {
342
+ t8 = $[22];
343
+ }
344
+ useEffect(t7, t8);
345
+ let 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() {
358
+ const viewer_2 = viewerRef.current;
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) {
399
+ return;
400
+ }
401
+ actions.reset();
402
+ lastViewerPhotoIdRef.current = photo.photoId;
403
+ if (navigableRef.current) {
404
+ viewer_3.moveTo(photo.photoId).catch(_temp4);
405
+ } else {
406
+ pendingImageIdRef.current = photo.photoId;
407
+ }
408
+ };
409
+ t13 = [actions, photo.photoId];
410
+ $[31] = actions;
411
+ $[32] = photo.photoId;
412
+ $[33] = t12;
413
+ $[34] = t13;
414
+ } else {
415
+ t12 = $[33];
416
+ t13 = $[34];
417
+ }
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;
425
+ } else {
426
+ t14 = $[37];
427
+ }
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) {
434
+ return;
435
+ }
436
+ viewer_4.getImage().then((image_2) => turnToWantedBearing(viewer_4, image_2)).catch(_temp5);
437
+ };
438
+ $[38] = photo.photoId;
439
+ $[39] = turnToWantedBearing;
440
+ $[40] = t15;
441
+ } else {
442
+ t15 = $[40];
443
+ }
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;
450
+ } else {
451
+ t16 = $[43];
452
+ }
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);
460
+ }
461
+ };
462
+ $[44] = drawOutlines;
463
+ $[45] = t17;
464
+ } else {
465
+ t17 = $[45];
466
+ }
467
+ let t18;
468
+ if ($[46] !== outlines) {
469
+ t18 = [outlines];
470
+ $[46] = outlines;
471
+ $[47] = t18;
472
+ } else {
473
+ t18 = $[47];
474
+ }
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: {
479
+ aspectRatio: "4 / 3"
480
+ } });
481
+ $[48] = t19;
482
+ } else {
483
+ t19 = $[48];
484
+ }
485
+ return t19;
486
+ };
487
+ function _temp() {
488
+ }
489
+ function _temp2() {
490
+ }
491
+ function _temp3() {
492
+ }
493
+ function _temp4() {
494
+ }
495
+ function _temp5() {
496
+ }
497
+
498
+ export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo };
@@ -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
- /** Called when a handle is released, a year label is clicked or a date input changes. */
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
+ };
@@ -1,9 +1,7 @@
1
1
  import type { Bbox } from '@osm-editor-kit/street-imagery';
2
2
  /**
3
- * Live WGS84 bbox from a react-map-gl map instance.
3
+ * WGS84 bbox of a react-map-gl map, updated when a move of the map ends. The array stays the same
4
+ * object while the viewport is the same, so it works as a dependency and as a prop.
4
5
  * @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
6
  */
9
- export declare const useMapViewportBbox: (mapId: string, viewportTrigger?: unknown) => Bbox | null;
7
+ export declare const useMapViewportBbox: (mapId: string) => Bbox | null;
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';