@osm-editor-kit/street-imagery-react 0.1.0-alpha.11 → 0.1.0-alpha.13

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.
@@ -0,0 +1,448 @@
1
+ import { useViewerActions } from './chunk-GYUS2JVV.js';
2
+ import { c } from 'react/compiler-runtime';
3
+ import { viewFromOutline, SIGN_TARGET, viewFromFlatCamera, cameraPitch, panoX, bearingDeg, viewFromLocation, getStreetImageryConfig, fetchImageDetections } from '@osm-editor-kit/street-imagery';
4
+ import 'mapillary-js/dist/mapillary.css';
5
+ import { OutlineTag, PolygonGeometry, Viewer } from 'mapillary-js';
6
+ import { useRef, useEffectEvent, useEffect } from 'react';
7
+ import { jsx } from 'react/jsx-runtime';
8
+
9
+ var isSpherical = (image) => image.cameraType === "spherical" || image.cameraType === "equirectangular";
10
+ var mapillaryViewFor = async (viewer, image, {
11
+ lngLat,
12
+ shape = SIGN_TARGET,
13
+ outline
14
+ }) => {
15
+ const spherical = isSpherical(image);
16
+ if (outline?.length) {
17
+ return viewFromOutline(outline, spherical, shape.maxZoom);
18
+ }
19
+ const position = image.lngLat ?? image.originalLngLat;
20
+ const imageLngLat = [position.lng, position.lat];
21
+ const compass = image.computedCompassAngle ?? image.compassAngle;
22
+ if (!spherical) {
23
+ const focal = image.cameraParameters?.[0];
24
+ return focal ? viewFromFlatCamera({
25
+ focal,
26
+ width: image.width,
27
+ height: image.height,
28
+ compassAngle: compass,
29
+ pitch: cameraPitch(image.rotation)
30
+ }, imageLngLat, lngLat, shape) : void 0;
31
+ }
32
+ let x = panoX(bearingDeg(imageLngLat, lngLat), compass);
33
+ try {
34
+ viewer.setCenter([x, 0.5]);
35
+ const pixel = await viewer.project({
36
+ lng: lngLat[0],
37
+ lat: lngLat[1]
38
+ });
39
+ const basic = pixel ? await viewer.unprojectToBasic(pixel) : null;
40
+ const projected = basic?.[0];
41
+ if (projected != null && projected >= 0 && projected <= 1) {
42
+ x = projected;
43
+ }
44
+ } catch {
45
+ }
46
+ return viewFromLocation(x, {
47
+ lngLat: imageLngLat,
48
+ isPano: true
49
+ }, lngLat, shape);
50
+ };
51
+ var xDistance = (a, b, spherical) => {
52
+ const distance = Math.abs(a - b);
53
+ return spherical ? Math.min(distance, 1 - distance) : distance;
54
+ };
55
+ var findOwnDetection = async (image, value, estimate) => {
56
+ const spherical = isSpherical(image);
57
+ const candidates = await fetchImageDetections(image.id, {
58
+ filter: (v) => v === value
59
+ }).catch(() => []);
60
+ const centerX = (detection) => viewFromOutline(detection.outline, spherical)?.center[0] ?? 0.5;
61
+ return candidates.sort((a, b) => estimate ? xDistance(centerX(a), estimate.center[0], spherical) - xDistance(centerX(b), estimate.center[0], spherical) : 0)[0];
62
+ };
63
+ var turnMapillaryViewerTo = async (viewer, image, lookAt) => {
64
+ let outline = lookAt.outline?.length ? lookAt.outline : void 0;
65
+ let outlineId = `look-at-${image.id}`;
66
+ let view;
67
+ if (!outline) {
68
+ view = await mapillaryViewFor(viewer, image, lookAt);
69
+ const own = lookAt.value ? await findOwnDetection(image, lookAt.value, view) : void 0;
70
+ if (own) {
71
+ outline = own.outline;
72
+ outlineId = own.id;
73
+ }
74
+ }
75
+ if (outline) {
76
+ view = viewFromOutline(outline, isSpherical(image), (lookAt.shape ?? SIGN_TARGET).maxZoom);
77
+ }
78
+ if (!view) {
79
+ return false;
80
+ }
81
+ viewer.setCenter(view.center);
82
+ viewer.setZoom(view.zoom);
83
+ return outline ? {
84
+ id: outlineId,
85
+ outline,
86
+ label: lookAt.label
87
+ } : null;
88
+ };
89
+ var setMapillaryViewerOutlines = (viewer, outlines) => {
90
+ const tags = viewer.getComponent("tag");
91
+ tags.removeAll();
92
+ tags.add(outlines.filter(({
93
+ outline
94
+ }) => outline.length >= 3).map(({
95
+ id,
96
+ outline,
97
+ label,
98
+ color = 16776960
99
+ }) => {
100
+ const first = outline[0];
101
+ const last = outline[outline.length - 1];
102
+ const closed = first[0] === last[0] && first[1] === last[1] ? outline : [...outline, first];
103
+ return new OutlineTag(id, new PolygonGeometry(closed), {
104
+ text: label,
105
+ textColor: color,
106
+ lineColor: color,
107
+ lineWidth: 2,
108
+ fillColor: color,
109
+ fillOpacity: 0.3
110
+ });
111
+ }));
112
+ };
113
+ var panoCenterX = (bearing, compassAngle) => ((0.5 + (bearing - compassAngle) / 360) % 1 + 1) % 1;
114
+ var MapillaryPanel = (t0) => {
115
+ const $ = c(41);
116
+ const {
117
+ photo,
118
+ onPhotoSelected,
119
+ onEaseMapToPoint,
120
+ onViewerPhoto,
121
+ lookAtBearing,
122
+ lookAt,
123
+ outlines,
124
+ hideAttribution: t1
125
+ } = t0;
126
+ const hideAttribution = t1 === void 0 ? false : t1;
127
+ const containerRef = useRef(null);
128
+ const viewerRef = useRef(null);
129
+ const navigableRef = useRef(false);
130
+ const pendingImageIdRef = useRef(null);
131
+ const lastViewerPhotoIdRef = useRef(null);
132
+ const bearingRafRef = useRef(null);
133
+ const pendingBearingRef = useRef(null);
134
+ const actions = useViewerActions();
135
+ const initialPhotoIdRef = useRef(photo.photoId);
136
+ const initialHideAttributionRef = useRef(hideAttribution);
137
+ const emitPhotoSelected = useEffectEvent(onPhotoSelected);
138
+ const emitEaseMapToPoint = useEffectEvent(onEaseMapToPoint);
139
+ let t2;
140
+ if ($[0] !== lookAt || $[1] !== lookAtBearing || $[2] !== outlines || $[3] !== photo.photoId) {
141
+ t2 = (viewer, image) => {
142
+ if (image.id !== photo.photoId) {
143
+ return;
144
+ }
145
+ if (lookAt) {
146
+ turnMapillaryViewerTo(viewer, image, lookAt).then((aimedAt) => {
147
+ if (aimedAt && !outlines && lastViewerPhotoIdRef.current === image.id) {
148
+ setMapillaryViewerOutlines(viewer, [aimedAt]);
149
+ }
150
+ }).catch(_temp);
151
+ return;
152
+ }
153
+ const spherical = image.cameraType === "spherical" || image.cameraType === "equirectangular";
154
+ const compass = image.computedCompassAngle ?? image.compassAngle;
155
+ if (lookAtBearing != null && spherical && compass != null) {
156
+ viewer.setCenter([panoCenterX(lookAtBearing, compass), 0.5]);
157
+ }
158
+ };
159
+ $[0] = lookAt;
160
+ $[1] = lookAtBearing;
161
+ $[2] = outlines;
162
+ $[3] = photo.photoId;
163
+ $[4] = t2;
164
+ } else {
165
+ t2 = $[4];
166
+ }
167
+ const turnToWantedBearing = useEffectEvent(t2);
168
+ let t3;
169
+ if ($[5] !== outlines) {
170
+ t3 = (viewer_0) => {
171
+ setMapillaryViewerOutlines(viewer_0, outlines ?? []);
172
+ };
173
+ $[5] = outlines;
174
+ $[6] = t3;
175
+ } else {
176
+ t3 = $[6];
177
+ }
178
+ const drawOutlines = useEffectEvent(t3);
179
+ let t4;
180
+ if ($[7] !== onViewerPhoto) {
181
+ t4 = (viewerPhoto) => onViewerPhoto?.(viewerPhoto);
182
+ $[7] = onViewerPhoto;
183
+ $[8] = t4;
184
+ } else {
185
+ t4 = $[8];
186
+ }
187
+ const emitViewerPhoto = useEffectEvent(t4);
188
+ let t5;
189
+ if ($[9] !== actions) {
190
+ t5 = function resetViewerStoreOnProviderChange() {
191
+ return () => {
192
+ actions.reset();
193
+ };
194
+ };
195
+ $[9] = actions;
196
+ $[10] = t5;
197
+ } else {
198
+ t5 = $[10];
199
+ }
200
+ let t6;
201
+ if ($[11] !== actions || $[12] !== photo.providerId) {
202
+ t6 = [actions, photo.providerId];
203
+ $[11] = actions;
204
+ $[12] = photo.providerId;
205
+ $[13] = t6;
206
+ } else {
207
+ t6 = $[13];
208
+ }
209
+ useEffect(t5, t6);
210
+ let t7;
211
+ if ($[14] !== actions || $[15] !== drawOutlines || $[16] !== emitEaseMapToPoint || $[17] !== emitPhotoSelected || $[18] !== emitViewerPhoto || $[19] !== turnToWantedBearing) {
212
+ t7 = function mountMapillaryViewer() {
213
+ const container = containerRef.current;
214
+ if (!container) {
215
+ return;
216
+ }
217
+ const viewer_1 = new Viewer({
218
+ accessToken: getStreetImageryConfig().mapillaryToken,
219
+ container,
220
+ imageId: initialPhotoIdRef.current,
221
+ component: {
222
+ attribution: !initialHideAttributionRef.current,
223
+ cover: false,
224
+ sequence: {
225
+ visible: true
226
+ },
227
+ tag: true
228
+ }
229
+ });
230
+ viewerRef.current = viewer_1;
231
+ lastViewerPhotoIdRef.current = initialPhotoIdRef.current;
232
+ const flushPendingMove = () => {
233
+ const pendingId = pendingImageIdRef.current;
234
+ if (!pendingId) {
235
+ return;
236
+ }
237
+ pendingImageIdRef.current = null;
238
+ viewer_1.moveTo(pendingId).catch(_temp2);
239
+ };
240
+ const onNavigable = (event) => {
241
+ navigableRef.current = event.navigable;
242
+ if (event.navigable) {
243
+ flushPendingMove();
244
+ }
245
+ };
246
+ const onImage = (event_0) => {
247
+ const {
248
+ image: image_0
249
+ } = event_0;
250
+ lastViewerPhotoIdRef.current = image_0.id;
251
+ turnToWantedBearing(viewer_1, image_0);
252
+ drawOutlines(viewer_1);
253
+ emitPhotoSelected({
254
+ provider: "mapillary",
255
+ sequenceId: image_0.sequenceId,
256
+ photoId: image_0.id
257
+ });
258
+ const position = image_0.lngLat ?? image_0.originalLngLat;
259
+ if (!position) {
260
+ return;
261
+ }
262
+ emitViewerPhoto({
263
+ providerId: "mapillary",
264
+ photoId: image_0.id,
265
+ sequenceId: image_0.sequenceId,
266
+ capturedAt: image_0.capturedAt,
267
+ isPano: image_0.cameraType === "spherical" || image_0.cameraType === "equirectangular",
268
+ heading: image_0.computedCompassAngle ?? image_0.compassAngle,
269
+ lngLat: [position.lng, position.lat],
270
+ ...image_0.creatorUsername ? {
271
+ creatorName: image_0.creatorUsername
272
+ } : {}
273
+ });
274
+ const isFlat = image_0.cameraType !== "spherical" && image_0.cameraType !== "equirectangular";
275
+ const compass_0 = image_0.computedCompassAngle ?? image_0.compassAngle;
276
+ actions.setPov({
277
+ lngLat: [position.lng, position.lat],
278
+ ...isFlat && compass_0 != null ? {
279
+ bearing: compass_0
280
+ } : {}
281
+ });
282
+ emitEaseMapToPoint(position.lng, position.lat);
283
+ };
284
+ const flushBearing = () => {
285
+ bearingRafRef.current = null;
286
+ if (pendingBearingRef.current != null) {
287
+ actions.setPov({
288
+ bearing: pendingBearingRef.current
289
+ });
290
+ pendingBearingRef.current = null;
291
+ }
292
+ viewer_1.getFieldOfView().then((fov) => {
293
+ actions.setPov({
294
+ hfov: fov
295
+ });
296
+ }).catch(_temp3);
297
+ };
298
+ const onBearing = (event_1) => {
299
+ pendingBearingRef.current = event_1.bearing;
300
+ if (bearingRafRef.current == null) {
301
+ bearingRafRef.current = requestAnimationFrame(flushBearing);
302
+ }
303
+ };
304
+ viewer_1.on("navigable", onNavigable);
305
+ viewer_1.on("image", onImage);
306
+ viewer_1.on("bearing", onBearing);
307
+ const resizeObserver = new ResizeObserver(() => {
308
+ viewer_1.resize();
309
+ });
310
+ resizeObserver.observe(container);
311
+ return () => {
312
+ if (bearingRafRef.current != null) {
313
+ cancelAnimationFrame(bearingRafRef.current);
314
+ bearingRafRef.current = null;
315
+ }
316
+ resizeObserver.disconnect();
317
+ viewer_1.off("navigable", onNavigable);
318
+ viewer_1.off("image", onImage);
319
+ viewer_1.off("bearing", onBearing);
320
+ viewer_1.remove();
321
+ viewerRef.current = null;
322
+ };
323
+ };
324
+ $[14] = actions;
325
+ $[15] = drawOutlines;
326
+ $[16] = emitEaseMapToPoint;
327
+ $[17] = emitPhotoSelected;
328
+ $[18] = emitViewerPhoto;
329
+ $[19] = turnToWantedBearing;
330
+ $[20] = t7;
331
+ } else {
332
+ t7 = $[20];
333
+ }
334
+ let t8;
335
+ if ($[21] !== actions) {
336
+ t8 = [actions];
337
+ $[21] = actions;
338
+ $[22] = t8;
339
+ } else {
340
+ t8 = $[22];
341
+ }
342
+ useEffect(t7, t8);
343
+ let t10;
344
+ let t9;
345
+ if ($[23] !== actions || $[24] !== photo.photoId) {
346
+ t9 = function syncExternalPhotoSelection() {
347
+ const viewer_2 = viewerRef.current;
348
+ if (!viewer_2 || photo.photoId === lastViewerPhotoIdRef.current) {
349
+ return;
350
+ }
351
+ actions.reset();
352
+ lastViewerPhotoIdRef.current = photo.photoId;
353
+ if (navigableRef.current) {
354
+ viewer_2.moveTo(photo.photoId).catch(_temp4);
355
+ } else {
356
+ pendingImageIdRef.current = photo.photoId;
357
+ }
358
+ };
359
+ t10 = [actions, photo.photoId];
360
+ $[23] = actions;
361
+ $[24] = photo.photoId;
362
+ $[25] = t10;
363
+ $[26] = t9;
364
+ } else {
365
+ t10 = $[25];
366
+ t9 = $[26];
367
+ }
368
+ useEffect(t9, t10);
369
+ let t11;
370
+ if ($[27] !== lookAt || $[28] !== lookAtBearing) {
371
+ t11 = lookAt ? `${lookAt.lngLat.join(",")}|${lookAt.outline?.length ?? 0}|${lookAt.value ?? ""}` : String(lookAtBearing);
372
+ $[27] = lookAt;
373
+ $[28] = lookAtBearing;
374
+ $[29] = t11;
375
+ } else {
376
+ t11 = $[29];
377
+ }
378
+ const lookAtKey = t11;
379
+ let t12;
380
+ if ($[30] !== photo.photoId || $[31] !== turnToWantedBearing) {
381
+ t12 = function turnToTargetOnChange() {
382
+ const viewer_3 = viewerRef.current;
383
+ if (!viewer_3 || lastViewerPhotoIdRef.current !== photo.photoId) {
384
+ return;
385
+ }
386
+ viewer_3.getImage().then((image_1) => turnToWantedBearing(viewer_3, image_1)).catch(_temp5);
387
+ };
388
+ $[30] = photo.photoId;
389
+ $[31] = turnToWantedBearing;
390
+ $[32] = t12;
391
+ } else {
392
+ t12 = $[32];
393
+ }
394
+ let t13;
395
+ if ($[33] !== lookAtKey || $[34] !== photo.photoId) {
396
+ t13 = [lookAtKey, photo.photoId];
397
+ $[33] = lookAtKey;
398
+ $[34] = photo.photoId;
399
+ $[35] = t13;
400
+ } else {
401
+ t13 = $[35];
402
+ }
403
+ useEffect(t12, t13);
404
+ let t14;
405
+ if ($[36] !== drawOutlines) {
406
+ t14 = function redrawOutlinesOnChange() {
407
+ const viewer_4 = viewerRef.current;
408
+ if (viewer_4 && navigableRef.current) {
409
+ drawOutlines(viewer_4);
410
+ }
411
+ };
412
+ $[36] = drawOutlines;
413
+ $[37] = t14;
414
+ } else {
415
+ t14 = $[37];
416
+ }
417
+ let t15;
418
+ if ($[38] !== outlines) {
419
+ t15 = [outlines];
420
+ $[38] = outlines;
421
+ $[39] = t15;
422
+ } else {
423
+ t15 = $[39];
424
+ }
425
+ useEffect(t14, t15);
426
+ let t16;
427
+ if ($[40] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
428
+ t16 = /* @__PURE__ */ jsx("div", { ref: containerRef, className: "min-h-48 overflow-hidden rounded-lg border border-slate-200 bg-slate-900", style: {
429
+ aspectRatio: "4 / 3"
430
+ } });
431
+ $[40] = t16;
432
+ } else {
433
+ t16 = $[40];
434
+ }
435
+ return t16;
436
+ };
437
+ function _temp() {
438
+ }
439
+ function _temp2() {
440
+ }
441
+ function _temp3() {
442
+ }
443
+ function _temp4() {
444
+ }
445
+ function _temp5() {
446
+ }
447
+
448
+ export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo };
@@ -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;