@osm-editor-kit/street-imagery-react 0.1.0-alpha.3 → 0.1.0-alpha.6
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 +113 -0
- package/dist/MapillaryPanel-N45NQY4C.js +2 -0
- package/dist/PanoramaxPanel-MFI4L67Z.js +2 -0
- package/dist/StreetLevelImagerySelectionOverlay.d.ts +20 -3
- package/dist/StreetLevelImagerySourcesAndLayers.d.ts +14 -1
- package/dist/StreetLevelImageryViewCone.d.ts +11 -1
- package/dist/StreetLevelImageryViewer.d.ts +15 -1
- package/dist/chunk-53RA4OSJ.js +304 -0
- package/dist/chunk-EQ2JKLGT.js +311 -0
- package/dist/chunk-HA4GJWHI.js +120 -0
- package/dist/hooks/useAllProviderMapFeatures.d.ts +0 -4
- package/dist/i18n/PhotoDate.d.ts +16 -0
- package/dist/i18n/StreetImageryLocaleProvider.d.ts +21 -0
- package/dist/i18n/messages.d.ts +35 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.js +984 -117
- package/dist/mapillary/MapillaryFeatureBar.d.ts +13 -0
- package/dist/mapillary/useMapillaryFeatures.d.ts +36 -0
- package/dist/openers/LocationPickOnMap.d.ts +10 -0
- package/dist/openers/useLocationPickStore.d.ts +9 -0
- package/dist/panels/MapillaryPanel.d.ts +19 -1
- package/dist/panels/PanoramaxPanel.d.ts +8 -1
- package/dist/panels/mapillaryLookAt.d.ts +41 -0
- package/dist/panels/panoramaxLimitPanning.d.ts +54 -0
- package/dist/panels/panoramaxPhotoFromMetadata.d.ts +8 -0
- package/dist/photo-details/PhotoDetailsDialog.d.ts +21 -0
- package/dist/photo-details/index.d.ts +6 -0
- package/dist/photo-details/labels.d.ts +33 -0
- package/dist/photo-details.js +235 -0
- package/dist/viewpoints/FloatingPhotoViewer.d.ts +54 -0
- package/dist/viewpoints/ViewpointLayer.d.ts +17 -0
- package/dist/viewpoints/useViewSuggestions.d.ts +23 -0
- package/dist/viewpoints/useViewpointSessionStore.d.ts +32 -0
- package/dist/viewpoints/viewDirectionLabels.d.ts +4 -0
- package/package.json +11 -3
- package/dist/MapillaryPanel-QX6NESX7.js +0 -2
- package/dist/PanoramaxPanel-J6ANR6LB.js +0 -2
- package/dist/chunk-BS6LPDMY.js +0 -143
- package/dist/chunk-ZH3TPW4D.js +0 -165
package/dist/index.js
CHANGED
|
@@ -1,66 +1,127 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
1
|
+
import { useStreetImageryI18n } from './chunk-HA4GJWHI.js';
|
|
2
|
+
export { STREET_IMAGERY_MESSAGES, StreetImageryLocaleProvider, useStreetImageryI18n } from './chunk-HA4GJWHI.js';
|
|
3
|
+
export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo } from './chunk-53RA4OSJ.js';
|
|
4
|
+
export { PanoramaxPanel } from './chunk-EQ2JKLGT.js';
|
|
3
5
|
export { useViewerActions, useViewerBearing, useViewerHfov, useViewerLngLat } from './chunk-25UGGXIE.js';
|
|
4
|
-
import {
|
|
6
|
+
import { distanceMeters, sequencesToFeatureCollection, emptyLineCollection, adapterById, viewConeGeoJson, viewShapeRadiusMeters, setStreetImageryConfig, viewfieldLayerId, photoLayerId, featureLayerId, PROVIDER_IDS, mapFeatureMatchesDateRange, isBrowserAvailableProvider, photoMatchesFilters, resolvePhotoPanoramaUrl, photoFullUrlQueryKey, resolvePhotoThumbnailUrl, photoThumbnailQueryKey, formatLocalDateTime, formatMonth, formatRelativeDate, formatDateTooltip, mapillaryPhotoSource, buildViewSuggestions, yearsToMs, SIGN_TARGET, fetchMapFeatureImages, fetchMapillaryMapFeaturesInBbox, bboxAround, fetchImageDetections, findLocationOpener, mapillaryIconUrl, openLocationInNewTab, alignLineToPoints, coneRadiusMeters, mapFeaturesToFeatureCollection, emptyPointCollection, buildMapFeatureLayerFilter, featureSourceId, photosToFeatureCollection, photosToViewfieldsFeatureCollection, emptyPolygonCollection, buildPhotoLayerFilter, photoSourceId, viewfieldSourceId, sequenceSourceId, sequenceLayerId, viewfieldLineLayerId, VIEWFIELD_MIN_ZOOM } from '@osm-editor-kit/street-imagery';
|
|
5
7
|
import { Source, Layer, useMap } from 'react-map-gl/maplibre';
|
|
6
8
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
7
|
-
import { lazy, useEffect, Fragment as Fragment$1, Suspense, useState } from 'react';
|
|
8
|
-
import { useQuery } from '@tanstack/react-query';
|
|
9
|
+
import { lazy, useEffect, Fragment as Fragment$1, Suspense, useState, useRef, useEffectEvent } from 'react';
|
|
10
|
+
import { useQuery, useQueries } from '@tanstack/react-query';
|
|
11
|
+
import { create } from 'zustand';
|
|
9
12
|
|
|
10
|
-
var
|
|
11
|
-
var
|
|
13
|
+
var POINTS_SOURCE_ID = "selection-highlight";
|
|
14
|
+
var CONNECTOR_SOURCE_ID = "selection-connector";
|
|
12
15
|
var SEQUENCE_HIGHLIGHT_SOURCE_ID = "sequence-highlight";
|
|
13
|
-
var
|
|
16
|
+
var BASE_COLOR = "#171717";
|
|
17
|
+
var SELECTION_COLOR = "#f97316";
|
|
18
|
+
var SAME_POSITION_METERS = 0.75;
|
|
19
|
+
var ACTIVE_LINE_WIDTH = 2.5;
|
|
14
20
|
var StreetLevelImagerySelectionOverlay = ({
|
|
15
21
|
selectedPhoto,
|
|
16
|
-
selectedSequence
|
|
22
|
+
selectedSequence,
|
|
23
|
+
cameraLngLat,
|
|
24
|
+
sequenceBeforeId
|
|
17
25
|
}) => {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
26
|
+
const photoLngLat = selectedPhoto?.lngLat;
|
|
27
|
+
const cameraDiffers = photoLngLat != null && cameraLngLat != null && distanceMeters(photoLngLat, cameraLngLat) > SAME_POSITION_METERS;
|
|
28
|
+
const camera = cameraDiffers ? cameraLngLat : null;
|
|
29
|
+
const dot = photoLngLat && cameraLngLat && !cameraDiffers ? cameraLngLat : photoLngLat;
|
|
30
|
+
const sequenceLines = selectedSequence ? sequencesToFeatureCollection([selectedSequence]) : emptyLineCollection();
|
|
31
|
+
const throughDot = (line) => dot ? alignLineToPoints(line, [dot]) : line;
|
|
32
|
+
const sequenceCollection = {
|
|
33
|
+
...sequenceLines,
|
|
34
|
+
features: sequenceLines.features.map((feature) => ({
|
|
35
|
+
...feature,
|
|
36
|
+
geometry: feature.geometry.type === "LineString" ? { ...feature.geometry, coordinates: throughDot(feature.geometry.coordinates) } : { ...feature.geometry, coordinates: feature.geometry.coordinates.map(throughDot) }
|
|
37
|
+
}))
|
|
38
|
+
};
|
|
39
|
+
if (!selectedPhoto && sequenceCollection.features.length === 0) {
|
|
21
40
|
return null;
|
|
22
41
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Layer,
|
|
42
|
+
const points = {
|
|
43
|
+
type: "FeatureCollection",
|
|
44
|
+
features: [
|
|
45
|
+
...dot ? [
|
|
28
46
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
paint: {
|
|
33
|
-
"circle-radius": 10,
|
|
34
|
-
"circle-color": "#ffffff",
|
|
35
|
-
"circle-stroke-width": 3,
|
|
36
|
-
"circle-stroke-color": "#0f172a"
|
|
37
|
-
}
|
|
47
|
+
type: "Feature",
|
|
48
|
+
properties: { kind: "dot" },
|
|
49
|
+
geometry: { type: "Point", coordinates: dot }
|
|
38
50
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
sequenceHighlightCollection.features.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42
|
-
/* @__PURE__ */ jsx(
|
|
43
|
-
Source,
|
|
51
|
+
] : [],
|
|
52
|
+
...camera ? [
|
|
44
53
|
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
type: "Feature",
|
|
55
|
+
properties: { kind: "camera" },
|
|
56
|
+
geometry: { type: "Point", coordinates: camera }
|
|
48
57
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
] : []
|
|
59
|
+
]
|
|
60
|
+
};
|
|
61
|
+
const connector = {
|
|
62
|
+
type: "FeatureCollection",
|
|
63
|
+
features: dot && camera ? [
|
|
64
|
+
{
|
|
65
|
+
type: "Feature",
|
|
66
|
+
properties: {},
|
|
67
|
+
geometry: { type: "LineString", coordinates: [dot, camera] }
|
|
68
|
+
}
|
|
69
|
+
] : []
|
|
70
|
+
};
|
|
71
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
72
|
+
/* @__PURE__ */ jsx(Source, { id: SEQUENCE_HIGHLIGHT_SOURCE_ID, type: "geojson", data: sequenceCollection }),
|
|
73
|
+
/* @__PURE__ */ jsx(
|
|
74
|
+
Layer,
|
|
75
|
+
{
|
|
76
|
+
beforeId: sequenceBeforeId,
|
|
77
|
+
id: "sequence-highlight-layer",
|
|
78
|
+
type: "line",
|
|
79
|
+
source: SEQUENCE_HIGHLIGHT_SOURCE_ID,
|
|
80
|
+
layout: { "line-cap": "round", "line-join": "round" },
|
|
81
|
+
paint: { "line-color": BASE_COLOR, "line-width": ACTIVE_LINE_WIDTH }
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
/* @__PURE__ */ jsx(Source, { id: CONNECTOR_SOURCE_ID, type: "geojson", data: connector }),
|
|
85
|
+
/* @__PURE__ */ jsx(
|
|
86
|
+
Layer,
|
|
87
|
+
{
|
|
88
|
+
id: "selection-connector-layer",
|
|
89
|
+
type: "line",
|
|
90
|
+
source: CONNECTOR_SOURCE_ID,
|
|
91
|
+
paint: { "line-color": SELECTION_COLOR, "line-width": 1.5, "line-dasharray": [2, 1.5] }
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
/* @__PURE__ */ jsx(Source, { id: POINTS_SOURCE_ID, type: "geojson", data: points }),
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
Layer,
|
|
97
|
+
{
|
|
98
|
+
id: "selection-highlight-layer",
|
|
99
|
+
type: "circle",
|
|
100
|
+
source: POINTS_SOURCE_ID,
|
|
101
|
+
filter: ["==", ["get", "kind"], "dot"],
|
|
102
|
+
paint: {
|
|
103
|
+
"circle-radius": 6,
|
|
104
|
+
"circle-color": SELECTION_COLOR,
|
|
105
|
+
"circle-stroke-width": 2,
|
|
106
|
+
"circle-stroke-color": BASE_COLOR
|
|
61
107
|
}
|
|
62
|
-
|
|
63
|
-
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
Layer,
|
|
112
|
+
{
|
|
113
|
+
id: "selection-camera-layer",
|
|
114
|
+
type: "circle",
|
|
115
|
+
source: POINTS_SOURCE_ID,
|
|
116
|
+
filter: ["==", ["get", "kind"], "camera"],
|
|
117
|
+
paint: {
|
|
118
|
+
"circle-radius": 3.5,
|
|
119
|
+
"circle-color": SELECTION_COLOR,
|
|
120
|
+
"circle-stroke-width": 1.5,
|
|
121
|
+
"circle-stroke-color": BASE_COLOR
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
)
|
|
64
125
|
] });
|
|
65
126
|
};
|
|
66
127
|
var resolveSelectedSequence = (selectedPhoto, sequences, sequenceId) => {
|
|
@@ -115,7 +176,8 @@ var useProviderMapFeatures = (providerId, bbox, zoom) => {
|
|
|
115
176
|
};
|
|
116
177
|
var CONE_SOURCE_ID = "view-direction-cone";
|
|
117
178
|
var CONE_FILL_LAYER_ID = "view-direction-cone-fill";
|
|
118
|
-
var
|
|
179
|
+
var MIN_CONE_FOV_DEG = 45;
|
|
180
|
+
var VIEW_SHAPE_FILL_OPACITY = 0.25;
|
|
119
181
|
var INTERACTIVE_PANO_PROVIDERS = /* @__PURE__ */ new Set([
|
|
120
182
|
"mapillary",
|
|
121
183
|
"panoramax",
|
|
@@ -127,7 +189,9 @@ var INTERACTIVE_PANO_PROVIDERS = /* @__PURE__ */ new Set([
|
|
|
127
189
|
var StreetLevelImageryViewCone = ({
|
|
128
190
|
selectedPhoto,
|
|
129
191
|
zoom,
|
|
130
|
-
viewerPov
|
|
192
|
+
viewerPov,
|
|
193
|
+
color = SELECTION_COLOR,
|
|
194
|
+
scale = 2.5
|
|
131
195
|
}) => {
|
|
132
196
|
const apex = viewerPov?.lngLat ?? selectedPhoto.lngLat;
|
|
133
197
|
const isPano = selectedPhoto.isPano === true;
|
|
@@ -147,7 +211,11 @@ var StreetLevelImageryViewCone = ({
|
|
|
147
211
|
if (bearing == null) {
|
|
148
212
|
return null;
|
|
149
213
|
}
|
|
150
|
-
|
|
214
|
+
fov = Math.max(fov, MIN_CONE_FOV_DEG);
|
|
215
|
+
const coneFeature = {
|
|
216
|
+
...viewConeGeoJson(apex, bearing, fov, viewShapeRadiusMeters(zoom) * scale),
|
|
217
|
+
properties: { isPano: selectedPhoto.isPano, capturedAt: selectedPhoto.capturedAt }
|
|
218
|
+
};
|
|
151
219
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
152
220
|
/* @__PURE__ */ jsx(Source, { id: CONE_SOURCE_ID, type: "geojson", data: coneFeature }),
|
|
153
221
|
/* @__PURE__ */ jsx(
|
|
@@ -157,37 +225,14 @@ var StreetLevelImageryViewCone = ({
|
|
|
157
225
|
type: "fill",
|
|
158
226
|
source: CONE_SOURCE_ID,
|
|
159
227
|
paint: {
|
|
160
|
-
"fill-color":
|
|
161
|
-
"fill-opacity":
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
),
|
|
165
|
-
/* @__PURE__ */ jsx(
|
|
166
|
-
Layer,
|
|
167
|
-
{
|
|
168
|
-
id: CONE_LINE_LAYER_ID,
|
|
169
|
-
type: "line",
|
|
170
|
-
source: CONE_SOURCE_ID,
|
|
171
|
-
paint: {
|
|
172
|
-
"line-color": "#0f172a",
|
|
173
|
-
"line-width": 1.5,
|
|
174
|
-
"line-opacity": 0.5
|
|
228
|
+
"fill-color": color,
|
|
229
|
+
"fill-opacity": VIEW_SHAPE_FILL_OPACITY,
|
|
230
|
+
"fill-outline-color": "rgba(0, 0, 0, 0)"
|
|
175
231
|
}
|
|
176
232
|
}
|
|
177
233
|
)
|
|
178
234
|
] });
|
|
179
235
|
};
|
|
180
|
-
var CIRCLE_RADIUS = [
|
|
181
|
-
"interpolate",
|
|
182
|
-
["linear"],
|
|
183
|
-
["zoom"],
|
|
184
|
-
10,
|
|
185
|
-
2,
|
|
186
|
-
14,
|
|
187
|
-
4,
|
|
188
|
-
18,
|
|
189
|
-
6
|
|
190
|
-
];
|
|
191
236
|
var FEATURE_CIRCLE_RADIUS = [
|
|
192
237
|
"interpolate",
|
|
193
238
|
["linear"],
|
|
@@ -199,6 +244,30 @@ var FEATURE_CIRCLE_RADIUS = [
|
|
|
199
244
|
18,
|
|
200
245
|
4
|
|
201
246
|
];
|
|
247
|
+
var padBbox = ([west, south, east, north], factor) => {
|
|
248
|
+
const dx = (east - west) * factor;
|
|
249
|
+
const dy = (north - south) * factor;
|
|
250
|
+
return [west - dx, south - dy, east + dx, north + dy];
|
|
251
|
+
};
|
|
252
|
+
var lngLatInBbox = ([lng, lat], [west, south, east, north]) => lng >= west && lng <= east && lat >= south && lat <= north;
|
|
253
|
+
var YEAR_MS = 365.25 * 24 * 60 * 60 * 1e3;
|
|
254
|
+
var LOADED_AT = Date.now();
|
|
255
|
+
var AGE_SHADE = [
|
|
256
|
+
"case",
|
|
257
|
+
["==", ["get", "capturedAt"], null],
|
|
258
|
+
BASE_COLOR,
|
|
259
|
+
[
|
|
260
|
+
"step",
|
|
261
|
+
["get", "capturedAt"],
|
|
262
|
+
"#909090",
|
|
263
|
+
LOADED_AT - 3 * YEAR_MS,
|
|
264
|
+
"#686868",
|
|
265
|
+
LOADED_AT - 2 * YEAR_MS,
|
|
266
|
+
"#404040",
|
|
267
|
+
LOADED_AT - YEAR_MS,
|
|
268
|
+
BASE_COLOR
|
|
269
|
+
]
|
|
270
|
+
];
|
|
202
271
|
var PHOTO_SORT_KEY = ["coalesce", ["get", "capturedAt"], 0];
|
|
203
272
|
var FEATURE_SORT_KEY = ["coalesce", ["get", "lastSeenAt"], 0];
|
|
204
273
|
var PhotoProviderLayer = ({
|
|
@@ -208,14 +277,56 @@ var PhotoProviderLayer = ({
|
|
|
208
277
|
filter,
|
|
209
278
|
showSequences,
|
|
210
279
|
showViewfields,
|
|
211
|
-
photoCircleColor
|
|
280
|
+
photoCircleColor,
|
|
281
|
+
activeSequenceId,
|
|
282
|
+
hasActiveSequence,
|
|
283
|
+
shownPhotoId
|
|
212
284
|
}) => {
|
|
213
285
|
const adapter = adapterById[providerId];
|
|
214
286
|
const { data: photos = [] } = useProviderPhotos(providerId, bbox, zoom);
|
|
215
287
|
const { data: sequences = [] } = useProviderSequences(providerId, bbox, zoom);
|
|
216
288
|
const visiblePhotos = zoom >= adapter.minZoom ? photos.filter((photo) => photoMatchesFilters(photo, filter?.photoTypes, filter?.date)) : [];
|
|
217
|
-
const
|
|
218
|
-
const
|
|
289
|
+
const drawBbox = bbox ? padBbox(bbox, 0.25) : null;
|
|
290
|
+
const photoCollection = zoom >= adapter.minZoom ? photosToFeatureCollection(
|
|
291
|
+
drawBbox ? visiblePhotos.filter((photo) => lngLatInBbox(photo.lngLat, drawBbox)) : visiblePhotos
|
|
292
|
+
) : emptyPointCollection();
|
|
293
|
+
const viewfieldsActive = showViewfields && zoom >= VIEWFIELD_MIN_ZOOM;
|
|
294
|
+
const viewfieldCollection = viewfieldsActive ? photosToViewfieldsFeatureCollection(visiblePhotos, zoom, { bbox }) : emptyPolygonCollection();
|
|
295
|
+
const inActiveSequence = [
|
|
296
|
+
"==",
|
|
297
|
+
["get", "sequenceId"],
|
|
298
|
+
activeSequenceId ?? ""
|
|
299
|
+
];
|
|
300
|
+
const lineOpacity = [
|
|
301
|
+
"case",
|
|
302
|
+
inActiveSequence,
|
|
303
|
+
1,
|
|
304
|
+
hasActiveSequence ? 0.45 : 1
|
|
305
|
+
];
|
|
306
|
+
const dotRadius = [
|
|
307
|
+
"interpolate",
|
|
308
|
+
["linear"],
|
|
309
|
+
["zoom"],
|
|
310
|
+
10,
|
|
311
|
+
["case", inActiveSequence, 2, 1.2],
|
|
312
|
+
14,
|
|
313
|
+
["case", inActiveSequence, 4, 2],
|
|
314
|
+
// Up to here there are no view shapes and the dots are 20 % smaller.
|
|
315
|
+
VIEWFIELD_MIN_ZOOM - 0.01,
|
|
316
|
+
["case", inActiveSequence, 5.5, 2.9],
|
|
317
|
+
VIEWFIELD_MIN_ZOOM,
|
|
318
|
+
["case", inActiveSequence, 5.5, 3.6],
|
|
319
|
+
18,
|
|
320
|
+
["case", inActiveSequence, 6, 4]
|
|
321
|
+
];
|
|
322
|
+
const lineAndDotColor = viewfieldsActive ? ["case", inActiveSequence, BASE_COLOR, AGE_SHADE] : photoCircleColor;
|
|
323
|
+
const viewShapeOpacity = (full) => [
|
|
324
|
+
"case",
|
|
325
|
+
inActiveSequence,
|
|
326
|
+
full,
|
|
327
|
+
hasActiveSequence ? full * 0.4 : full
|
|
328
|
+
];
|
|
329
|
+
const notShownPhoto = ["!=", ["get", "photoId"], shownPhotoId ?? ""];
|
|
219
330
|
const photoFilter = buildPhotoLayerFilter(filter?.photoTypes, filter?.date);
|
|
220
331
|
const photoSrcId = photoSourceId(providerId);
|
|
221
332
|
const viewfieldSrcId = viewfieldSourceId(providerId);
|
|
@@ -243,7 +354,34 @@ var PhotoProviderLayer = ({
|
|
|
243
354
|
};
|
|
244
355
|
return photoMatchesFilters(asPhoto, filter?.photoTypes, filter?.date);
|
|
245
356
|
}) : [];
|
|
246
|
-
const
|
|
357
|
+
const photoLngLatsBySequence = /* @__PURE__ */ new Map();
|
|
358
|
+
for (const photo of photos) {
|
|
359
|
+
if (photo.sequenceId == null) continue;
|
|
360
|
+
const list = photoLngLatsBySequence.get(photo.sequenceId);
|
|
361
|
+
if (list) {
|
|
362
|
+
list.push(photo.lngLat);
|
|
363
|
+
} else {
|
|
364
|
+
photoLngLatsBySequence.set(photo.sequenceId, [photo.lngLat]);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const alignedSequences = filteredSequences.map((sequence) => {
|
|
368
|
+
const points = photoLngLatsBySequence.get(sequence.sequenceId);
|
|
369
|
+
if (!points) return sequence;
|
|
370
|
+
const { geometry } = sequence;
|
|
371
|
+
return {
|
|
372
|
+
...sequence,
|
|
373
|
+
geometry: geometry.type === "LineString" ? {
|
|
374
|
+
...geometry,
|
|
375
|
+
coordinates: alignLineToPoints(geometry.coordinates, points)
|
|
376
|
+
} : {
|
|
377
|
+
...geometry,
|
|
378
|
+
coordinates: geometry.coordinates.map(
|
|
379
|
+
(line) => alignLineToPoints(line, points)
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
const sequenceCollection = alignedSequences.length > 0 ? sequencesToFeatureCollection(alignedSequences) : emptyLineCollection();
|
|
247
385
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
248
386
|
adapter.fetchSequences && showSequences ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
249
387
|
/* @__PURE__ */ jsx(
|
|
@@ -261,10 +399,15 @@ var PhotoProviderLayer = ({
|
|
|
261
399
|
id: sequenceLayerId(providerId),
|
|
262
400
|
type: "line",
|
|
263
401
|
source: sequenceSourceId(providerId),
|
|
402
|
+
layout: {
|
|
403
|
+
"line-cap": "round",
|
|
404
|
+
"line-join": "round",
|
|
405
|
+
"line-sort-key": ["case", inActiveSequence, 1, 0]
|
|
406
|
+
},
|
|
264
407
|
paint: {
|
|
265
|
-
"line-color":
|
|
266
|
-
"line-width":
|
|
267
|
-
"line-opacity":
|
|
408
|
+
"line-color": lineAndDotColor,
|
|
409
|
+
"line-width": ["case", inActiveSequence, ACTIVE_LINE_WIDTH, 1.25],
|
|
410
|
+
"line-opacity": lineOpacity
|
|
268
411
|
}
|
|
269
412
|
}
|
|
270
413
|
)
|
|
@@ -285,9 +428,11 @@ var PhotoProviderLayer = ({
|
|
|
285
428
|
id: viewfieldLayerId(providerId),
|
|
286
429
|
type: "fill",
|
|
287
430
|
source: viewfieldSrcId,
|
|
431
|
+
filter: notShownPhoto,
|
|
288
432
|
paint: {
|
|
289
433
|
"fill-color": photoCircleColor,
|
|
290
|
-
"fill-opacity":
|
|
434
|
+
"fill-opacity": viewShapeOpacity(VIEW_SHAPE_FILL_OPACITY),
|
|
435
|
+
"fill-outline-color": "rgba(0, 0, 0, 0)"
|
|
291
436
|
}
|
|
292
437
|
}
|
|
293
438
|
),
|
|
@@ -297,10 +442,11 @@ var PhotoProviderLayer = ({
|
|
|
297
442
|
id: viewfieldLineLayerId(providerId),
|
|
298
443
|
type: "line",
|
|
299
444
|
source: viewfieldSrcId,
|
|
445
|
+
filter: notShownPhoto,
|
|
300
446
|
paint: {
|
|
301
447
|
"line-color": photoCircleColor,
|
|
302
|
-
"line-width":
|
|
303
|
-
"line-opacity": 0.
|
|
448
|
+
"line-width": 0.5,
|
|
449
|
+
"line-opacity": viewShapeOpacity(0.3)
|
|
304
450
|
}
|
|
305
451
|
}
|
|
306
452
|
)
|
|
@@ -324,10 +470,8 @@ var PhotoProviderLayer = ({
|
|
|
324
470
|
filter: photoFilter,
|
|
325
471
|
layout: { "circle-sort-key": PHOTO_SORT_KEY },
|
|
326
472
|
paint: {
|
|
327
|
-
"circle-radius":
|
|
328
|
-
"circle-color":
|
|
329
|
-
"circle-stroke-width": 1,
|
|
330
|
-
"circle-stroke-color": "#ffffff"
|
|
473
|
+
"circle-radius": dotRadius,
|
|
474
|
+
"circle-color": lineAndDotColor
|
|
331
475
|
}
|
|
332
476
|
}
|
|
333
477
|
)
|
|
@@ -342,7 +486,10 @@ var MapFeatureProviderLayer = ({
|
|
|
342
486
|
}) => {
|
|
343
487
|
const adapter = adapterById[providerId];
|
|
344
488
|
const { data: features = [] } = useProviderMapFeatures(providerId, bbox, zoom);
|
|
345
|
-
const
|
|
489
|
+
const valueFilter = filter?.mapFeatureValue;
|
|
490
|
+
const featureCollection = zoom >= adapter.minZoom ? mapFeaturesToFeatureCollection(
|
|
491
|
+
valueFilter ? features.filter((feature) => valueFilter(feature.value)) : features
|
|
492
|
+
) : emptyPointCollection();
|
|
346
493
|
const featureFilter = buildMapFeatureLayerFilter(filter?.date);
|
|
347
494
|
const featureSrcId = featureSourceId(providerId);
|
|
348
495
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -366,9 +513,7 @@ var MapFeatureProviderLayer = ({
|
|
|
366
513
|
layout: { "circle-sort-key": FEATURE_SORT_KEY },
|
|
367
514
|
paint: {
|
|
368
515
|
"circle-radius": FEATURE_CIRCLE_RADIUS,
|
|
369
|
-
"circle-color": mapFeatureCircleColor
|
|
370
|
-
"circle-stroke-width": 1,
|
|
371
|
-
"circle-stroke-color": "#ffffff"
|
|
516
|
+
"circle-color": mapFeatureCircleColor
|
|
372
517
|
}
|
|
373
518
|
}
|
|
374
519
|
)
|
|
@@ -394,6 +539,8 @@ var StreetLevelImagerySourcesAndLayers = ({
|
|
|
394
539
|
selectedPhoto,
|
|
395
540
|
selectedSequenceId,
|
|
396
541
|
viewerPov,
|
|
542
|
+
viewConeColor,
|
|
543
|
+
viewConeScale,
|
|
397
544
|
photoCircleColor,
|
|
398
545
|
mapFeatureCircleColor
|
|
399
546
|
} = options;
|
|
@@ -411,11 +558,13 @@ var StreetLevelImagerySourcesAndLayers = ({
|
|
|
411
558
|
showSelectionHighlight && selectedProviderId ? bbox : null,
|
|
412
559
|
zoom
|
|
413
560
|
);
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
561
|
+
const activeSequenceId = selectedSequenceId ?? selectedPhoto?.sequenceId;
|
|
562
|
+
const selectedSequence = resolveSelectedSequence(selectedPhoto, sequences, activeSequenceId);
|
|
563
|
+
const providerDrawsSequences = selectedProviderId != null && showSequences && providers.includes(selectedProviderId);
|
|
564
|
+
const firstPhotoProvider = providers.find(
|
|
565
|
+
(id) => adapterById[id].kind === "photo" && adapterById[id].fetchPhotos
|
|
418
566
|
);
|
|
567
|
+
const sequenceBeforeId = firstPhotoProvider ? showViewfields ? viewfieldLayerId(firstPhotoProvider) : photoLayerId(firstPhotoProvider) : void 0;
|
|
419
568
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
420
569
|
providers.map((providerId) => /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
|
|
421
570
|
ProviderLayer,
|
|
@@ -423,6 +572,9 @@ var StreetLevelImagerySourcesAndLayers = ({
|
|
|
423
572
|
bbox,
|
|
424
573
|
filter,
|
|
425
574
|
mapFeatureCircleColor,
|
|
575
|
+
activeSequenceId: providerId === selectedProviderId ? activeSequenceId : null,
|
|
576
|
+
hasActiveSequence: activeSequenceId != null,
|
|
577
|
+
shownPhotoId: showViewCone && providerId === selectedProviderId ? selectedPhoto?.photoId : null,
|
|
426
578
|
photoCircleColor,
|
|
427
579
|
providerId,
|
|
428
580
|
showSequences,
|
|
@@ -433,6 +585,8 @@ var StreetLevelImagerySourcesAndLayers = ({
|
|
|
433
585
|
showViewCone && selectedPhoto ? /* @__PURE__ */ jsx(
|
|
434
586
|
StreetLevelImageryViewCone,
|
|
435
587
|
{
|
|
588
|
+
color: viewConeColor ?? SELECTION_COLOR,
|
|
589
|
+
scale: viewConeScale,
|
|
436
590
|
selectedPhoto,
|
|
437
591
|
viewerPov,
|
|
438
592
|
zoom
|
|
@@ -441,36 +595,51 @@ var StreetLevelImagerySourcesAndLayers = ({
|
|
|
441
595
|
showSelectionHighlight ? /* @__PURE__ */ jsx(
|
|
442
596
|
StreetLevelImagerySelectionOverlay,
|
|
443
597
|
{
|
|
598
|
+
cameraLngLat: viewerPov?.lngLat,
|
|
444
599
|
selectedPhoto,
|
|
445
|
-
selectedSequence
|
|
600
|
+
selectedSequence: providerDrawsSequences ? null : selectedSequence,
|
|
601
|
+
sequenceBeforeId
|
|
446
602
|
}
|
|
447
603
|
) : null
|
|
448
604
|
] });
|
|
449
605
|
};
|
|
450
606
|
var MapillaryPanel2 = lazy(
|
|
451
|
-
() => import('./MapillaryPanel-
|
|
607
|
+
() => import('./MapillaryPanel-N45NQY4C.js').then((module) => ({
|
|
452
608
|
default: module.MapillaryPanel
|
|
453
609
|
}))
|
|
454
610
|
);
|
|
455
611
|
var PanoramaxPanel2 = lazy(
|
|
456
|
-
() => import('./PanoramaxPanel-
|
|
612
|
+
() => import('./PanoramaxPanel-MFI4L67Z.js').then((module) => ({
|
|
457
613
|
default: module.PanoramaxPanel
|
|
458
614
|
}))
|
|
459
615
|
);
|
|
460
|
-
var ViewerPanelPlaceholder = () =>
|
|
616
|
+
var ViewerPanelPlaceholder = () => {
|
|
617
|
+
const { messages } = useStreetImageryI18n();
|
|
618
|
+
return /* @__PURE__ */ jsx("div", { className: "flex min-h-48 animate-pulse items-center justify-center rounded-lg border border-slate-200 bg-slate-100", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-500", children: messages.viewer.loading }) });
|
|
619
|
+
};
|
|
461
620
|
var StreetLevelImageryViewer = ({
|
|
462
621
|
photo,
|
|
463
622
|
groupPhotos,
|
|
464
623
|
onPhotoSelected,
|
|
465
|
-
onEaseMapToPoint
|
|
624
|
+
onEaseMapToPoint,
|
|
625
|
+
onViewerPhoto,
|
|
626
|
+
lookAtBearing,
|
|
627
|
+
lookAt,
|
|
628
|
+
outlines,
|
|
629
|
+
hideAttribution
|
|
466
630
|
}) => {
|
|
467
631
|
if (photo.providerId === "mapillary") {
|
|
468
632
|
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerPanelPlaceholder, {}), children: /* @__PURE__ */ jsx(
|
|
469
633
|
MapillaryPanel2,
|
|
470
634
|
{
|
|
471
635
|
groupPhotos,
|
|
636
|
+
hideAttribution,
|
|
472
637
|
onEaseMapToPoint,
|
|
473
638
|
onPhotoSelected,
|
|
639
|
+
lookAt,
|
|
640
|
+
lookAtBearing,
|
|
641
|
+
outlines,
|
|
642
|
+
onViewerPhoto,
|
|
474
643
|
photo
|
|
475
644
|
}
|
|
476
645
|
) });
|
|
@@ -480,6 +649,8 @@ var StreetLevelImageryViewer = ({
|
|
|
480
649
|
PanoramaxPanel2,
|
|
481
650
|
{
|
|
482
651
|
groupPhotos,
|
|
652
|
+
hideLegend: hideAttribution,
|
|
653
|
+
onViewerPhoto,
|
|
483
654
|
onEaseMapToPoint,
|
|
484
655
|
onPhotoSelected,
|
|
485
656
|
photo
|
|
@@ -567,18 +738,6 @@ var useAllProviderMapFeatures = (providerIds, bbox, zoom, date) => {
|
|
|
567
738
|
return data.filter((feature) => mapFeatureMatchesDateRange(feature, date));
|
|
568
739
|
});
|
|
569
740
|
};
|
|
570
|
-
var useAllProviderMapFeaturesLoading = (providerIds, bbox, zoom) => {
|
|
571
|
-
const enabled = new Set(providerIds);
|
|
572
|
-
const queries = PROVIDER_IDS.map((providerId) => ({
|
|
573
|
-
providerId,
|
|
574
|
-
query: useProviderMapFeaturesMaybe(providerId, enabled.has(providerId), bbox, zoom)
|
|
575
|
-
}));
|
|
576
|
-
const activeQueries = queries.filter(({ providerId }) => enabled.has(providerId));
|
|
577
|
-
return {
|
|
578
|
-
isLoading: activeQueries.some(({ query }) => query.isLoading),
|
|
579
|
-
isFetching: activeQueries.some(({ query }) => query.isFetching)
|
|
580
|
-
};
|
|
581
|
-
};
|
|
582
741
|
var useProviderPhotosMaybe = (providerId, enabled, bbox, zoom) => useProviderPhotos(providerId, enabled ? bbox : null, zoom);
|
|
583
742
|
var useAllProviderPhotos = (providerIds, bbox, zoom, photoTypes, date) => {
|
|
584
743
|
const enabled = new Set(providerIds);
|
|
@@ -655,5 +814,713 @@ var usePhotoThumbnail = (photo) => useQuery({
|
|
|
655
814
|
enabled: photo != null,
|
|
656
815
|
staleTime: 24 * 60 * 60 * 1e3
|
|
657
816
|
});
|
|
817
|
+
var PhotoDate = ({ timestamp, className, localDateTime }) => {
|
|
818
|
+
const { locale, messages } = useStreetImageryI18n();
|
|
819
|
+
if (timestamp == null) {
|
|
820
|
+
return /* @__PURE__ */ jsx("span", { className, children: messages.unknownDate });
|
|
821
|
+
}
|
|
822
|
+
const precise = localDateTime ? formatLocalDateTime(localDateTime, locale) : null;
|
|
823
|
+
return /* @__PURE__ */ jsx(
|
|
824
|
+
"time",
|
|
825
|
+
{
|
|
826
|
+
className,
|
|
827
|
+
dateTime: new Date(timestamp).toISOString(),
|
|
828
|
+
title: precise ? `${precise}
|
|
829
|
+
${formatRelativeDate(timestamp, locale)}` : formatDateTooltip(timestamp, locale),
|
|
830
|
+
children: formatMonth(timestamp, locale)
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
// src/viewpoints/viewDirectionLabels.ts
|
|
836
|
+
var viewSuggestionLabel = ({ viewpoint, direction }, messages) => messages.viewDirection(viewpoint.label ?? messages.viewpointRole[viewpoint.role], direction.kind);
|
|
837
|
+
var DEFAULT_LAYOUT = { right: 16, bottom: 32, width: 420, minimized: false };
|
|
838
|
+
var MIN_WIDTH = 280;
|
|
839
|
+
var EDGE = 8;
|
|
840
|
+
var readLayout = (storageKey) => {
|
|
841
|
+
try {
|
|
842
|
+
const raw = localStorage.getItem(storageKey);
|
|
843
|
+
return raw ? { ...DEFAULT_LAYOUT, ...JSON.parse(raw) } : DEFAULT_LAYOUT;
|
|
844
|
+
} catch {
|
|
845
|
+
return DEFAULT_LAYOUT;
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
var writeLayout = (storageKey, layout) => {
|
|
849
|
+
try {
|
|
850
|
+
localStorage.setItem(storageKey, JSON.stringify(layout));
|
|
851
|
+
} catch {
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
var Icon = ({ path, className = "size-4" }) => /* @__PURE__ */ jsx(
|
|
855
|
+
"svg",
|
|
856
|
+
{
|
|
857
|
+
"aria-hidden": true,
|
|
858
|
+
className,
|
|
859
|
+
fill: "none",
|
|
860
|
+
stroke: "currentColor",
|
|
861
|
+
strokeLinecap: "round",
|
|
862
|
+
strokeLinejoin: "round",
|
|
863
|
+
strokeWidth: 1.75,
|
|
864
|
+
viewBox: "0 0 24 24",
|
|
865
|
+
children: /* @__PURE__ */ jsx("path", { d: path })
|
|
866
|
+
}
|
|
867
|
+
);
|
|
868
|
+
var ICONS = {
|
|
869
|
+
back: "M15 18l-6-6 6-6",
|
|
870
|
+
undo: "M9 14 4 9l5-5M4 9h10.5a5.5 5.5 0 0 1 0 11H11",
|
|
871
|
+
redo: "m15 14 5-5-5-5M20 9H9.5a5.5 5.5 0 0 0 0 11H13",
|
|
872
|
+
forward: "M9 18l6-6-6-6",
|
|
873
|
+
close: "M18 6 6 18M6 6l12 12",
|
|
874
|
+
minimize: "M5 12h14",
|
|
875
|
+
expand: "M4 14v6h6M20 10V4h-6M14 10l6-6M10 14l-6 6",
|
|
876
|
+
arrow: "M12 20V5M6 11l6-6 6 6",
|
|
877
|
+
info: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18ZM12 11v5M12 8h.01"
|
|
878
|
+
};
|
|
879
|
+
var ToolbarButton = ({
|
|
880
|
+
label,
|
|
881
|
+
onClick,
|
|
882
|
+
disabled,
|
|
883
|
+
children
|
|
884
|
+
}) => /* @__PURE__ */ jsx(
|
|
885
|
+
"button",
|
|
886
|
+
{
|
|
887
|
+
"aria-label": label,
|
|
888
|
+
className: "inline-flex size-7 items-center justify-center rounded-md text-slate-600 hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:opacity-30 disabled:hover:bg-transparent",
|
|
889
|
+
disabled,
|
|
890
|
+
onClick,
|
|
891
|
+
title: label,
|
|
892
|
+
type: "button",
|
|
893
|
+
children
|
|
894
|
+
}
|
|
895
|
+
);
|
|
896
|
+
var SuggestionChip = ({
|
|
897
|
+
suggestion,
|
|
898
|
+
active,
|
|
899
|
+
onSelect
|
|
900
|
+
}) => {
|
|
901
|
+
const { messages } = useStreetImageryI18n();
|
|
902
|
+
const best = suggestion.candidates[0];
|
|
903
|
+
const label = viewSuggestionLabel(suggestion, messages);
|
|
904
|
+
return /* @__PURE__ */ jsxs(
|
|
905
|
+
"button",
|
|
906
|
+
{
|
|
907
|
+
"aria-label": best ? label : messages.viewer.noPhoto(label),
|
|
908
|
+
"aria-pressed": active,
|
|
909
|
+
className: [
|
|
910
|
+
"inline-flex shrink-0 items-center gap-1 rounded-full border px-2 py-0.5 text-xs whitespace-nowrap",
|
|
911
|
+
active ? "border-fuchsia-600 bg-fuchsia-600 text-white" : best ? "border-slate-300 bg-white text-slate-700 hover:border-fuchsia-500" : "border-dashed border-slate-200 bg-white text-slate-400"
|
|
912
|
+
].join(" "),
|
|
913
|
+
disabled: !best,
|
|
914
|
+
onClick: onSelect,
|
|
915
|
+
title: best ? label : messages.viewer.noMatchingPhoto(label),
|
|
916
|
+
type: "button",
|
|
917
|
+
children: [
|
|
918
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: suggestion.viewpoint.label ?? messages.viewpointRole[suggestion.viewpoint.role] }),
|
|
919
|
+
/* @__PURE__ */ jsx(
|
|
920
|
+
"span",
|
|
921
|
+
{
|
|
922
|
+
"aria-hidden": true,
|
|
923
|
+
className: "inline-flex",
|
|
924
|
+
style: { transform: `rotate(${suggestion.direction.bearing}deg)` },
|
|
925
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "size-3", path: ICONS.arrow })
|
|
926
|
+
}
|
|
927
|
+
),
|
|
928
|
+
best ? /* @__PURE__ */ jsx(
|
|
929
|
+
PhotoDate,
|
|
930
|
+
{
|
|
931
|
+
className: active ? "text-fuchsia-100" : "text-slate-400",
|
|
932
|
+
timestamp: best.photo.capturedAt
|
|
933
|
+
}
|
|
934
|
+
) : /* @__PURE__ */ jsx("span", { className: "text-slate-400", children: "\u2014" })
|
|
935
|
+
]
|
|
936
|
+
}
|
|
937
|
+
);
|
|
938
|
+
};
|
|
939
|
+
var FloatingViewerInfoButton = ({
|
|
940
|
+
label,
|
|
941
|
+
onClick
|
|
942
|
+
}) => /* @__PURE__ */ jsx(ToolbarButton, { label, onClick, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.info }) });
|
|
943
|
+
var FloatingPhotoViewer = ({
|
|
944
|
+
title,
|
|
945
|
+
titleActions,
|
|
946
|
+
suggestions,
|
|
947
|
+
activeDirectionKey,
|
|
948
|
+
onSelectSuggestion,
|
|
949
|
+
canGoBack,
|
|
950
|
+
canGoForward,
|
|
951
|
+
onBack,
|
|
952
|
+
onForward,
|
|
953
|
+
onClose,
|
|
954
|
+
step,
|
|
955
|
+
toolbar,
|
|
956
|
+
status,
|
|
957
|
+
children,
|
|
958
|
+
footer,
|
|
959
|
+
drawer,
|
|
960
|
+
storageKey = "street-imagery:floating-viewer"
|
|
961
|
+
}) => {
|
|
962
|
+
const { messages } = useStreetImageryI18n();
|
|
963
|
+
const [layout, setLayout] = useState(() => readLayout(storageKey));
|
|
964
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
965
|
+
const dragRef = useRef(null);
|
|
966
|
+
const updateLayout = (next) => {
|
|
967
|
+
setLayout(next);
|
|
968
|
+
writeLayout(storageKey, next);
|
|
969
|
+
};
|
|
970
|
+
const onShortcut = useEffectEvent((event) => {
|
|
971
|
+
const target = event.target;
|
|
972
|
+
if (target instanceof Element && target.closest('input, textarea, select, [contenteditable="true"]')) {
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (event.key === "Escape") {
|
|
976
|
+
onClose();
|
|
977
|
+
} else if (event.key === "[" && canGoBack) {
|
|
978
|
+
onBack();
|
|
979
|
+
} else if (event.key === "]" && canGoForward) {
|
|
980
|
+
onForward();
|
|
981
|
+
} else if (event.key === "ArrowLeft" && event.altKey) {
|
|
982
|
+
step?.onPrevious?.();
|
|
983
|
+
} else if (event.key === "ArrowRight" && event.altKey) {
|
|
984
|
+
step?.onNext?.();
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
useEffect(function subscribeKeyboardShortcuts() {
|
|
988
|
+
window.addEventListener("keydown", onShortcut);
|
|
989
|
+
return () => window.removeEventListener("keydown", onShortcut);
|
|
990
|
+
}, []);
|
|
991
|
+
const startDrag = (event, mode) => {
|
|
992
|
+
if (event.button !== 0 || event.target.closest("button")) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
996
|
+
dragRef.current = { x: event.clientX, y: event.clientY, layout, mode };
|
|
997
|
+
};
|
|
998
|
+
const onDrag = (event) => {
|
|
999
|
+
const drag = dragRef.current;
|
|
1000
|
+
if (!drag) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const dx = event.clientX - drag.x;
|
|
1004
|
+
const dy = event.clientY - drag.y;
|
|
1005
|
+
setLayout(
|
|
1006
|
+
drag.mode === "move" ? {
|
|
1007
|
+
...drag.layout,
|
|
1008
|
+
right: Math.max(EDGE, drag.layout.right - dx),
|
|
1009
|
+
bottom: Math.max(EDGE, drag.layout.bottom - dy)
|
|
1010
|
+
} : { ...drag.layout, width: Math.max(MIN_WIDTH, drag.layout.width - dx) }
|
|
1011
|
+
);
|
|
1012
|
+
};
|
|
1013
|
+
const endDrag = () => {
|
|
1014
|
+
if (dragRef.current) {
|
|
1015
|
+
dragRef.current = null;
|
|
1016
|
+
writeLayout(storageKey, layout);
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const dragHandlers = { onPointerMove: onDrag, onPointerUp: endDrag, onPointerCancel: endDrag };
|
|
1020
|
+
if (layout.minimized) {
|
|
1021
|
+
return /* @__PURE__ */ jsxs(
|
|
1022
|
+
"button",
|
|
1023
|
+
{
|
|
1024
|
+
className: "absolute z-10 inline-flex items-center gap-2 rounded-full bg-white px-3 py-1.5 text-sm font-medium text-slate-800 shadow-lg ring-1 ring-slate-200 hover:bg-slate-50",
|
|
1025
|
+
onClick: () => updateLayout({ ...layout, minimized: false }),
|
|
1026
|
+
style: { right: layout.right, bottom: layout.bottom },
|
|
1027
|
+
type: "button",
|
|
1028
|
+
children: [
|
|
1029
|
+
/* @__PURE__ */ jsx(Icon, { path: ICONS.expand }),
|
|
1030
|
+
title
|
|
1031
|
+
]
|
|
1032
|
+
}
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
return /* @__PURE__ */ jsxs(
|
|
1036
|
+
"section",
|
|
1037
|
+
{
|
|
1038
|
+
"aria-label": messages.viewer.regionLabel,
|
|
1039
|
+
className: "absolute z-10 flex max-h-[calc(100%-1rem)] flex-col overflow-hidden rounded-xl bg-white shadow-xl ring-1 ring-slate-200",
|
|
1040
|
+
style: {
|
|
1041
|
+
right: layout.right,
|
|
1042
|
+
bottom: layout.bottom,
|
|
1043
|
+
width: `min(${layout.width}px, calc(100% - 1rem))`
|
|
1044
|
+
},
|
|
1045
|
+
children: [
|
|
1046
|
+
/* @__PURE__ */ jsx(
|
|
1047
|
+
"div",
|
|
1048
|
+
{
|
|
1049
|
+
"aria-hidden": true,
|
|
1050
|
+
className: "absolute inset-y-0 left-0 z-10 w-1.5 cursor-ew-resize hover:bg-fuchsia-500/20",
|
|
1051
|
+
onPointerDown: (event) => startDrag(event, "resize"),
|
|
1052
|
+
...dragHandlers
|
|
1053
|
+
}
|
|
1054
|
+
),
|
|
1055
|
+
/* @__PURE__ */ jsxs(
|
|
1056
|
+
"header",
|
|
1057
|
+
{
|
|
1058
|
+
className: "flex cursor-move touch-none items-center gap-1 border-b border-slate-100 px-2 py-1.5 select-none",
|
|
1059
|
+
onPointerDown: (event) => startDrag(event, "move"),
|
|
1060
|
+
...dragHandlers,
|
|
1061
|
+
children: [
|
|
1062
|
+
step ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1063
|
+
/* @__PURE__ */ jsx(
|
|
1064
|
+
ToolbarButton,
|
|
1065
|
+
{
|
|
1066
|
+
disabled: !step.onPrevious,
|
|
1067
|
+
label: step.previousLabel,
|
|
1068
|
+
onClick: () => step.onPrevious?.(),
|
|
1069
|
+
children: /* @__PURE__ */ jsx(Icon, { path: ICONS.back })
|
|
1070
|
+
}
|
|
1071
|
+
),
|
|
1072
|
+
/* @__PURE__ */ jsx(
|
|
1073
|
+
ToolbarButton,
|
|
1074
|
+
{
|
|
1075
|
+
disabled: !step.onNext,
|
|
1076
|
+
label: step.nextLabel,
|
|
1077
|
+
onClick: () => step.onNext?.(),
|
|
1078
|
+
children: /* @__PURE__ */ jsx(Icon, { path: ICONS.forward })
|
|
1079
|
+
}
|
|
1080
|
+
)
|
|
1081
|
+
] }) : null,
|
|
1082
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-0.5", children: [
|
|
1083
|
+
/* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate px-1 text-sm font-semibold text-slate-800", children: title }),
|
|
1084
|
+
titleActions
|
|
1085
|
+
] }),
|
|
1086
|
+
/* @__PURE__ */ jsx(ToolbarButton, { disabled: !canGoBack, label: messages.viewer.backInHistory, onClick: onBack, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.undo }) }),
|
|
1087
|
+
/* @__PURE__ */ jsx(
|
|
1088
|
+
ToolbarButton,
|
|
1089
|
+
{
|
|
1090
|
+
disabled: !canGoForward,
|
|
1091
|
+
label: messages.viewer.forwardInHistory,
|
|
1092
|
+
onClick: onForward,
|
|
1093
|
+
children: /* @__PURE__ */ jsx(Icon, { path: ICONS.redo })
|
|
1094
|
+
}
|
|
1095
|
+
),
|
|
1096
|
+
/* @__PURE__ */ jsx(
|
|
1097
|
+
ToolbarButton,
|
|
1098
|
+
{
|
|
1099
|
+
label: messages.viewer.minimize,
|
|
1100
|
+
onClick: () => updateLayout({ ...layout, minimized: true }),
|
|
1101
|
+
children: /* @__PURE__ */ jsx(Icon, { path: ICONS.minimize })
|
|
1102
|
+
}
|
|
1103
|
+
),
|
|
1104
|
+
/* @__PURE__ */ jsx(ToolbarButton, { label: messages.viewer.close, onClick: onClose, children: /* @__PURE__ */ jsx(Icon, { path: ICONS.close }) })
|
|
1105
|
+
]
|
|
1106
|
+
}
|
|
1107
|
+
),
|
|
1108
|
+
toolbar ? /* @__PURE__ */ jsx("div", { className: "border-b border-slate-100 px-2 py-1.5", children: toolbar }) : suggestions.length > 0 ? /* @__PURE__ */ jsxs(
|
|
1109
|
+
"nav",
|
|
1110
|
+
{
|
|
1111
|
+
"aria-label": messages.viewer.suggestedViews,
|
|
1112
|
+
className: "flex gap-1 overflow-x-auto border-b border-slate-100 px-2 py-1.5",
|
|
1113
|
+
title: messages.viewer.suggestedViewsHint,
|
|
1114
|
+
children: [
|
|
1115
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 self-center pr-0.5 text-xs text-slate-500", children: messages.viewer.views }),
|
|
1116
|
+
suggestions.map((suggestion) => /* @__PURE__ */ jsx(
|
|
1117
|
+
SuggestionChip,
|
|
1118
|
+
{
|
|
1119
|
+
active: suggestion.direction.key === activeDirectionKey,
|
|
1120
|
+
onSelect: () => onSelectSuggestion(suggestion),
|
|
1121
|
+
suggestion
|
|
1122
|
+
},
|
|
1123
|
+
suggestion.direction.key
|
|
1124
|
+
))
|
|
1125
|
+
]
|
|
1126
|
+
}
|
|
1127
|
+
) : null,
|
|
1128
|
+
/* @__PURE__ */ jsxs("div", { className: "min-h-0 overflow-y-auto", children: [
|
|
1129
|
+
status ? /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-xs text-slate-500", role: "status", children: status }) : null,
|
|
1130
|
+
children,
|
|
1131
|
+
footer ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-slate-600", children: footer }) : null,
|
|
1132
|
+
drawer ? /* @__PURE__ */ jsxs("div", { className: "border-t border-slate-100", children: [
|
|
1133
|
+
/* @__PURE__ */ jsxs(
|
|
1134
|
+
"button",
|
|
1135
|
+
{
|
|
1136
|
+
"aria-expanded": drawerOpen,
|
|
1137
|
+
className: "flex w-full items-center justify-between px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50",
|
|
1138
|
+
onClick: () => setDrawerOpen((open) => !open),
|
|
1139
|
+
type: "button",
|
|
1140
|
+
children: [
|
|
1141
|
+
drawer.label,
|
|
1142
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: drawerOpen ? "inline-flex rotate-90" : "inline-flex", children: /* @__PURE__ */ jsx(Icon, { className: "size-3.5", path: ICONS.forward }) })
|
|
1143
|
+
]
|
|
1144
|
+
}
|
|
1145
|
+
),
|
|
1146
|
+
drawerOpen ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2", children: drawer.content }) : null
|
|
1147
|
+
] }) : null
|
|
1148
|
+
] })
|
|
1149
|
+
]
|
|
1150
|
+
}
|
|
1151
|
+
);
|
|
1152
|
+
};
|
|
1153
|
+
var SOURCE_DIRECTIONS = "viewpoint-directions";
|
|
1154
|
+
var SOURCE_POINTS = "viewpoint-points";
|
|
1155
|
+
var SOURCE_LINE = "viewpoint-line";
|
|
1156
|
+
var VIEWPOINT_DIRECTION_LAYER_ID = "viewpoint-direction-fill";
|
|
1157
|
+
var VIEWPOINT_DEFAULT_COLOR = "#c026d3";
|
|
1158
|
+
var directionLengthMeters = (zoom) => Math.max(8, coneRadiusMeters(zoom) * 3);
|
|
1159
|
+
var ViewpointLayer = ({
|
|
1160
|
+
viewpoints,
|
|
1161
|
+
suggestions,
|
|
1162
|
+
activeDirectionKey,
|
|
1163
|
+
zoom,
|
|
1164
|
+
line,
|
|
1165
|
+
color = VIEWPOINT_DEFAULT_COLOR
|
|
1166
|
+
}) => {
|
|
1167
|
+
const length = directionLengthMeters(zoom);
|
|
1168
|
+
const directions = {
|
|
1169
|
+
type: "FeatureCollection",
|
|
1170
|
+
features: suggestions.map(({ viewpoint, direction, candidates }) => ({
|
|
1171
|
+
...viewConeGeoJson(viewpoint.lngLat, direction.bearing, 40, length),
|
|
1172
|
+
properties: {
|
|
1173
|
+
key: direction.key,
|
|
1174
|
+
active: direction.key === activeDirectionKey,
|
|
1175
|
+
hasPhoto: candidates.length > 0
|
|
1176
|
+
}
|
|
1177
|
+
}))
|
|
1178
|
+
};
|
|
1179
|
+
const points = {
|
|
1180
|
+
type: "FeatureCollection",
|
|
1181
|
+
features: viewpoints.map(
|
|
1182
|
+
(viewpoint) => ({
|
|
1183
|
+
type: "Feature",
|
|
1184
|
+
properties: { id: viewpoint.id },
|
|
1185
|
+
geometry: { type: "Point", coordinates: viewpoint.lngLat }
|
|
1186
|
+
})
|
|
1187
|
+
)
|
|
1188
|
+
};
|
|
1189
|
+
const lineFeature = {
|
|
1190
|
+
type: "FeatureCollection",
|
|
1191
|
+
features: line && line.length > 1 ? [{ type: "Feature", properties: {}, geometry: { type: "LineString", coordinates: line } }] : []
|
|
1192
|
+
};
|
|
1193
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1194
|
+
/* @__PURE__ */ jsx(Source, { id: SOURCE_LINE, type: "geojson", data: lineFeature }),
|
|
1195
|
+
/* @__PURE__ */ jsx(
|
|
1196
|
+
Layer,
|
|
1197
|
+
{
|
|
1198
|
+
id: "viewpoint-line",
|
|
1199
|
+
type: "line",
|
|
1200
|
+
source: SOURCE_LINE,
|
|
1201
|
+
layout: { "line-cap": "round", "line-join": "round" },
|
|
1202
|
+
paint: { "line-color": color, "line-width": 5, "line-opacity": 0.5 }
|
|
1203
|
+
}
|
|
1204
|
+
),
|
|
1205
|
+
/* @__PURE__ */ jsx(Source, { id: SOURCE_DIRECTIONS, type: "geojson", data: directions }),
|
|
1206
|
+
/* @__PURE__ */ jsx(
|
|
1207
|
+
Layer,
|
|
1208
|
+
{
|
|
1209
|
+
id: VIEWPOINT_DIRECTION_LAYER_ID,
|
|
1210
|
+
type: "fill",
|
|
1211
|
+
source: SOURCE_DIRECTIONS,
|
|
1212
|
+
paint: {
|
|
1213
|
+
"fill-color": color,
|
|
1214
|
+
"fill-opacity": ["case", ["get", "active"], 0.55, ["get", "hasPhoto"], 0.18, 0.04]
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
),
|
|
1218
|
+
/* @__PURE__ */ jsx(
|
|
1219
|
+
Layer,
|
|
1220
|
+
{
|
|
1221
|
+
id: "viewpoint-direction-line",
|
|
1222
|
+
type: "line",
|
|
1223
|
+
source: SOURCE_DIRECTIONS,
|
|
1224
|
+
paint: {
|
|
1225
|
+
"line-color": color,
|
|
1226
|
+
"line-width": ["case", ["get", "active"], 2, 1],
|
|
1227
|
+
"line-dasharray": [2, 1.5],
|
|
1228
|
+
"line-opacity": ["case", ["get", "hasPhoto"], 0.9, 0.35]
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
),
|
|
1232
|
+
/* @__PURE__ */ jsx(Source, { id: SOURCE_POINTS, type: "geojson", data: points }),
|
|
1233
|
+
/* @__PURE__ */ jsx(
|
|
1234
|
+
Layer,
|
|
1235
|
+
{
|
|
1236
|
+
id: "viewpoint-point",
|
|
1237
|
+
type: "circle",
|
|
1238
|
+
source: SOURCE_POINTS,
|
|
1239
|
+
paint: {
|
|
1240
|
+
"circle-radius": 5,
|
|
1241
|
+
"circle-color": "#ffffff",
|
|
1242
|
+
"circle-stroke-color": color,
|
|
1243
|
+
"circle-stroke-width": 2.5
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
)
|
|
1247
|
+
] });
|
|
1248
|
+
};
|
|
1249
|
+
var viewDirectionKeyFromFeatures = (features) => {
|
|
1250
|
+
const hit = features?.find((feature) => feature.layer?.id === VIEWPOINT_DIRECTION_LAYER_ID);
|
|
1251
|
+
const key = hit?.properties?.key;
|
|
1252
|
+
return typeof key === "string" ? key : null;
|
|
1253
|
+
};
|
|
1254
|
+
var samePhoto = (a, b) => a != null && a.providerId === b.providerId && a.photoId === b.photoId;
|
|
1255
|
+
var useViewpointSessionStore = create()((set, get) => ({
|
|
1256
|
+
sessionId: 0,
|
|
1257
|
+
viewpoints: [],
|
|
1258
|
+
line: null,
|
|
1259
|
+
activeDirectionKey: null,
|
|
1260
|
+
history: [],
|
|
1261
|
+
historyIndex: -1,
|
|
1262
|
+
actions: {
|
|
1263
|
+
open: ({ viewpoints, line = null }) => set((state) => ({
|
|
1264
|
+
sessionId: state.sessionId + 1,
|
|
1265
|
+
viewpoints,
|
|
1266
|
+
line,
|
|
1267
|
+
activeDirectionKey: null
|
|
1268
|
+
})),
|
|
1269
|
+
showPhoto: (entry) => set((state) => {
|
|
1270
|
+
const current = state.history[state.historyIndex];
|
|
1271
|
+
if (samePhoto(current?.photo, entry.photo)) {
|
|
1272
|
+
return entry.directionKey != null && entry.directionKey !== state.activeDirectionKey ? { activeDirectionKey: entry.directionKey } : state;
|
|
1273
|
+
}
|
|
1274
|
+
const history = [...state.history.slice(0, state.historyIndex + 1), entry].slice(
|
|
1275
|
+
-100
|
|
1276
|
+
);
|
|
1277
|
+
return {
|
|
1278
|
+
history,
|
|
1279
|
+
historyIndex: history.length - 1,
|
|
1280
|
+
activeDirectionKey: entry.directionKey ?? state.activeDirectionKey
|
|
1281
|
+
};
|
|
1282
|
+
}),
|
|
1283
|
+
back: () => {
|
|
1284
|
+
const { history, historyIndex } = get();
|
|
1285
|
+
const entry = history[historyIndex - 1];
|
|
1286
|
+
if (!entry) {
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
set({ historyIndex: historyIndex - 1, activeDirectionKey: entry.directionKey });
|
|
1290
|
+
return entry;
|
|
1291
|
+
},
|
|
1292
|
+
forward: () => {
|
|
1293
|
+
const { history, historyIndex } = get();
|
|
1294
|
+
const entry = history[historyIndex + 1];
|
|
1295
|
+
if (!entry) {
|
|
1296
|
+
return null;
|
|
1297
|
+
}
|
|
1298
|
+
set({ historyIndex: historyIndex + 1, activeDirectionKey: entry.directionKey });
|
|
1299
|
+
return entry;
|
|
1300
|
+
},
|
|
1301
|
+
close: () => set({ viewpoints: [], line: null, activeDirectionKey: null }),
|
|
1302
|
+
reset: () => set({
|
|
1303
|
+
viewpoints: [],
|
|
1304
|
+
line: null,
|
|
1305
|
+
activeDirectionKey: null,
|
|
1306
|
+
history: [],
|
|
1307
|
+
historyIndex: -1
|
|
1308
|
+
})
|
|
1309
|
+
}
|
|
1310
|
+
}));
|
|
1311
|
+
var useViewpoints = () => useViewpointSessionStore((state) => state.viewpoints);
|
|
1312
|
+
var useViewpointLine = () => useViewpointSessionStore((state) => state.line);
|
|
1313
|
+
var useActiveDirectionKey = () => useViewpointSessionStore((state) => state.activeDirectionKey);
|
|
1314
|
+
var useCurrentHistoryEntry = () => useViewpointSessionStore((state) => state.history[state.historyIndex] ?? null);
|
|
1315
|
+
var useCanGoBack = () => useViewpointSessionStore((state) => state.historyIndex > 0);
|
|
1316
|
+
var useCanGoForward = () => useViewpointSessionStore((state) => state.historyIndex < state.history.length - 1);
|
|
1317
|
+
var getViewpointSession = () => {
|
|
1318
|
+
const { actions, history, historyIndex } = useViewpointSessionStore.getState();
|
|
1319
|
+
return { actions, current: history[historyIndex] ?? null };
|
|
1320
|
+
};
|
|
1321
|
+
var DEFAULT_SOURCES = [mapillaryPhotoSource];
|
|
1322
|
+
var useViewSuggestions = (viewpoints, {
|
|
1323
|
+
maxAgeYears,
|
|
1324
|
+
photoTypes,
|
|
1325
|
+
limit,
|
|
1326
|
+
filterPhoto,
|
|
1327
|
+
sources = DEFAULT_SOURCES,
|
|
1328
|
+
enabled = true
|
|
1329
|
+
} = {}) => {
|
|
1330
|
+
const [now] = useState(() => Date.now());
|
|
1331
|
+
const requests = viewpoints.flatMap(
|
|
1332
|
+
(viewpoint) => sources.map((source) => ({ viewpoint, source }))
|
|
1333
|
+
);
|
|
1334
|
+
const queries = useQueries({
|
|
1335
|
+
queries: requests.map(({ viewpoint, source }) => ({
|
|
1336
|
+
queryKey: [
|
|
1337
|
+
"street-imagery",
|
|
1338
|
+
"photos-near",
|
|
1339
|
+
source.id,
|
|
1340
|
+
viewpoint.lngLat[0],
|
|
1341
|
+
viewpoint.lngLat[1]
|
|
1342
|
+
],
|
|
1343
|
+
queryFn: ({ signal }) => source.fetchNear(viewpoint.lngLat, signal),
|
|
1344
|
+
enabled,
|
|
1345
|
+
staleTime: 10 * 60 * 1e3
|
|
1346
|
+
}))
|
|
1347
|
+
});
|
|
1348
|
+
const photosByViewpointId = /* @__PURE__ */ new Map();
|
|
1349
|
+
requests.forEach(({ viewpoint }, index) => {
|
|
1350
|
+
const photos = queries[index]?.data ?? [];
|
|
1351
|
+
const kept = filterPhoto ? photos.filter(filterPhoto) : photos;
|
|
1352
|
+
photosByViewpointId.set(viewpoint.id, [
|
|
1353
|
+
...photosByViewpointId.get(viewpoint.id) ?? [],
|
|
1354
|
+
...kept
|
|
1355
|
+
]);
|
|
1356
|
+
});
|
|
1357
|
+
const suggestions = buildViewSuggestions(
|
|
1358
|
+
viewpoints,
|
|
1359
|
+
(viewpoint) => photosByViewpointId.get(viewpoint.id) ?? [],
|
|
1360
|
+
{
|
|
1361
|
+
now,
|
|
1362
|
+
maxAgeMs: maxAgeYears == null ? void 0 : yearsToMs(maxAgeYears),
|
|
1363
|
+
photoTypes,
|
|
1364
|
+
limit
|
|
1365
|
+
}
|
|
1366
|
+
);
|
|
1367
|
+
return {
|
|
1368
|
+
suggestions,
|
|
1369
|
+
isLoading: enabled && queries.some((query) => query.isPending),
|
|
1370
|
+
// An error only when every source failed; one failing provider still leaves suggestions.
|
|
1371
|
+
isError: queries.length > 0 && queries.every((query) => query.isError)
|
|
1372
|
+
};
|
|
1373
|
+
};
|
|
1374
|
+
var STALE_TIME = 10 * 60 * 1e3;
|
|
1375
|
+
var useMapillaryMapFeatureImages = (featureId, { shape = SIGN_TARGET } = {}) => useQuery({
|
|
1376
|
+
queryKey: ["street-imagery", "mapillary-map-feature", featureId, shape],
|
|
1377
|
+
queryFn: ({ signal }) => fetchMapFeatureImages(featureId, { shape, signal }),
|
|
1378
|
+
enabled: !!featureId,
|
|
1379
|
+
staleTime: STALE_TIME
|
|
1380
|
+
});
|
|
1381
|
+
var useMapillaryMapFeaturesNear = (lngLat, {
|
|
1382
|
+
radiusMeters = 40,
|
|
1383
|
+
objectValues,
|
|
1384
|
+
enabled = true
|
|
1385
|
+
} = {}) => useQuery({
|
|
1386
|
+
queryKey: ["street-imagery", "mapillary-map-features-near", lngLat, radiusMeters, objectValues],
|
|
1387
|
+
queryFn: async ({ signal }) => {
|
|
1388
|
+
const center = lngLat;
|
|
1389
|
+
const features = await fetchMapillaryMapFeaturesInBbox(bboxAround(center, radiusMeters), {
|
|
1390
|
+
objectValues,
|
|
1391
|
+
signal
|
|
1392
|
+
});
|
|
1393
|
+
return features.map((feature) => ({ ...feature, distanceMeters: distanceMeters(center, feature.lngLat) })).filter((feature) => feature.distanceMeters <= radiusMeters).sort((a, b) => a.distanceMeters - b.distanceMeters);
|
|
1394
|
+
},
|
|
1395
|
+
enabled: enabled && !!lngLat,
|
|
1396
|
+
staleTime: STALE_TIME
|
|
1397
|
+
});
|
|
1398
|
+
var useMapillaryImageDetections = (imageId, { filter, filterKey } = {}) => useQuery({
|
|
1399
|
+
queryKey: ["street-imagery", "mapillary-detections", imageId, filterKey ?? null],
|
|
1400
|
+
queryFn: ({ signal }) => fetchImageDetections(imageId, { filter, signal }),
|
|
1401
|
+
enabled: !!imageId,
|
|
1402
|
+
staleTime: STALE_TIME
|
|
1403
|
+
});
|
|
1404
|
+
var useLocationPickStore = create()((set) => ({
|
|
1405
|
+
armedOpenerId: null,
|
|
1406
|
+
actions: {
|
|
1407
|
+
arm: (openerId) => set({ armedOpenerId: openerId }),
|
|
1408
|
+
disarm: () => set((state) => state.armedOpenerId == null ? state : { armedOpenerId: null }),
|
|
1409
|
+
toggle: (openerId) => set((state) => ({ armedOpenerId: state.armedOpenerId === openerId ? null : openerId }))
|
|
1410
|
+
}
|
|
1411
|
+
}));
|
|
1412
|
+
var useArmedLocationOpenerId = () => useLocationPickStore((state) => state.armedOpenerId);
|
|
1413
|
+
var useLocationPickActions = () => useLocationPickStore((state) => state.actions);
|
|
1414
|
+
|
|
1415
|
+
// src/openers/LocationPickOnMap.tsx
|
|
1416
|
+
var roundCoordinate = (value) => Number(value.toFixed(7));
|
|
1417
|
+
var LocationPickOnMap = ({ onPick }) => {
|
|
1418
|
+
const { current: map } = useMap();
|
|
1419
|
+
const armedOpenerId = useArmedLocationOpenerId();
|
|
1420
|
+
const { disarm } = useLocationPickActions();
|
|
1421
|
+
useEffect(
|
|
1422
|
+
function openArmedOpenerOnNextMapClick() {
|
|
1423
|
+
if (!map || !armedOpenerId) {
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
const opener = findLocationOpener(armedOpenerId);
|
|
1427
|
+
if (!opener) {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
const onClick = (event) => {
|
|
1431
|
+
const target = {
|
|
1432
|
+
lngLat: [roundCoordinate(event.lngLat.lng), roundCoordinate(event.lngLat.lat)],
|
|
1433
|
+
zoom: map.getZoom()
|
|
1434
|
+
};
|
|
1435
|
+
openLocationInNewTab(opener, target);
|
|
1436
|
+
disarm();
|
|
1437
|
+
onPick?.(target, opener);
|
|
1438
|
+
};
|
|
1439
|
+
const onKeyDown = (event) => {
|
|
1440
|
+
if (event.key === "Escape") {
|
|
1441
|
+
disarm();
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
map.on("click", onClick);
|
|
1445
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1446
|
+
return function stopListeningForPick() {
|
|
1447
|
+
map.off("click", onClick);
|
|
1448
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
1449
|
+
};
|
|
1450
|
+
},
|
|
1451
|
+
[armedOpenerId, disarm, map, onPick]
|
|
1452
|
+
);
|
|
1453
|
+
return null;
|
|
1454
|
+
};
|
|
1455
|
+
var MapillaryFeatureBar = ({ data, shownImage, onShow }) => {
|
|
1456
|
+
const { locale, messages } = useStreetImageryI18n();
|
|
1457
|
+
const labels = messages.feature;
|
|
1458
|
+
const { feature, days, images } = data;
|
|
1459
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1460
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", title: feature.value, children: [
|
|
1461
|
+
/* @__PURE__ */ jsx(
|
|
1462
|
+
"img",
|
|
1463
|
+
{
|
|
1464
|
+
alt: "",
|
|
1465
|
+
className: "size-7 shrink-0",
|
|
1466
|
+
onError: (event) => {
|
|
1467
|
+
event.currentTarget.style.display = "none";
|
|
1468
|
+
},
|
|
1469
|
+
src: mapillaryIconUrl(feature.value)
|
|
1470
|
+
},
|
|
1471
|
+
feature.value
|
|
1472
|
+
),
|
|
1473
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
1474
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm leading-tight font-medium text-slate-900", children: labels.name(feature.value) }),
|
|
1475
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-xs leading-tight text-slate-500", children: [
|
|
1476
|
+
labels.seen,
|
|
1477
|
+
" ",
|
|
1478
|
+
/* @__PURE__ */ jsx(PhotoDate, { timestamp: feature.firstSeenAt }),
|
|
1479
|
+
" \u2013",
|
|
1480
|
+
" ",
|
|
1481
|
+
/* @__PURE__ */ jsx(PhotoDate, { timestamp: feature.lastSeenAt })
|
|
1482
|
+
] })
|
|
1483
|
+
] })
|
|
1484
|
+
] }),
|
|
1485
|
+
days.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-500", children: labels.noPhotos }) : /* @__PURE__ */ jsx(
|
|
1486
|
+
"div",
|
|
1487
|
+
{
|
|
1488
|
+
"aria-label": labels.daysSummary(images.length, days.length),
|
|
1489
|
+
className: "flex gap-1 overflow-x-auto",
|
|
1490
|
+
role: "group",
|
|
1491
|
+
children: days.map((day) => {
|
|
1492
|
+
const noon = /* @__PURE__ */ new Date(`${day.day}T12:00:00Z`);
|
|
1493
|
+
const shownIndex = shownImage ? day.images.findIndex((image) => image.id === shownImage.id) : -1;
|
|
1494
|
+
const active = shownIndex >= 0;
|
|
1495
|
+
return /* @__PURE__ */ jsxs(
|
|
1496
|
+
"button",
|
|
1497
|
+
{
|
|
1498
|
+
"aria-pressed": active,
|
|
1499
|
+
className: `shrink-0 rounded-md border px-2 py-1 text-xs whitespace-nowrap ${active ? "border-amber-500 bg-amber-100 text-amber-950" : "border-slate-300 bg-white text-slate-700 hover:border-slate-400"}`,
|
|
1500
|
+
onClick: () => {
|
|
1501
|
+
const next = active ? day.images[(shownIndex + 1) % day.images.length] : day.best;
|
|
1502
|
+
if (next) {
|
|
1503
|
+
onShow(next);
|
|
1504
|
+
}
|
|
1505
|
+
},
|
|
1506
|
+
title: `${formatDateTooltip(noon, locale)}
|
|
1507
|
+
${labels.dayPhotos(day.images.length, active && day.images.length > 1)}`,
|
|
1508
|
+
type: "button",
|
|
1509
|
+
children: [
|
|
1510
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: formatMonth(noon, locale) }),
|
|
1511
|
+
" ",
|
|
1512
|
+
/* @__PURE__ */ jsxs("span", { className: active ? "text-amber-800" : "text-slate-500", children: [
|
|
1513
|
+
active ? `${shownIndex + 1}/` : "",
|
|
1514
|
+
day.images.length
|
|
1515
|
+
] })
|
|
1516
|
+
]
|
|
1517
|
+
},
|
|
1518
|
+
day.day
|
|
1519
|
+
);
|
|
1520
|
+
})
|
|
1521
|
+
}
|
|
1522
|
+
)
|
|
1523
|
+
] });
|
|
1524
|
+
};
|
|
658
1525
|
|
|
659
|
-
export { StreetLevelImagerySelectionOverlay, StreetLevelImagerySourcesAndLayers, StreetLevelImageryViewCone, StreetLevelImageryViewer, queryStreetImageryFeatures, resolveSelectedSequence, streetImageryInteractiveLayerIds,
|
|
1526
|
+
export { BASE_COLOR, FloatingPhotoViewer, FloatingViewerInfoButton, LocationPickOnMap, MapillaryFeatureBar, PhotoDate, SELECTION_COLOR, StreetLevelImagerySelectionOverlay, StreetLevelImagerySourcesAndLayers, StreetLevelImageryViewCone, StreetLevelImageryViewer, VIEWPOINT_DEFAULT_COLOR, VIEWPOINT_DIRECTION_LAYER_ID, VIEW_SHAPE_FILL_OPACITY, ViewpointLayer, getViewpointSession, queryStreetImageryFeatures, resolveSelectedSequence, streetImageryInteractiveLayerIds, useActiveDirectionKey, useAllProviderMapFeatures, useAllProviderPhotos, useArmedLocationOpenerId, useCanGoBack, useCanGoForward, useCurrentHistoryEntry, useLocationPickActions, useMapViewportBbox, useMapillaryImageDetections, useMapillaryMapFeatureImages, useMapillaryMapFeaturesNear, usePhotoFullUrl, usePhotoThumbnail, useProviderMapFeatures, useProviderPhotos, useProviderSequences, useViewSuggestions, useViewpointLine, useViewpoints, viewDirectionKeyFromFeatures, viewSuggestionLabel };
|