@osm-editor-kit/street-imagery-react 0.1.0-alpha.9 → 1.0.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/LICENSE +21 -0
- package/README.md +113 -8
- package/dist/MapillaryPanel-SOHGB7JA.js +3 -0
- package/dist/PanoramaxPanel-5P5LWMRY.js +3 -0
- package/dist/StreetLevelImageryViewer.d.ts +18 -4
- package/dist/{chunk-6EMFFIL2.js → chunk-3DFVM6VK.js} +57 -13
- package/dist/{chunk-25UGGXIE.js → chunk-GYUS2JVV.js} +24 -4
- package/dist/chunk-LZR5SZDS.js +498 -0
- package/dist/chunk-R6BDVYWM.js +665 -0
- 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/hooks/useMapViewportBbox.d.ts +3 -5
- package/dist/hooks/useProviderData.d.ts +9 -3
- package/dist/index.d.ts +11 -10
- package/dist/index.js +3921 -1558
- package/dist/mapillary/useMapillaryFeatures.d.ts +5 -5
- package/dist/panels/MapillaryPanel.d.ts +7 -2
- package/dist/panels/PanoramaxPanel.d.ts +16 -9
- package/dist/panels/PanoramaxWebViewerPanel.d.ts +25 -0
- package/dist/panels/panoramaxDataProvider.d.ts +44 -0
- package/dist/panels/panoramaxItem.d.ts +98 -0
- package/dist/panels/photoView.d.ts +19 -0
- package/dist/panoramax-web-viewer/index.d.ts +1 -0
- package/dist/{chunk-KZTMKQT5.js → panoramax-web-viewer.js} +299 -93
- package/dist/photo-details.js +222 -151
- package/dist/viewer-panels/index.d.ts +3 -1
- package/dist/viewer-panels.js +4 -3
- 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 +24 -6
- package/dist/MapillaryPanel-FAXVZH37.js +0 -2
- package/dist/PanoramaxPanel-BTHEMKYY.js +0 -2
- package/dist/chunk-SGIB7AVC.js +0 -309
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
import { watchMapillaryJsView, panoramaxZoomLevel } from './chunk-XLBFXACK.js';
|
|
2
|
+
import { useViewerActions } from './chunk-GYUS2JVV.js';
|
|
3
|
+
import { c } from 'react/compiler-runtime';
|
|
4
|
+
import { providerExternalLink, getStreetImageryConfig } from '@osm-editor-kit/street-imagery';
|
|
5
|
+
import 'mapillary-js/dist/mapillary.css';
|
|
6
|
+
import { DataProviderBase, S2GeometryProvider, Viewer } from 'mapillary-js';
|
|
7
|
+
import { useRef, useState, useEffectEvent, useEffect } from 'react';
|
|
8
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
// src/panels/panoramaxItem.ts
|
|
11
|
+
var DEFAULT_FIELD_OF_VIEW_DEG = 70;
|
|
12
|
+
var toMs = (iso) => {
|
|
13
|
+
const ms = iso ? Date.parse(iso) : Number.NaN;
|
|
14
|
+
return Number.isNaN(ms) ? null : ms;
|
|
15
|
+
};
|
|
16
|
+
var orientation = (item) => item.properties?.["pers:interior_orientation"];
|
|
17
|
+
var isPanoramaxItemSpherical = (item) => orientation(item)?.field_of_view === 360;
|
|
18
|
+
var panoramaxItemFieldOfView = (item) => orientation(item)?.field_of_view ?? DEFAULT_FIELD_OF_VIEW_DEG;
|
|
19
|
+
var panoramaxItemSize = (item) => orientation(item)?.sensor_array_dimensions ?? (isPanoramaxItemSpherical(item) ? [2, 1] : [4, 3]);
|
|
20
|
+
var panoramaxItemNeighbour = (item, rel) => item.links?.find((link) => link.rel === rel)?.id ?? null;
|
|
21
|
+
var SD_WIDTH = 2048;
|
|
22
|
+
var panoramaxItemImageUrl = (item) => {
|
|
23
|
+
const {
|
|
24
|
+
hd,
|
|
25
|
+
sd,
|
|
26
|
+
thumb
|
|
27
|
+
} = item.assets ?? {};
|
|
28
|
+
return sd?.href ?? hd?.href ?? thumb?.href ?? "";
|
|
29
|
+
};
|
|
30
|
+
var TILE_SIZE = 1024;
|
|
31
|
+
var tileSpan = ([width, height], z) => TILE_SIZE * 2 ** (Math.ceil(Math.log2(Math.max(width, height))) - z);
|
|
32
|
+
var panoramaxViewerSize = (item) => {
|
|
33
|
+
const [width, height] = panoramaxItemSize(item);
|
|
34
|
+
const scale = 2 ** Math.ceil(Math.log2(Math.max(width, height))) / Math.max(width, height);
|
|
35
|
+
return [Math.round(width * scale), Math.round(height * scale)];
|
|
36
|
+
};
|
|
37
|
+
var panoramaxLevelNeedsOriginal = (size, z) => size[0] / (tileSpan(size, z) / TILE_SIZE) > SD_WIDTH;
|
|
38
|
+
var panoramaxTilesOfLevel = (size, z) => {
|
|
39
|
+
const [width, height] = size;
|
|
40
|
+
const span = tileSpan(size, z);
|
|
41
|
+
const tiles = [];
|
|
42
|
+
for (let y = 0; y < Math.ceil(height / span); y++) {
|
|
43
|
+
for (let x = 0; x < Math.ceil(width / span); x++) {
|
|
44
|
+
tiles.push({
|
|
45
|
+
x,
|
|
46
|
+
y
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return tiles;
|
|
51
|
+
};
|
|
52
|
+
var panoramaxTileRect = (size, tile, original) => {
|
|
53
|
+
const [width, height] = size;
|
|
54
|
+
const span = tileSpan(size, tile.z);
|
|
55
|
+
const x = tile.x * span;
|
|
56
|
+
const y = tile.y * span;
|
|
57
|
+
const cutWidth = Math.min(span, width - x);
|
|
58
|
+
const cutHeight = Math.min(span, height - y);
|
|
59
|
+
const scaleX = original.width / width;
|
|
60
|
+
const scaleY = original.height / height;
|
|
61
|
+
return {
|
|
62
|
+
x: x * scaleX,
|
|
63
|
+
y: y * scaleY,
|
|
64
|
+
width: cutWidth * scaleX,
|
|
65
|
+
height: cutHeight * scaleY,
|
|
66
|
+
outWidth: Math.max(1, Math.round(cutWidth / span * TILE_SIZE)),
|
|
67
|
+
outHeight: Math.max(1, Math.round(cutHeight / span * TILE_SIZE))
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
var panoramaxItemFocal = (item) => {
|
|
71
|
+
const fieldOfView = orientation(item)?.field_of_view || DEFAULT_FIELD_OF_VIEW_DEG;
|
|
72
|
+
const [width, height] = panoramaxItemSize(item);
|
|
73
|
+
const focalByWidth = 0.5 / Math.tan(Math.min(fieldOfView, 170) * Math.PI / 360);
|
|
74
|
+
return focalByWidth * (width / Math.max(width, height));
|
|
75
|
+
};
|
|
76
|
+
var creatorOf = (item) => item.properties?.["geovisio:producer"] ?? item.providers?.at(-1)?.name;
|
|
77
|
+
var panoramaxPhotoFromItem = (item) => {
|
|
78
|
+
const properties = item.properties ?? {};
|
|
79
|
+
const camera = orientation(item);
|
|
80
|
+
const cameraName = [camera?.camera_manufacturer, camera?.camera_model].filter(Boolean).join(" ");
|
|
81
|
+
const isPano = isPanoramaxItemSpherical(item);
|
|
82
|
+
const creatorName = creatorOf(item);
|
|
83
|
+
const uploadedAt = toMs(properties.created);
|
|
84
|
+
const licenseUrl = item.links?.find((link) => link.rel === "license")?.href;
|
|
85
|
+
const instance = item.links?.find((link) => link.rel === "via")?.instance_name;
|
|
86
|
+
const exifEntries = Object.entries(properties.exif ?? {}).filter(([, value]) => typeof value === "string" || typeof value === "number").map(([key, value]) => [key, String(value)]);
|
|
87
|
+
const details = {
|
|
88
|
+
...properties.license ? {
|
|
89
|
+
license: properties.license
|
|
90
|
+
} : {},
|
|
91
|
+
...licenseUrl ? {
|
|
92
|
+
licenseUrl
|
|
93
|
+
} : {},
|
|
94
|
+
...properties.datetimetz ? {
|
|
95
|
+
capturedAtLocal: properties.datetimetz
|
|
96
|
+
} : {},
|
|
97
|
+
...uploadedAt != null ? {
|
|
98
|
+
uploadedAt
|
|
99
|
+
} : {},
|
|
100
|
+
...cameraName ? {
|
|
101
|
+
camera: cameraName
|
|
102
|
+
} : {},
|
|
103
|
+
...!isPano && camera?.field_of_view ? {
|
|
104
|
+
fieldOfViewDeg: camera.field_of_view
|
|
105
|
+
} : {},
|
|
106
|
+
...properties["quality:horizontal_accuracy"] != null ? {
|
|
107
|
+
positionAccuracyMeters: properties["quality:horizontal_accuracy"]
|
|
108
|
+
} : {},
|
|
109
|
+
...instance ? {
|
|
110
|
+
instance
|
|
111
|
+
} : {},
|
|
112
|
+
...camera?.focal_length != null ? {
|
|
113
|
+
focalLengthMm: camera.focal_length
|
|
114
|
+
} : {},
|
|
115
|
+
...properties["original_file:name"] ? {
|
|
116
|
+
originalFileName: properties["original_file:name"]
|
|
117
|
+
} : {},
|
|
118
|
+
...properties["original_file:size"] != null ? {
|
|
119
|
+
originalFileSizeBytes: properties["original_file:size"]
|
|
120
|
+
} : {},
|
|
121
|
+
...properties["geovisio:rank_in_collection"] != null ? {
|
|
122
|
+
rankInSequence: properties["geovisio:rank_in_collection"]
|
|
123
|
+
} : {},
|
|
124
|
+
...exifEntries.length > 0 ? {
|
|
125
|
+
exif: Object.fromEntries(exifEntries)
|
|
126
|
+
} : {}
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
providerId: "panoramax",
|
|
130
|
+
photoId: item.id,
|
|
131
|
+
sequenceId: item.collection ?? null,
|
|
132
|
+
capturedAt: toMs(properties.datetime),
|
|
133
|
+
isPano,
|
|
134
|
+
heading: properties["view:azimuth"] ?? null,
|
|
135
|
+
lngLat: item.geometry.coordinates,
|
|
136
|
+
...creatorName ? {
|
|
137
|
+
creatorName
|
|
138
|
+
} : {},
|
|
139
|
+
details
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/panels/panoramaxDataProvider.ts
|
|
144
|
+
var IDS_PER_REQUEST = 40;
|
|
145
|
+
var SEQUENCE_PREFIX = "panoramax-around:";
|
|
146
|
+
var fetchFile = async (url) => {
|
|
147
|
+
const response = await fetch(url);
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
throw new Error(`Panoramax picture file: HTTP ${response.status}`);
|
|
150
|
+
}
|
|
151
|
+
return response;
|
|
152
|
+
};
|
|
153
|
+
var TILE_PREFIX = "panoramax-tile:";
|
|
154
|
+
var PanoramaxDataProvider = class extends DataProviderBase {
|
|
155
|
+
endpoint;
|
|
156
|
+
items = /* @__PURE__ */ new Map();
|
|
157
|
+
itemRequests = /* @__PURE__ */ new Map();
|
|
158
|
+
imageBuffers = /* @__PURE__ */ new Map();
|
|
159
|
+
pictures = /* @__PURE__ */ new Map();
|
|
160
|
+
constructor({
|
|
161
|
+
endpoint
|
|
162
|
+
}) {
|
|
163
|
+
super(new S2GeometryProvider());
|
|
164
|
+
this.endpoint = endpoint.replace(/\/$/, "");
|
|
165
|
+
}
|
|
166
|
+
/** The Panoramax data of a picture the viewer has loaded. */
|
|
167
|
+
getItem(imageId) {
|
|
168
|
+
return this.items.get(imageId);
|
|
169
|
+
}
|
|
170
|
+
// Only the arrows to pictures nearby need these, and those need a 3D reconstruction.
|
|
171
|
+
getCoreImages(cellId) {
|
|
172
|
+
return Promise.resolve({
|
|
173
|
+
cell_id: cellId,
|
|
174
|
+
images: []
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async getImages(imageIds) {
|
|
178
|
+
const items = await this.loadItems(imageIds);
|
|
179
|
+
return items.map((item) => ({
|
|
180
|
+
node: this.toImageEnt(item),
|
|
181
|
+
node_id: item.id
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
getSpatialImages(imageIds) {
|
|
185
|
+
return this.getImages(imageIds);
|
|
186
|
+
}
|
|
187
|
+
async getSequence(sequenceId) {
|
|
188
|
+
const imageId = sequenceId.slice(SEQUENCE_PREFIX.length);
|
|
189
|
+
const [item] = await this.loadItems([imageId]);
|
|
190
|
+
if (!item) {
|
|
191
|
+
throw new Error(`Panoramax picture ${imageId} not found`);
|
|
192
|
+
}
|
|
193
|
+
const around = [panoramaxItemNeighbour(item, "prev"), item.id, panoramaxItemNeighbour(item, "next")];
|
|
194
|
+
return {
|
|
195
|
+
id: sequenceId,
|
|
196
|
+
image_ids: around.filter((id) => id != null)
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// The viewer asks for the same file more than once while it starts; one download serves all.
|
|
200
|
+
getImageBuffer(url) {
|
|
201
|
+
const running = this.imageBuffers.get(url);
|
|
202
|
+
if (running) {
|
|
203
|
+
return running;
|
|
204
|
+
}
|
|
205
|
+
const load = url.startsWith(TILE_PREFIX) ? this.cutTile(url) : fetchFile(url).then((response) => response.arrayBuffer());
|
|
206
|
+
const request = load.finally(() => this.imageBuffers.delete(url));
|
|
207
|
+
this.imageBuffers.set(url, request);
|
|
208
|
+
return request;
|
|
209
|
+
}
|
|
210
|
+
async getImageTiles({
|
|
211
|
+
imageId,
|
|
212
|
+
z
|
|
213
|
+
}) {
|
|
214
|
+
const [item] = await this.loadItems([imageId]);
|
|
215
|
+
const tiles = item ? panoramaxTilesOfLevel(panoramaxViewerSize(item), z) : [];
|
|
216
|
+
return {
|
|
217
|
+
node_id: imageId,
|
|
218
|
+
node: tiles.map(({
|
|
219
|
+
x,
|
|
220
|
+
y
|
|
221
|
+
}) => ({
|
|
222
|
+
url: `${TILE_PREFIX}${imageId}/${z}/${x}/${y}`,
|
|
223
|
+
x,
|
|
224
|
+
y,
|
|
225
|
+
z
|
|
226
|
+
}))
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
async cutTile(url) {
|
|
230
|
+
const [imageId = "", z, x, y] = url.slice(TILE_PREFIX.length).split("/");
|
|
231
|
+
const item = this.items.get(imageId);
|
|
232
|
+
if (!item) {
|
|
233
|
+
throw new Error(`Panoramax picture ${imageId} not loaded`);
|
|
234
|
+
}
|
|
235
|
+
const size = panoramaxViewerSize(item);
|
|
236
|
+
const {
|
|
237
|
+
hd,
|
|
238
|
+
sd
|
|
239
|
+
} = item.assets ?? {};
|
|
240
|
+
const file = (panoramaxLevelNeedsOriginal(size, Number(z)) ? hd : sd) ?? hd ?? sd;
|
|
241
|
+
const original = await this.loadPicture(file?.href ?? "");
|
|
242
|
+
const rect = panoramaxTileRect(size, {
|
|
243
|
+
x: Number(x),
|
|
244
|
+
y: Number(y),
|
|
245
|
+
z: Number(z)
|
|
246
|
+
}, {
|
|
247
|
+
width: original.width,
|
|
248
|
+
height: original.height
|
|
249
|
+
});
|
|
250
|
+
const canvas = new OffscreenCanvas(rect.outWidth, rect.outHeight);
|
|
251
|
+
canvas.getContext("2d")?.drawImage(original, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.outWidth, rect.outHeight);
|
|
252
|
+
const blob = await canvas.convertToBlob({
|
|
253
|
+
type: "image/jpeg",
|
|
254
|
+
quality: 0.9
|
|
255
|
+
});
|
|
256
|
+
return blob.arrayBuffer();
|
|
257
|
+
}
|
|
258
|
+
loadPicture(url) {
|
|
259
|
+
const known = this.pictures.get(url);
|
|
260
|
+
if (known) {
|
|
261
|
+
return known;
|
|
262
|
+
}
|
|
263
|
+
const picture = fetchFile(url).then((response) => response.blob()).then((blob) => createImageBitmap(blob));
|
|
264
|
+
this.pictures.set(url, picture);
|
|
265
|
+
picture.catch(() => this.pictures.delete(url));
|
|
266
|
+
for (const [oldUrl, old] of [...this.pictures].slice(0, -3)) {
|
|
267
|
+
this.pictures.delete(oldUrl);
|
|
268
|
+
void old.then((bitmap) => bitmap.close()).catch(() => {
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return picture;
|
|
272
|
+
}
|
|
273
|
+
getMesh() {
|
|
274
|
+
return Promise.resolve({
|
|
275
|
+
faces: [],
|
|
276
|
+
vertices: []
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
getCluster(url) {
|
|
280
|
+
return Promise.resolve({
|
|
281
|
+
id: url,
|
|
282
|
+
points: {},
|
|
283
|
+
reference: {
|
|
284
|
+
lat: 0,
|
|
285
|
+
lng: 0,
|
|
286
|
+
alt: 0
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
async loadItems(imageIds) {
|
|
291
|
+
const missing = [...new Set(imageIds.filter((id) => !this.items.has(id) && !this.itemRequests.has(id)))];
|
|
292
|
+
for (let start = 0; start < missing.length; start += IDS_PER_REQUEST) {
|
|
293
|
+
const ids = missing.slice(start, start + IDS_PER_REQUEST);
|
|
294
|
+
const request = fetch(`${this.endpoint}/search?ids=${ids.join(",")}&limit=${ids.length}`).then((response) => {
|
|
295
|
+
if (!response.ok) {
|
|
296
|
+
throw new Error(`Panoramax search: HTTP ${response.status}`);
|
|
297
|
+
}
|
|
298
|
+
return response.json();
|
|
299
|
+
}).then((result) => {
|
|
300
|
+
for (const item of result.features ?? []) {
|
|
301
|
+
this.items.set(item.id, item);
|
|
302
|
+
}
|
|
303
|
+
}).finally(() => {
|
|
304
|
+
for (const id of ids) {
|
|
305
|
+
this.itemRequests.delete(id);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
for (const id of ids) {
|
|
309
|
+
this.itemRequests.set(id, request);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
await Promise.all(imageIds.flatMap((id) => this.itemRequests.get(id) ?? []));
|
|
313
|
+
return imageIds.flatMap((id) => this.items.get(id) ?? []);
|
|
314
|
+
}
|
|
315
|
+
toImageEnt(item) {
|
|
316
|
+
const [lng, lat] = item.geometry.coordinates;
|
|
317
|
+
const [width, height] = panoramaxViewerSize(item);
|
|
318
|
+
const spherical = isPanoramaxItemSpherical(item);
|
|
319
|
+
const properties = item.properties ?? {};
|
|
320
|
+
const none = {
|
|
321
|
+
id: "",
|
|
322
|
+
url: ""
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
id: item.id,
|
|
326
|
+
geometry: {
|
|
327
|
+
lat,
|
|
328
|
+
lng
|
|
329
|
+
},
|
|
330
|
+
sequence: {
|
|
331
|
+
id: `${SEQUENCE_PREFIX}${item.id}`
|
|
332
|
+
},
|
|
333
|
+
altitude: 0,
|
|
334
|
+
camera_type: spherical ? "spherical" : "perspective",
|
|
335
|
+
camera_parameters: spherical ? [] : [panoramaxItemFocal(item), 0, 0],
|
|
336
|
+
captured_at: Date.parse(properties.datetime ?? "") || 0,
|
|
337
|
+
compass_angle: properties["view:azimuth"] ?? 0,
|
|
338
|
+
cluster: none,
|
|
339
|
+
creator: {
|
|
340
|
+
id: "",
|
|
341
|
+
username: properties["geovisio:producer"] ?? ""
|
|
342
|
+
},
|
|
343
|
+
// Panoramax serves its pictures upright.
|
|
344
|
+
exif_orientation: 1,
|
|
345
|
+
width,
|
|
346
|
+
height,
|
|
347
|
+
mesh: none,
|
|
348
|
+
owner: {
|
|
349
|
+
id: ""
|
|
350
|
+
},
|
|
351
|
+
thumb: {
|
|
352
|
+
id: item.id,
|
|
353
|
+
url: panoramaxItemImageUrl(item)
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
var PanoramaxPanel = (t0) => {
|
|
359
|
+
const $ = c(33);
|
|
360
|
+
const {
|
|
361
|
+
photo,
|
|
362
|
+
onPhotoSelected,
|
|
363
|
+
onEaseMapToPoint,
|
|
364
|
+
onViewerPhoto,
|
|
365
|
+
onViewerView,
|
|
366
|
+
hideAttribution: t1
|
|
367
|
+
} = t0;
|
|
368
|
+
const hideAttribution = t1 === void 0 ? false : t1;
|
|
369
|
+
const containerRef = useRef(null);
|
|
370
|
+
const viewerRef = useRef(null);
|
|
371
|
+
const dataProviderRef = useRef(null);
|
|
372
|
+
const navigableRef = useRef(false);
|
|
373
|
+
const pendingImageIdRef = useRef(null);
|
|
374
|
+
const lastViewerPhotoIdRef = useRef(null);
|
|
375
|
+
const bearingRafRef = useRef(null);
|
|
376
|
+
const pendingBearingRef = useRef(null);
|
|
377
|
+
const actions = useViewerActions();
|
|
378
|
+
const initialPhotoIdRef = useRef(photo.photoId);
|
|
379
|
+
const [shownPhoto, setShownPhoto] = useState(null);
|
|
380
|
+
const emitPhotoSelected = useEffectEvent(onPhotoSelected);
|
|
381
|
+
const emitEaseMapToPoint = useEffectEvent(onEaseMapToPoint);
|
|
382
|
+
let t2;
|
|
383
|
+
if ($[0] !== onViewerPhoto) {
|
|
384
|
+
t2 = (viewerPhoto) => onViewerPhoto?.(viewerPhoto);
|
|
385
|
+
$[0] = onViewerPhoto;
|
|
386
|
+
$[1] = t2;
|
|
387
|
+
} else {
|
|
388
|
+
t2 = $[1];
|
|
389
|
+
}
|
|
390
|
+
const emitViewerPhoto = useEffectEvent(t2);
|
|
391
|
+
let t3;
|
|
392
|
+
if ($[2] !== actions) {
|
|
393
|
+
t3 = function resetViewerStoreOnProviderChange() {
|
|
394
|
+
return () => {
|
|
395
|
+
actions.reset();
|
|
396
|
+
};
|
|
397
|
+
};
|
|
398
|
+
$[2] = actions;
|
|
399
|
+
$[3] = t3;
|
|
400
|
+
} else {
|
|
401
|
+
t3 = $[3];
|
|
402
|
+
}
|
|
403
|
+
let t4;
|
|
404
|
+
if ($[4] !== actions || $[5] !== photo.providerId) {
|
|
405
|
+
t4 = [actions, photo.providerId];
|
|
406
|
+
$[4] = actions;
|
|
407
|
+
$[5] = photo.providerId;
|
|
408
|
+
$[6] = t4;
|
|
409
|
+
} else {
|
|
410
|
+
t4 = $[6];
|
|
411
|
+
}
|
|
412
|
+
useEffect(t3, t4);
|
|
413
|
+
let t5;
|
|
414
|
+
if ($[7] !== actions || $[8] !== emitEaseMapToPoint || $[9] !== emitPhotoSelected || $[10] !== emitViewerPhoto) {
|
|
415
|
+
t5 = function mountViewer() {
|
|
416
|
+
const container = containerRef.current;
|
|
417
|
+
if (!container) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const dataProvider = new PanoramaxDataProvider({
|
|
421
|
+
endpoint: `${getStreetImageryConfig().panoramaxApiBase}/api`
|
|
422
|
+
});
|
|
423
|
+
dataProviderRef.current = dataProvider;
|
|
424
|
+
const viewer = new Viewer({
|
|
425
|
+
container,
|
|
426
|
+
dataProvider,
|
|
427
|
+
imageId: initialPhotoIdRef.current,
|
|
428
|
+
component: {
|
|
429
|
+
attribution: false,
|
|
430
|
+
cache: {
|
|
431
|
+
depth: {
|
|
432
|
+
sequence: 1,
|
|
433
|
+
spherical: 0,
|
|
434
|
+
step: 0,
|
|
435
|
+
turn: 0
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
cover: false,
|
|
439
|
+
sequence: {
|
|
440
|
+
visible: true
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
viewerRef.current = viewer;
|
|
445
|
+
lastViewerPhotoIdRef.current = initialPhotoIdRef.current;
|
|
446
|
+
const flushPendingMove = () => {
|
|
447
|
+
const pendingId = pendingImageIdRef.current;
|
|
448
|
+
if (!pendingId) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
pendingImageIdRef.current = null;
|
|
452
|
+
viewer.moveTo(pendingId).catch(_temp);
|
|
453
|
+
};
|
|
454
|
+
const onNavigable = (event) => {
|
|
455
|
+
navigableRef.current = event.navigable;
|
|
456
|
+
if (event.navigable) {
|
|
457
|
+
flushPendingMove();
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
const onImage = (event_0) => {
|
|
461
|
+
const {
|
|
462
|
+
image
|
|
463
|
+
} = event_0;
|
|
464
|
+
lastViewerPhotoIdRef.current = image.id;
|
|
465
|
+
const item = dataProvider.getItem(image.id);
|
|
466
|
+
if (!item) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const viewerPhoto_0 = panoramaxPhotoFromItem(item);
|
|
470
|
+
emitPhotoSelected({
|
|
471
|
+
provider: "panoramax",
|
|
472
|
+
sequenceId: viewerPhoto_0.sequenceId ?? `photo:${image.id}`,
|
|
473
|
+
photoId: image.id
|
|
474
|
+
});
|
|
475
|
+
setShownPhoto(viewerPhoto_0);
|
|
476
|
+
emitViewerPhoto(viewerPhoto_0);
|
|
477
|
+
actions.setPov({
|
|
478
|
+
lngLat: viewerPhoto_0.lngLat,
|
|
479
|
+
...!viewerPhoto_0.isPano && viewerPhoto_0.heading != null ? {
|
|
480
|
+
bearing: viewerPhoto_0.heading
|
|
481
|
+
} : {}
|
|
482
|
+
});
|
|
483
|
+
emitEaseMapToPoint(...viewerPhoto_0.lngLat);
|
|
484
|
+
};
|
|
485
|
+
const flushBearing = () => {
|
|
486
|
+
bearingRafRef.current = null;
|
|
487
|
+
if (pendingBearingRef.current != null) {
|
|
488
|
+
actions.setPov({
|
|
489
|
+
bearing: pendingBearingRef.current
|
|
490
|
+
});
|
|
491
|
+
pendingBearingRef.current = null;
|
|
492
|
+
}
|
|
493
|
+
viewer.getFieldOfView().then((fov) => {
|
|
494
|
+
actions.setPov({
|
|
495
|
+
hfov: fov
|
|
496
|
+
});
|
|
497
|
+
}).catch(_temp2);
|
|
498
|
+
};
|
|
499
|
+
const onBearing = (event_1) => {
|
|
500
|
+
pendingBearingRef.current = event_1.bearing;
|
|
501
|
+
if (bearingRafRef.current == null) {
|
|
502
|
+
bearingRafRef.current = requestAnimationFrame(flushBearing);
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
viewer.on("navigable", onNavigable);
|
|
506
|
+
viewer.on("image", onImage);
|
|
507
|
+
viewer.on("bearing", onBearing);
|
|
508
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
509
|
+
viewer.resize();
|
|
510
|
+
});
|
|
511
|
+
resizeObserver.observe(container);
|
|
512
|
+
return () => {
|
|
513
|
+
if (bearingRafRef.current != null) {
|
|
514
|
+
cancelAnimationFrame(bearingRafRef.current);
|
|
515
|
+
bearingRafRef.current = null;
|
|
516
|
+
}
|
|
517
|
+
resizeObserver.disconnect();
|
|
518
|
+
viewer.off("navigable", onNavigable);
|
|
519
|
+
viewer.off("image", onImage);
|
|
520
|
+
viewer.off("bearing", onBearing);
|
|
521
|
+
viewer.remove();
|
|
522
|
+
viewerRef.current = null;
|
|
523
|
+
};
|
|
524
|
+
};
|
|
525
|
+
$[7] = actions;
|
|
526
|
+
$[8] = emitEaseMapToPoint;
|
|
527
|
+
$[9] = emitPhotoSelected;
|
|
528
|
+
$[10] = emitViewerPhoto;
|
|
529
|
+
$[11] = t5;
|
|
530
|
+
} else {
|
|
531
|
+
t5 = $[11];
|
|
532
|
+
}
|
|
533
|
+
let t6;
|
|
534
|
+
if ($[12] !== actions) {
|
|
535
|
+
t6 = [actions];
|
|
536
|
+
$[12] = actions;
|
|
537
|
+
$[13] = t6;
|
|
538
|
+
} else {
|
|
539
|
+
t6 = $[13];
|
|
540
|
+
}
|
|
541
|
+
useEffect(t5, t6);
|
|
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;
|
|
552
|
+
let t8;
|
|
553
|
+
if ($[16] !== emitViewerView || $[17] !== reportsView) {
|
|
554
|
+
t8 = function reportViewInPhoto() {
|
|
555
|
+
const viewer_0 = viewerRef.current;
|
|
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) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
actions.reset();
|
|
601
|
+
lastViewerPhotoIdRef.current = photo.photoId;
|
|
602
|
+
if (navigableRef.current) {
|
|
603
|
+
viewer_1.moveTo(photo.photoId).catch(_temp3);
|
|
604
|
+
} else {
|
|
605
|
+
pendingImageIdRef.current = photo.photoId;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
t11 = [actions, photo.photoId];
|
|
609
|
+
$[22] = actions;
|
|
610
|
+
$[23] = photo.photoId;
|
|
611
|
+
$[24] = t10;
|
|
612
|
+
$[25] = t11;
|
|
613
|
+
} else {
|
|
614
|
+
t10 = $[24];
|
|
615
|
+
t11 = $[25];
|
|
616
|
+
}
|
|
617
|
+
useEffect(t10, t11);
|
|
618
|
+
const attribution = hideAttribution ? null : shownPhoto;
|
|
619
|
+
const license = attribution?.details?.license;
|
|
620
|
+
let t12;
|
|
621
|
+
let t13;
|
|
622
|
+
if ($[26] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
623
|
+
t12 = {
|
|
624
|
+
aspectRatio: "4 / 3"
|
|
625
|
+
};
|
|
626
|
+
t13 = /* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full w-full" });
|
|
627
|
+
$[26] = t12;
|
|
628
|
+
$[27] = t13;
|
|
629
|
+
} else {
|
|
630
|
+
t12 = $[26];
|
|
631
|
+
t13 = $[27];
|
|
632
|
+
}
|
|
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: [
|
|
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 ") }),
|
|
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
|
|
638
|
+
] }) : null;
|
|
639
|
+
$[28] = attribution;
|
|
640
|
+
$[29] = license;
|
|
641
|
+
$[30] = t14;
|
|
642
|
+
} else {
|
|
643
|
+
t14 = $[30];
|
|
644
|
+
}
|
|
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
|
|
650
|
+
] });
|
|
651
|
+
$[31] = t14;
|
|
652
|
+
$[32] = t15;
|
|
653
|
+
} else {
|
|
654
|
+
t15 = $[32];
|
|
655
|
+
}
|
|
656
|
+
return t15;
|
|
657
|
+
};
|
|
658
|
+
function _temp() {
|
|
659
|
+
}
|
|
660
|
+
function _temp2() {
|
|
661
|
+
}
|
|
662
|
+
function _temp3() {
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export { PanoramaxDataProvider, PanoramaxPanel, panoramaxPhotoFromItem };
|