@osm-editor-kit/street-imagery-react 0.1.0-alpha.6 → 0.1.0-alpha.8
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 +40 -1
- package/dist/{chunk-HA4GJWHI.js → chunk-ZAIXGYAH.js} +48 -21
- package/dist/filters/PhotoDateRangeFilter.d.ts +41 -0
- package/dist/i18n/PhotoCount.d.ts +12 -0
- package/dist/i18n/StreetImageryLocaleProvider.d.ts +1 -1
- package/dist/i18n/messages.d.ts +21 -11
- package/dist/index.d.ts +4 -0
- package/dist/index.js +467 -53
- package/dist/mapillary/MapillaryFeatureBar.d.ts +4 -1
- package/dist/mapillary/SelectedMapFeatureLayer.d.ts +19 -0
- package/dist/mapillary/featureBarLabels.d.ts +16 -0
- package/dist/mapillary/useMapillaryFeatures.d.ts +18 -0
- package/dist/photo-details.js +1 -1
- package/dist/viewpoints/FloatingPhotoViewer.d.ts +9 -3
- package/dist/viewpoints/useViewpointSessionStore.d.ts +4 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -117,10 +117,49 @@ const { data: detections } = useMapillaryImageDetections(photo.photoId, {
|
|
|
117
117
|
<StreetLevelImageryViewer photo={photo} outlines={detections?.map((d) => ({ id: d.id, outline: d.outline }))} … />
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
- Map layers: `filter.mapFeatureValue` limits which signs/objects are drawn (`matchesAnyGroup(
|
|
120
|
+
- Map layers: `filter.mapFeatureValue` limits which signs/objects are drawn: `signGroupFilter(['parking', 'bike'])` for sign groups, or `matchesAnyGroup(…)`.
|
|
121
121
|
- Suggested views: `useViewSuggestions(viewpoints, { sources: [mapillaryPhotoSource, streetViewPhotoSource] })`.
|
|
122
122
|
- `turnMapillaryViewerTo` / `setMapillaryViewerOutlines` work on a mapillary-js `Viewer` you own.
|
|
123
123
|
|
|
124
|
+
## A selected sign: viewer, bar and map highlight
|
|
125
|
+
|
|
126
|
+
Keep the selected sign (`featureId`) and its shown photo in your own state or URL:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
const { data, shownImage, firstImage } = useSelectedMapillaryFeature({
|
|
130
|
+
featureId,
|
|
131
|
+
shownPhotoId, // the photo the viewer shows now
|
|
132
|
+
minCapturedAt: fromMs, // optional: open the best photo from this time on
|
|
133
|
+
})
|
|
134
|
+
// Open `firstImage` first: showPhoto(targetImageToPhoto(firstImage))
|
|
135
|
+
<MapillaryFeatureBar data={data} shownImage={shownImage} onShow={(image) => showPhoto(targetImageToPhoto(image))} />
|
|
136
|
+
<StreetLevelImageryViewer lookAt={{ lngLat: data.feature.lngLat, outline: shownImage?.outline, value: data.feature.value }} … />
|
|
137
|
+
// On the map, after StreetLevelImagerySourcesAndLayers: the sign's halo and a line from the camera
|
|
138
|
+
<SelectedMapFeatureLayer data={data} shownImage={shownImage} providers={providers} bbox={bbox} zoom={zoom} />
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A click on a sign dot: `queryStreetImageryFeatures(event).find((hit) => hit.kind === 'mapFeature')?.featureId`.
|
|
142
|
+
|
|
143
|
+
## Date filter with a slider
|
|
144
|
+
|
|
145
|
+
`PhotoDateRangeFilter` is the date filter of iD's photo panel as a controlled component: one slider with two handles on a scale that gives recent years more room. It works on plain `{ from?, to? }` days (`YYYY-MM-DD`), the same `DateRange` the layers take as `filter.date`.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
const { photos } = useAllProviderPhotos(providers, bbox, zoom) // no date: all photos in view
|
|
149
|
+
|
|
150
|
+
<PhotoDateRangeFilter
|
|
151
|
+
value={date}
|
|
152
|
+
onChange={setDate} // on release of a handle, not while dragging
|
|
153
|
+
capturedAt={photos.map((photo) => photo.capturedAt)} // marks: where photos are
|
|
154
|
+
yearLines={[1, 2, 3, 4]} // lines with a label; a click shows the last N years
|
|
155
|
+
recommendedMaxAgeYears={2} // this line is red
|
|
156
|
+
markers={[{ date: '2024-04-01', label: 'Survey start' }]} // dashed lines for fixed dates
|
|
157
|
+
dateInputs="collapsed" // exact days: 'collapsed' (default), 'open' or 'none'
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
A handle at its end means no limit, so the left end also shows photos older than the slider spans (`maxYears`, default 10). The scale itself is in the core package (`dateSliderPosition`, `dateSliderBins`, …) for a host that draws its own chart. The texts are in `messages.dateFilter`.
|
|
162
|
+
|
|
124
163
|
## Pick a place on the map to open elsewhere
|
|
125
164
|
|
|
126
165
|
A button arms a location opener (see `@osm-editor-kit/street-imagery`), the next map click opens the clicked place in that service and disarms; Escape cancels.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_STREET_IMAGERY_LOCALE
|
|
1
|
+
import { DEFAULT_STREET_IMAGERY_LOCALE } from '@osm-editor-kit/street-imagery';
|
|
2
2
|
import { createContext, use } from 'react';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -11,13 +11,28 @@ var en = {
|
|
|
11
11
|
forwardInHistory: "Forward in history (])",
|
|
12
12
|
minimize: "Minimize",
|
|
13
13
|
close: "Close (Esc)",
|
|
14
|
-
views: "Views",
|
|
15
14
|
suggestedViews: "Suggested views",
|
|
16
15
|
suggestedViewsHint: "Suggested views: where the photo is taken from, and which way it looks",
|
|
17
16
|
noPhoto: (label) => `${label} (no photo)`,
|
|
18
|
-
noMatchingPhoto: (label) => `${label}: no matching photo
|
|
17
|
+
noMatchingPhoto: (label) => `${label}: no matching photo`,
|
|
18
|
+
nextPhotoOfView: (label) => `${label} \u2014 click for the next photo`
|
|
19
19
|
},
|
|
20
20
|
unknownDate: "Unknown date",
|
|
21
|
+
dateFilter: {
|
|
22
|
+
allDates: "All dates",
|
|
23
|
+
since: (date) => `Since ${date}`,
|
|
24
|
+
until: (date) => `Until ${date}`,
|
|
25
|
+
between: (from, to) => `${from} \u2013 ${to}`,
|
|
26
|
+
yearsShort: (years) => `${years} y`,
|
|
27
|
+
lastYears: (years) => years === 1 ? "Show the last year" : `Show the last ${years} years`,
|
|
28
|
+
recommendedMaxAge: (years) => `Recommended: photos of the last ${years === 1 ? "year" : `${years} years`}`,
|
|
29
|
+
fromHandle: "Oldest photos to show",
|
|
30
|
+
toHandle: "Newest photos to show",
|
|
31
|
+
exactDates: "Exact dates",
|
|
32
|
+
from: "From",
|
|
33
|
+
to: "To",
|
|
34
|
+
photoCount: (count) => count === 1 ? "1 photo" : `${count} photos`
|
|
35
|
+
},
|
|
21
36
|
viewpointRole: {
|
|
22
37
|
here: "Here",
|
|
23
38
|
"line-start": "Start",
|
|
@@ -25,24 +40,22 @@ var en = {
|
|
|
25
40
|
"into-node": "Junction",
|
|
26
41
|
custom: "View"
|
|
27
42
|
},
|
|
43
|
+
compass: ["N", "NE", "E", "SE", "S", "SW", "W", "NW"],
|
|
28
44
|
viewDirection: (role, kind) => {
|
|
29
45
|
switch (kind) {
|
|
30
46
|
case "forward":
|
|
31
47
|
return `${role}, looking along the street`;
|
|
32
48
|
case "back":
|
|
33
49
|
return `${role}, looking back along the street`;
|
|
50
|
+
case "right":
|
|
51
|
+
return `${role}, looking right across the street`;
|
|
52
|
+
case "left":
|
|
53
|
+
return `${role}, looking left across the street`;
|
|
34
54
|
case "into":
|
|
35
55
|
return `${role}, looking into the junction`;
|
|
36
56
|
default:
|
|
37
57
|
return `${role}, looking ${kind}`;
|
|
38
58
|
}
|
|
39
|
-
},
|
|
40
|
-
feature: {
|
|
41
|
-
name: (value) => mapillaryValueName(value, "en"),
|
|
42
|
-
seen: "Seen",
|
|
43
|
-
noPhotos: "Mapillary lists no photos for this feature.",
|
|
44
|
-
daysSummary: (photos, days) => `${photos} photos on ${days} days`,
|
|
45
|
-
dayPhotos: (photos, canStep) => `${photos} photo${photos === 1 ? "" : "s"}${canStep ? " \u2014 click for the next one" : ""}`
|
|
46
59
|
}
|
|
47
60
|
};
|
|
48
61
|
var COMPASS_DE = { N: "Norden", E: "Osten", S: "S\xFCden", W: "Westen" };
|
|
@@ -54,13 +67,28 @@ var de = {
|
|
|
54
67
|
forwardInHistory: "Vor im Verlauf (])",
|
|
55
68
|
minimize: "Minimieren",
|
|
56
69
|
close: "Schlie\xDFen (Esc)",
|
|
57
|
-
views: "Blicke",
|
|
58
70
|
suggestedViews: "Vorgeschlagene Blicke",
|
|
59
71
|
suggestedViewsHint: "Vorgeschlagene Blicke: von wo das Foto aufgenommen ist und wohin es schaut",
|
|
60
72
|
noPhoto: (label) => `${label} (kein Foto)`,
|
|
61
|
-
noMatchingPhoto: (label) => `${label}: kein passendes Foto
|
|
73
|
+
noMatchingPhoto: (label) => `${label}: kein passendes Foto`,
|
|
74
|
+
nextPhotoOfView: (label) => `${label} \u2014 klicken f\xFCr das n\xE4chste Foto`
|
|
62
75
|
},
|
|
63
76
|
unknownDate: "Datum unbekannt",
|
|
77
|
+
dateFilter: {
|
|
78
|
+
allDates: "Alle Daten",
|
|
79
|
+
since: (date) => `Seit ${date}`,
|
|
80
|
+
until: (date) => `Bis ${date}`,
|
|
81
|
+
between: (from, to) => `${from} \u2013 ${to}`,
|
|
82
|
+
yearsShort: (years) => `${years} J.`,
|
|
83
|
+
lastYears: (years) => years === 1 ? "Das letzte Jahr zeigen" : `Die letzten ${years} Jahre zeigen`,
|
|
84
|
+
recommendedMaxAge: (years) => `Empfohlen: Fotos ${years === 1 ? "des letzten Jahres" : `der letzten ${years} Jahre`}`,
|
|
85
|
+
fromHandle: "\xC4lteste Fotos, die gezeigt werden",
|
|
86
|
+
toHandle: "Neueste Fotos, die gezeigt werden",
|
|
87
|
+
exactDates: "Genaue Daten",
|
|
88
|
+
from: "Von",
|
|
89
|
+
to: "Bis",
|
|
90
|
+
photoCount: (count) => count === 1 ? "1 Foto" : `${count} Fotos`
|
|
91
|
+
},
|
|
64
92
|
viewpointRole: {
|
|
65
93
|
here: "Hier",
|
|
66
94
|
"line-start": "Anfang",
|
|
@@ -68,24 +96,22 @@ var de = {
|
|
|
68
96
|
"into-node": "Knoten",
|
|
69
97
|
custom: "Blick"
|
|
70
98
|
},
|
|
99
|
+
compass: ["N", "NO", "O", "SO", "S", "SW", "W", "NW"],
|
|
71
100
|
viewDirection: (role, kind) => {
|
|
72
101
|
switch (kind) {
|
|
73
102
|
case "forward":
|
|
74
103
|
return `${role}, Blick entlang der Stra\xDFe`;
|
|
75
104
|
case "back":
|
|
76
105
|
return `${role}, Blick zur\xFCck entlang der Stra\xDFe`;
|
|
106
|
+
case "right":
|
|
107
|
+
return `${role}, Blick nach rechts quer zur Stra\xDFe`;
|
|
108
|
+
case "left":
|
|
109
|
+
return `${role}, Blick nach links quer zur Stra\xDFe`;
|
|
77
110
|
case "into":
|
|
78
111
|
return `${role}, Blick in den Knoten`;
|
|
79
112
|
default:
|
|
80
113
|
return `${role}, Blick nach ${COMPASS_DE[kind] ?? kind}`;
|
|
81
114
|
}
|
|
82
|
-
},
|
|
83
|
-
feature: {
|
|
84
|
-
name: (value) => mapillaryValueName(value, "de"),
|
|
85
|
-
seen: "Gesehen",
|
|
86
|
-
noPhotos: "Mapillary f\xFChrt keine Fotos zu diesem Objekt.",
|
|
87
|
-
daysSummary: (photos, days) => `${photos} Fotos an ${days} Tagen`,
|
|
88
|
-
dayPhotos: (photos, canStep) => `${photos} Foto${photos === 1 ? "" : "s"}${canStep ? " \u2014 klicken f\xFCr das n\xE4chste" : ""}`
|
|
89
115
|
}
|
|
90
116
|
};
|
|
91
117
|
var STREET_IMAGERY_MESSAGES = {
|
|
@@ -99,10 +125,11 @@ var DEFAULT_I18N = {
|
|
|
99
125
|
var I18nContext = createContext(DEFAULT_I18N);
|
|
100
126
|
var mergeMessages = (base, overrides) => overrides ? {
|
|
101
127
|
unknownDate: overrides.unknownDate ?? base.unknownDate,
|
|
128
|
+
dateFilter: { ...base.dateFilter, ...overrides.dateFilter },
|
|
102
129
|
viewer: { ...base.viewer, ...overrides.viewer },
|
|
103
130
|
viewpointRole: { ...base.viewpointRole, ...overrides.viewpointRole },
|
|
104
|
-
|
|
105
|
-
|
|
131
|
+
compass: overrides.compass?.length === 8 ? overrides.compass : base.compass,
|
|
132
|
+
viewDirection: overrides.viewDirection ?? base.viewDirection
|
|
106
133
|
} : base;
|
|
107
134
|
var StreetImageryLocaleProvider = ({
|
|
108
135
|
locale,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type DateRange } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
/** A fixed date to show in the chart, e.g. the start of a project or of a survey. */
|
|
3
|
+
export type PhotoDateMarker = {
|
|
4
|
+
/** A day as `YYYY-MM-DD`, or a time (ms). */
|
|
5
|
+
date: string | number;
|
|
6
|
+
label: string;
|
|
7
|
+
/** CSS color of line and label. Default red. */
|
|
8
|
+
color?: string;
|
|
9
|
+
};
|
|
10
|
+
export type PhotoDateRangeFilterProps = {
|
|
11
|
+
/** The chosen range, days as `YYYY-MM-DD`. No `from` and no `to`: all dates. */
|
|
12
|
+
value: DateRange;
|
|
13
|
+
/** Called when a handle is released, a year label is clicked or a date input changes. */
|
|
14
|
+
onChange: (range: DateRange) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Capture times (ms) of the photos to show as marks above the slider, usually all photos in
|
|
17
|
+
* view before the date filter. Without it there are no marks.
|
|
18
|
+
*/
|
|
19
|
+
capturedAt?: Iterable<number | null | undefined>;
|
|
20
|
+
/** Lines with a label at these ages in years; a click on a label shows that many years. */
|
|
21
|
+
yearLines?: readonly number[];
|
|
22
|
+
/** Its line is red; added to `yearLines` when it is not one of them. */
|
|
23
|
+
recommendedMaxAgeYears?: number;
|
|
24
|
+
markers?: readonly PhotoDateMarker[];
|
|
25
|
+
/** Years the slider spans. Default 10. */
|
|
26
|
+
maxYears?: number;
|
|
27
|
+
/** Reference time (ms). Default: the time of the first render. */
|
|
28
|
+
now?: number;
|
|
29
|
+
/**
|
|
30
|
+
* The two date inputs for exact days, in sync with the slider: folded away (default), open,
|
|
31
|
+
* or left out.
|
|
32
|
+
*/
|
|
33
|
+
dateInputs?: 'collapsed' | 'open' | 'none';
|
|
34
|
+
className?: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Date filter for photos after iD's photo age slider: one slider with two handles on a scale that
|
|
38
|
+
* gives recent years more room, marks that show how many photos there are at each time, lines at
|
|
39
|
+
* whole years, and optional fixed markers. Controlled: `value` in, `onChange` out.
|
|
40
|
+
*/
|
|
41
|
+
export declare const PhotoDateRangeFilter: ({ value, onChange, capturedAt, yearLines, recommendedMaxAgeYears, markers, maxYears, now: nowProp, dateInputs, className, }: PhotoDateRangeFilterProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type PhotoCountProps = {
|
|
2
|
+
/** Position of the shown photo, starting at 1; leave out to show only the total. */
|
|
3
|
+
index?: number;
|
|
4
|
+
total: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* "1/5" behind a small picture icon: this button has several photos and a click on it shows the
|
|
9
|
+
* next one.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PhotoCount: ({ index, total, className }: PhotoCountProps) => import("react").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -7,7 +7,7 @@ type StreetImageryI18n = {
|
|
|
7
7
|
};
|
|
8
8
|
/** Overrides per group, e.g. `{ feature: { name: germanSignName } }`. */
|
|
9
9
|
export type StreetImageryMessageOverrides = {
|
|
10
|
-
[Group in keyof StreetImageryMessages]?: StreetImageryMessages[Group] extends object ? StreetImageryMessages[Group] extends (...args: never[]) => unknown ? StreetImageryMessages[Group] : Partial<StreetImageryMessages[Group]> : StreetImageryMessages[Group];
|
|
10
|
+
[Group in keyof StreetImageryMessages]?: StreetImageryMessages[Group] extends object ? StreetImageryMessages[Group] extends ((...args: never[]) => unknown) | readonly unknown[] ? StreetImageryMessages[Group] : Partial<StreetImageryMessages[Group]> : StreetImageryMessages[Group];
|
|
11
11
|
};
|
|
12
12
|
type StreetImageryLocaleProviderProps = {
|
|
13
13
|
/** Language of the package's texts and date formats. Without a provider: English. */
|
package/dist/i18n/messages.d.ts
CHANGED
|
@@ -11,25 +11,35 @@ export type StreetImageryMessages = {
|
|
|
11
11
|
forwardInHistory: string;
|
|
12
12
|
minimize: string;
|
|
13
13
|
close: string;
|
|
14
|
-
views: string;
|
|
15
14
|
suggestedViews: string;
|
|
16
15
|
suggestedViewsHint: string;
|
|
17
16
|
noPhoto: (label: string) => string;
|
|
18
17
|
noMatchingPhoto: (label: string) => string;
|
|
18
|
+
nextPhotoOfView: (label: string) => string;
|
|
19
19
|
};
|
|
20
20
|
viewpointRole: Record<ViewpointRole, string>;
|
|
21
|
+
/** Eight compass points, clockwise from north: N, NE, E, SE, S, SW, W, NW. */
|
|
22
|
+
compass: readonly string[];
|
|
21
23
|
unknownDate: string;
|
|
24
|
+
dateFilter: {
|
|
25
|
+
allDates: string;
|
|
26
|
+
/** e.g. "Since 5 Oct 2024". */
|
|
27
|
+
since: (date: string) => string;
|
|
28
|
+
until: (date: string) => string;
|
|
29
|
+
between: (from: string, to: string) => string;
|
|
30
|
+
/** Short label below a year line, e.g. "2 y". */
|
|
31
|
+
yearsShort: (years: number) => string;
|
|
32
|
+
/** Tooltip of a year label; a click shows the photos of the last `years` years. */
|
|
33
|
+
lastYears: (years: number) => string;
|
|
34
|
+
recommendedMaxAge: (years: number) => string;
|
|
35
|
+
fromHandle: string;
|
|
36
|
+
toHandle: string;
|
|
37
|
+
exactDates: string;
|
|
38
|
+
from: string;
|
|
39
|
+
to: string;
|
|
40
|
+
photoCount: (count: number) => string;
|
|
41
|
+
};
|
|
22
42
|
/** e.g. "Start, looking along the street"; `role` is the viewpoint's label. */
|
|
23
43
|
viewDirection: (role: string, kind: ViewDirection['kind']) => string;
|
|
24
|
-
feature: {
|
|
25
|
-
/** Name of a Mapillary value like `regulatory--turn-right-ahead--g1`. */
|
|
26
|
-
name: (value: string) => string;
|
|
27
|
-
/** Line below the name; `first` and `last` are the rendered dates. */
|
|
28
|
-
seen: string;
|
|
29
|
-
noPhotos: string;
|
|
30
|
-
daysSummary: (photos: number, days: number) => string;
|
|
31
|
-
/** Third tooltip line of a day button, below date and age. */
|
|
32
|
-
dayPhotos: (photos: number, canStep: boolean) => string;
|
|
33
|
-
};
|
|
34
44
|
};
|
|
35
45
|
export declare const STREET_IMAGERY_MESSAGES: Record<StreetImageryLocale, StreetImageryMessages>;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,11 @@ export { mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo, ty
|
|
|
21
21
|
export * from './mapillary/useMapillaryFeatures';
|
|
22
22
|
export { LocationPickOnMap, type LocationPickOnMapProps } from './openers/LocationPickOnMap';
|
|
23
23
|
export * from './openers/useLocationPickStore';
|
|
24
|
+
export { SelectedMapFeatureLayer, type SelectedMapFeatureLayerProps, } from './mapillary/SelectedMapFeatureLayer';
|
|
24
25
|
export { MapillaryFeatureBar, type MapillaryFeatureBarProps } from './mapillary/MapillaryFeatureBar';
|
|
26
|
+
export { MAPILLARY_FEATURE_BAR_LABELS, type MapillaryFeatureBarLabels, } from './mapillary/featureBarLabels';
|
|
25
27
|
export { STREET_IMAGERY_MESSAGES, type StreetImageryMessages } from './i18n/messages';
|
|
26
28
|
export { StreetImageryLocaleProvider, useStreetImageryI18n, type StreetImageryMessageOverrides, } from './i18n/StreetImageryLocaleProvider';
|
|
29
|
+
export { PhotoDateRangeFilter, type PhotoDateMarker, type PhotoDateRangeFilterProps, } from './filters/PhotoDateRangeFilter';
|
|
27
30
|
export { PhotoDate } from './i18n/PhotoDate';
|
|
31
|
+
export { PhotoCount } from './i18n/PhotoCount';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useStreetImageryI18n } from './chunk-
|
|
2
|
-
export { STREET_IMAGERY_MESSAGES, StreetImageryLocaleProvider, useStreetImageryI18n } from './chunk-
|
|
1
|
+
import { useStreetImageryI18n } from './chunk-ZAIXGYAH.js';
|
|
2
|
+
export { STREET_IMAGERY_MESSAGES, StreetImageryLocaleProvider, useStreetImageryI18n } from './chunk-ZAIXGYAH.js';
|
|
3
3
|
export { MapillaryPanel, mapillaryViewFor, setMapillaryViewerOutlines, turnMapillaryViewerTo } from './chunk-53RA4OSJ.js';
|
|
4
4
|
export { PanoramaxPanel } from './chunk-EQ2JKLGT.js';
|
|
5
5
|
export { useViewerActions, useViewerBearing, useViewerHfov, useViewerLngLat } from './chunk-25UGGXIE.js';
|
|
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';
|
|
6
|
+
import { distanceMeters, sequencesToFeatureCollection, emptyLineCollection, adapterById, providerById, providerCoversBbox, 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, bestTargetImage, findLocationOpener, MAP_FEATURE_COLOR, mapillaryValueName, mapillaryIconUrl, dateRangeToSliderPositions, sliderPositionsToDateRange, dateSliderBins, dateSliderPosition, yearsAgoMs, parseIsoDateStartMs, openLocationInNewTab, formatDate, isIsoDate, alignLineToPoints, coneRadiusMeters, mapFeaturesToFeatureCollection, emptyPointCollection, buildMapFeatureLayerFilter, featureSourceId, photosToFeatureCollection, photosToViewfieldsFeatureCollection, emptyPolygonCollection, buildPhotoLayerFilter, photoSourceId, viewfieldSourceId, sequenceSourceId, sequenceLayerId, viewfieldLineLayerId, VIEWFIELD_MIN_ZOOM } from '@osm-editor-kit/street-imagery';
|
|
7
7
|
import { Source, Layer, useMap } from 'react-map-gl/maplibre';
|
|
8
8
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
9
9
|
import { lazy, useEffect, Fragment as Fragment$1, Suspense, useState, useRef, useEffectEvent } from 'react';
|
|
@@ -139,7 +139,8 @@ var withinBbox = (bbox) => (item) => {
|
|
|
139
139
|
};
|
|
140
140
|
var useProviderPhotos = (providerId, bbox, zoom) => {
|
|
141
141
|
const adapter = adapterById[providerId];
|
|
142
|
-
const
|
|
142
|
+
const meta = providerById[providerId];
|
|
143
|
+
const enabled = meta.kind === "photo" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= meta.minZoom && adapter?.fetchPhotos != null;
|
|
143
144
|
return useQuery({
|
|
144
145
|
queryKey: ["provider-photos", providerId, bboxKey(bbox), zoom],
|
|
145
146
|
queryFn: async ({ signal }) => {
|
|
@@ -152,18 +153,20 @@ var useProviderPhotos = (providerId, bbox, zoom) => {
|
|
|
152
153
|
};
|
|
153
154
|
var useProviderSequences = (providerId, bbox, zoom) => {
|
|
154
155
|
const adapter = adapterById[providerId];
|
|
155
|
-
const
|
|
156
|
-
const
|
|
156
|
+
const meta = providerById[providerId];
|
|
157
|
+
const sequencesMinZoom = meta.sequencesMinZoom;
|
|
158
|
+
const enabled = meta.kind === "photo" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= sequencesMinZoom && adapter?.fetchSequences != null;
|
|
157
159
|
return useQuery({
|
|
158
160
|
queryKey: ["provider-sequences", providerId, bboxKey(bbox), zoom],
|
|
159
|
-
queryFn: ({ signal }) => adapter
|
|
161
|
+
queryFn: ({ signal }) => adapter?.fetchSequences?.(bbox, zoom, signal) ?? [],
|
|
160
162
|
enabled,
|
|
161
163
|
placeholderData: (previous) => previous
|
|
162
164
|
});
|
|
163
165
|
};
|
|
164
166
|
var useProviderMapFeatures = (providerId, bbox, zoom) => {
|
|
165
167
|
const adapter = adapterById[providerId];
|
|
166
|
-
const
|
|
168
|
+
const meta = providerById[providerId];
|
|
169
|
+
const enabled = meta.kind === "mapFeature" && bbox != null && providerCoversBbox(providerId, bbox) && zoom >= meta.minZoom && adapter?.fetchMapFeatures != null;
|
|
167
170
|
return useQuery({
|
|
168
171
|
queryKey: ["provider-map-features", providerId, bboxKey(bbox), zoom],
|
|
169
172
|
queryFn: async ({ signal }) => {
|
|
@@ -283,11 +286,12 @@ var PhotoProviderLayer = ({
|
|
|
283
286
|
shownPhotoId
|
|
284
287
|
}) => {
|
|
285
288
|
const adapter = adapterById[providerId];
|
|
289
|
+
const meta = providerById[providerId];
|
|
286
290
|
const { data: photos = [] } = useProviderPhotos(providerId, bbox, zoom);
|
|
287
291
|
const { data: sequences = [] } = useProviderSequences(providerId, bbox, zoom);
|
|
288
|
-
const visiblePhotos = zoom >=
|
|
292
|
+
const visiblePhotos = zoom >= meta.minZoom ? photos.filter((photo) => photoMatchesFilters(photo, filter?.photoTypes, filter?.date)) : [];
|
|
289
293
|
const drawBbox = bbox ? padBbox(bbox, 0.25) : null;
|
|
290
|
-
const photoCollection = zoom >=
|
|
294
|
+
const photoCollection = zoom >= meta.minZoom ? photosToFeatureCollection(
|
|
291
295
|
drawBbox ? visiblePhotos.filter((photo) => lngLatInBbox(photo.lngLat, drawBbox)) : visiblePhotos
|
|
292
296
|
) : emptyPointCollection();
|
|
293
297
|
const viewfieldsActive = showViewfields && zoom >= VIEWFIELD_MIN_ZOOM;
|
|
@@ -338,7 +342,7 @@ var PhotoProviderLayer = ({
|
|
|
338
342
|
sequenceCapturedAtById.set(photo.sequenceId, photo.capturedAt);
|
|
339
343
|
}
|
|
340
344
|
}
|
|
341
|
-
const filteredSequences = showSequences && zoom >=
|
|
345
|
+
const filteredSequences = showSequences && zoom >= meta.sequencesMinZoom ? sequences.map((sequence) => {
|
|
342
346
|
if (sequence.capturedAt != null) return sequence;
|
|
343
347
|
const fromPhotos = sequenceCapturedAtById.get(sequence.sequenceId);
|
|
344
348
|
return fromPhotos != null ? { ...sequence, capturedAt: fromPhotos } : sequence;
|
|
@@ -383,7 +387,29 @@ var PhotoProviderLayer = ({
|
|
|
383
387
|
});
|
|
384
388
|
const sequenceCollection = alignedSequences.length > 0 ? sequencesToFeatureCollection(alignedSequences) : emptyLineCollection();
|
|
385
389
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
386
|
-
|
|
390
|
+
meta.coverageTiles ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
391
|
+
/* @__PURE__ */ jsx(
|
|
392
|
+
Source,
|
|
393
|
+
{
|
|
394
|
+
id: `coverage-tiles-source-${providerId}`,
|
|
395
|
+
minzoom: meta.coverageTiles.minZoom,
|
|
396
|
+
tileSize: meta.coverageTiles.tileSize,
|
|
397
|
+
tiles: [meta.coverageTiles.url],
|
|
398
|
+
type: "raster"
|
|
399
|
+
}
|
|
400
|
+
),
|
|
401
|
+
/* @__PURE__ */ jsx(
|
|
402
|
+
Layer,
|
|
403
|
+
{
|
|
404
|
+
id: `coverage-tiles-${providerId}`,
|
|
405
|
+
minzoom: meta.coverageTiles.minZoom,
|
|
406
|
+
paint: { "raster-opacity": 0.7 },
|
|
407
|
+
source: `coverage-tiles-source-${providerId}`,
|
|
408
|
+
type: "raster"
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
] }) : null,
|
|
412
|
+
adapter?.fetchSequences && showSequences ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
387
413
|
/* @__PURE__ */ jsx(
|
|
388
414
|
Source,
|
|
389
415
|
{
|
|
@@ -484,10 +510,10 @@ var MapFeatureProviderLayer = ({
|
|
|
484
510
|
filter,
|
|
485
511
|
mapFeatureCircleColor
|
|
486
512
|
}) => {
|
|
487
|
-
const
|
|
513
|
+
const meta = providerById[providerId];
|
|
488
514
|
const { data: features = [] } = useProviderMapFeatures(providerId, bbox, zoom);
|
|
489
515
|
const valueFilter = filter?.mapFeatureValue;
|
|
490
|
-
const featureCollection = zoom >=
|
|
516
|
+
const featureCollection = zoom >= meta.minZoom ? mapFeaturesToFeatureCollection(
|
|
491
517
|
valueFilter ? features.filter((feature) => valueFilter(feature.value)) : features
|
|
492
518
|
) : emptyPointCollection();
|
|
493
519
|
const featureFilter = buildMapFeatureLayerFilter(filter?.date);
|
|
@@ -520,8 +546,7 @@ var MapFeatureProviderLayer = ({
|
|
|
520
546
|
] });
|
|
521
547
|
};
|
|
522
548
|
var ProviderLayer = (props) => {
|
|
523
|
-
|
|
524
|
-
return adapter.kind === "mapFeature" ? /* @__PURE__ */ jsx(MapFeatureProviderLayer, { ...props }) : /* @__PURE__ */ jsx(PhotoProviderLayer, { ...props });
|
|
549
|
+
return providerById[props.providerId].kind === "mapFeature" ? /* @__PURE__ */ jsx(MapFeatureProviderLayer, { ...props }) : /* @__PURE__ */ jsx(PhotoProviderLayer, { ...props });
|
|
525
550
|
};
|
|
526
551
|
var StreetLevelImagerySourcesAndLayers = ({
|
|
527
552
|
providers,
|
|
@@ -562,7 +587,7 @@ var StreetLevelImagerySourcesAndLayers = ({
|
|
|
562
587
|
const selectedSequence = resolveSelectedSequence(selectedPhoto, sequences, activeSequenceId);
|
|
563
588
|
const providerDrawsSequences = selectedProviderId != null && showSequences && providers.includes(selectedProviderId);
|
|
564
589
|
const firstPhotoProvider = providers.find(
|
|
565
|
-
(id) =>
|
|
590
|
+
(id) => providerById[id].kind === "photo" && adapterById[id]?.fetchPhotos
|
|
566
591
|
);
|
|
567
592
|
const sequenceBeforeId = firstPhotoProvider ? showViewfields ? viewfieldLayerId(firstPhotoProvider) : photoLayerId(firstPhotoProvider) : void 0;
|
|
568
593
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -814,6 +839,24 @@ var usePhotoThumbnail = (photo) => useQuery({
|
|
|
814
839
|
enabled: photo != null,
|
|
815
840
|
staleTime: 24 * 60 * 60 * 1e3
|
|
816
841
|
});
|
|
842
|
+
var PhotoCount = ({ index, total, className }) => /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-0.5 tabular-nums ${className ?? ""}`, children: [
|
|
843
|
+
/* @__PURE__ */ jsx(
|
|
844
|
+
"svg",
|
|
845
|
+
{
|
|
846
|
+
"aria-hidden": true,
|
|
847
|
+
className: "size-3 shrink-0",
|
|
848
|
+
fill: "none",
|
|
849
|
+
stroke: "currentColor",
|
|
850
|
+
strokeLinecap: "round",
|
|
851
|
+
strokeLinejoin: "round",
|
|
852
|
+
strokeWidth: 1.75,
|
|
853
|
+
viewBox: "0 0 24 24",
|
|
854
|
+
children: /* @__PURE__ */ jsx("path", { d: "M4 6h16v12H4ZM4 15l4.5-4.5 4 4L15 12l5 5M9 9.5h.01" })
|
|
855
|
+
}
|
|
856
|
+
),
|
|
857
|
+
index != null ? `${index}/` : "",
|
|
858
|
+
total
|
|
859
|
+
] });
|
|
817
860
|
var PhotoDate = ({ timestamp, className, localDateTime }) => {
|
|
818
861
|
const { locale, messages } = useStreetImageryI18n();
|
|
819
862
|
if (timestamp == null) {
|
|
@@ -896,40 +939,74 @@ var ToolbarButton = ({
|
|
|
896
939
|
var SuggestionChip = ({
|
|
897
940
|
suggestion,
|
|
898
941
|
active,
|
|
942
|
+
shownPhotoId,
|
|
943
|
+
showPlace,
|
|
899
944
|
onSelect
|
|
900
945
|
}) => {
|
|
901
946
|
const { messages } = useStreetImageryI18n();
|
|
902
|
-
const
|
|
903
|
-
const
|
|
947
|
+
const { candidates } = suggestion;
|
|
948
|
+
const best = candidates[0];
|
|
949
|
+
const shownIndex = active ? candidates.findIndex((candidate) => candidate.photo.photoId === shownPhotoId) : -1;
|
|
950
|
+
const shown = shownIndex >= 0 ? candidates[shownIndex] : best;
|
|
951
|
+
const canStep = active && candidates.length > 1;
|
|
952
|
+
const hasCount = candidates.length > 0;
|
|
953
|
+
const name = viewSuggestionLabel(suggestion, messages);
|
|
954
|
+
const label = canStep ? messages.viewer.nextPhotoOfView(name) : name;
|
|
904
955
|
return /* @__PURE__ */ jsxs(
|
|
905
956
|
"button",
|
|
906
957
|
{
|
|
907
958
|
"aria-label": best ? label : messages.viewer.noPhoto(label),
|
|
908
959
|
"aria-pressed": active,
|
|
909
960
|
className: [
|
|
910
|
-
"
|
|
961
|
+
"flex min-w-0 flex-col items-center rounded-md border px-1 py-0.5 text-xs leading-tight",
|
|
911
962
|
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
963
|
].join(" "),
|
|
913
964
|
disabled: !best,
|
|
914
|
-
onClick:
|
|
965
|
+
onClick: () => {
|
|
966
|
+
const next = canStep ? candidates[(shownIndex + 1) % candidates.length] : best;
|
|
967
|
+
if (next) {
|
|
968
|
+
onSelect(next);
|
|
969
|
+
}
|
|
970
|
+
},
|
|
915
971
|
title: best ? label : messages.viewer.noMatchingPhoto(label),
|
|
916
972
|
type: "button",
|
|
917
973
|
children: [
|
|
918
|
-
/* @__PURE__ */
|
|
919
|
-
/* @__PURE__ */ jsx(
|
|
974
|
+
/* @__PURE__ */ jsxs(
|
|
920
975
|
"span",
|
|
921
976
|
{
|
|
922
|
-
"
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
977
|
+
className: `flex items-center gap-1 font-medium ${hasCount ? "w-full justify-between px-1" : "max-w-full"}`,
|
|
978
|
+
children: [
|
|
979
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1", children: [
|
|
980
|
+
/* @__PURE__ */ jsx(
|
|
981
|
+
"span",
|
|
982
|
+
{
|
|
983
|
+
"aria-hidden": true,
|
|
984
|
+
className: "inline-flex shrink-0",
|
|
985
|
+
style: { transform: `rotate(${suggestion.direction.bearing}deg)` },
|
|
986
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "size-3", path: ICONS.arrow })
|
|
987
|
+
}
|
|
988
|
+
),
|
|
989
|
+
/* @__PURE__ */ jsxs("span", { className: "truncate", children: [
|
|
990
|
+
showPlace ? `${suggestion.viewpoint.label ?? messages.viewpointRole[suggestion.viewpoint.role]} ` : "",
|
|
991
|
+
messages.compass[Math.round(suggestion.direction.bearing / 45) % 8]
|
|
992
|
+
] })
|
|
993
|
+
] }),
|
|
994
|
+
hasCount ? /* @__PURE__ */ jsx(
|
|
995
|
+
PhotoCount,
|
|
996
|
+
{
|
|
997
|
+
className: `font-normal ${active ? "text-fuchsia-100" : "text-slate-500"}`,
|
|
998
|
+
index: shownIndex >= 0 ? shownIndex + 1 : void 0,
|
|
999
|
+
total: candidates.length
|
|
1000
|
+
}
|
|
1001
|
+
) : null
|
|
1002
|
+
]
|
|
926
1003
|
}
|
|
927
1004
|
),
|
|
928
|
-
|
|
1005
|
+
shown ? /* @__PURE__ */ jsx(
|
|
929
1006
|
PhotoDate,
|
|
930
1007
|
{
|
|
931
|
-
className: active ? "text-fuchsia-100" : "text-slate-400"
|
|
932
|
-
timestamp:
|
|
1008
|
+
className: `max-w-full truncate ${active ? "text-fuchsia-100" : "text-slate-400"}`,
|
|
1009
|
+
timestamp: shown.photo.capturedAt
|
|
933
1010
|
}
|
|
934
1011
|
) : /* @__PURE__ */ jsx("span", { className: "text-slate-400", children: "\u2014" })
|
|
935
1012
|
]
|
|
@@ -946,6 +1023,7 @@ var FloatingPhotoViewer = ({
|
|
|
946
1023
|
suggestions,
|
|
947
1024
|
activeDirectionKey,
|
|
948
1025
|
onSelectSuggestion,
|
|
1026
|
+
shownPhotoId,
|
|
949
1027
|
canGoBack,
|
|
950
1028
|
canGoForward,
|
|
951
1029
|
onBack,
|
|
@@ -960,6 +1038,7 @@ var FloatingPhotoViewer = ({
|
|
|
960
1038
|
storageKey = "street-imagery:floating-viewer"
|
|
961
1039
|
}) => {
|
|
962
1040
|
const { messages } = useStreetImageryI18n();
|
|
1041
|
+
const severalViewpoints = new Set(suggestions.map((suggestion) => suggestion.viewpoint.id)).size > 1;
|
|
963
1042
|
const [layout, setLayout] = useState(() => readLayout(storageKey));
|
|
964
1043
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
965
1044
|
const dragRef = useRef(null);
|
|
@@ -1105,24 +1184,23 @@ var FloatingPhotoViewer = ({
|
|
|
1105
1184
|
]
|
|
1106
1185
|
}
|
|
1107
1186
|
),
|
|
1108
|
-
toolbar ? /* @__PURE__ */ jsx("div", { className: "border-b border-slate-100 px-2 py-1.5", children: toolbar }) : suggestions.length > 0 ? /* @__PURE__ */
|
|
1187
|
+
toolbar ? /* @__PURE__ */ jsx("div", { className: "border-b border-slate-100 px-2 py-1.5", children: toolbar }) : suggestions.length > 0 ? /* @__PURE__ */ jsx(
|
|
1109
1188
|
"nav",
|
|
1110
1189
|
{
|
|
1111
1190
|
"aria-label": messages.viewer.suggestedViews,
|
|
1112
|
-
className: "
|
|
1191
|
+
className: "grid grid-cols-[repeat(auto-fit,minmax(5.25rem,1fr))] gap-1 border-b border-slate-100 px-2 py-1.5",
|
|
1113
1192
|
title: messages.viewer.suggestedViewsHint,
|
|
1114
|
-
children:
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
]
|
|
1193
|
+
children: suggestions.map((suggestion) => /* @__PURE__ */ jsx(
|
|
1194
|
+
SuggestionChip,
|
|
1195
|
+
{
|
|
1196
|
+
active: suggestion.direction.key === activeDirectionKey,
|
|
1197
|
+
onSelect: (candidate) => onSelectSuggestion(suggestion, candidate),
|
|
1198
|
+
shownPhotoId,
|
|
1199
|
+
showPlace: severalViewpoints,
|
|
1200
|
+
suggestion
|
|
1201
|
+
},
|
|
1202
|
+
suggestion.direction.key
|
|
1203
|
+
))
|
|
1126
1204
|
}
|
|
1127
1205
|
) : null,
|
|
1128
1206
|
/* @__PURE__ */ jsxs("div", { className: "min-h-0 overflow-y-auto", children: [
|
|
@@ -1269,7 +1347,12 @@ var useViewpointSessionStore = create()((set, get) => ({
|
|
|
1269
1347
|
showPhoto: (entry) => set((state) => {
|
|
1270
1348
|
const current = state.history[state.historyIndex];
|
|
1271
1349
|
if (samePhoto(current?.photo, entry.photo)) {
|
|
1272
|
-
|
|
1350
|
+
if (entry.directionKey == null || entry.directionKey === current?.directionKey) {
|
|
1351
|
+
return entry.directionKey != null && entry.directionKey !== state.activeDirectionKey ? { activeDirectionKey: entry.directionKey } : state;
|
|
1352
|
+
}
|
|
1353
|
+
const history2 = [...state.history];
|
|
1354
|
+
history2[state.historyIndex] = { ...entry };
|
|
1355
|
+
return { history: history2, activeDirectionKey: entry.directionKey };
|
|
1273
1356
|
}
|
|
1274
1357
|
const history = [...state.history.slice(0, state.historyIndex + 1), entry].slice(
|
|
1275
1358
|
-100
|
|
@@ -1401,6 +1484,20 @@ var useMapillaryImageDetections = (imageId, { filter, filterKey } = {}) => useQu
|
|
|
1401
1484
|
enabled: !!imageId,
|
|
1402
1485
|
staleTime: STALE_TIME
|
|
1403
1486
|
});
|
|
1487
|
+
var useSelectedMapillaryFeature = ({
|
|
1488
|
+
featureId,
|
|
1489
|
+
shownPhotoId,
|
|
1490
|
+
minCapturedAt,
|
|
1491
|
+
shape
|
|
1492
|
+
}) => {
|
|
1493
|
+
const { data, isLoading, isError } = useMapillaryMapFeatureImages(featureId, { shape });
|
|
1494
|
+
const shownImage = data?.images.find((image) => image.id === shownPhotoId) ?? null;
|
|
1495
|
+
const firstImage = data ? bestTargetImage(data.images, data.feature.lngLat, {
|
|
1496
|
+
minCapturedAt: minCapturedAt ?? void 0,
|
|
1497
|
+
shape
|
|
1498
|
+
}) ?? null : null;
|
|
1499
|
+
return { data: data ?? null, isLoading, isError, shownImage, firstImage };
|
|
1500
|
+
};
|
|
1404
1501
|
var useLocationPickStore = create()((set) => ({
|
|
1405
1502
|
armedOpenerId: null,
|
|
1406
1503
|
actions: {
|
|
@@ -1452,9 +1549,94 @@ var LocationPickOnMap = ({ onPick }) => {
|
|
|
1452
1549
|
);
|
|
1453
1550
|
return null;
|
|
1454
1551
|
};
|
|
1455
|
-
var
|
|
1456
|
-
|
|
1457
|
-
|
|
1552
|
+
var SelectedMapFeatureLayer = ({
|
|
1553
|
+
data,
|
|
1554
|
+
shownImage,
|
|
1555
|
+
providers,
|
|
1556
|
+
bbox,
|
|
1557
|
+
zoom,
|
|
1558
|
+
color = MAP_FEATURE_COLOR
|
|
1559
|
+
}) => {
|
|
1560
|
+
const drawnFeatures = useAllProviderMapFeatures(providers, bbox, zoom);
|
|
1561
|
+
const target = data ? drawnFeatures.find((feature) => feature.featureId === data.feature.id)?.lngLat ?? data.feature.lngLat : null;
|
|
1562
|
+
const camera = shownImage?.lngLat;
|
|
1563
|
+
const points = {
|
|
1564
|
+
type: "FeatureCollection",
|
|
1565
|
+
features: target ? [{ type: "Feature", properties: {}, geometry: { type: "Point", coordinates: target } }] : []
|
|
1566
|
+
};
|
|
1567
|
+
const line = {
|
|
1568
|
+
type: "FeatureCollection",
|
|
1569
|
+
features: target && camera ? [
|
|
1570
|
+
{
|
|
1571
|
+
type: "Feature",
|
|
1572
|
+
properties: {},
|
|
1573
|
+
geometry: { type: "LineString", coordinates: [camera, target] }
|
|
1574
|
+
}
|
|
1575
|
+
] : []
|
|
1576
|
+
};
|
|
1577
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1578
|
+
/* @__PURE__ */ jsx(Source, { data: line, id: "selected-map-feature-line", type: "geojson" }),
|
|
1579
|
+
/* @__PURE__ */ jsx(
|
|
1580
|
+
Layer,
|
|
1581
|
+
{
|
|
1582
|
+
id: "selected-map-feature-line",
|
|
1583
|
+
paint: { "line-color": SELECTION_COLOR, "line-width": 1.5, "line-dasharray": [2, 1.5] },
|
|
1584
|
+
source: "selected-map-feature-line",
|
|
1585
|
+
type: "line"
|
|
1586
|
+
}
|
|
1587
|
+
),
|
|
1588
|
+
/* @__PURE__ */ jsx(Source, { data: points, id: "selected-map-feature-point", type: "geojson" }),
|
|
1589
|
+
/* @__PURE__ */ jsx(
|
|
1590
|
+
Layer,
|
|
1591
|
+
{
|
|
1592
|
+
id: "selected-map-feature-disk",
|
|
1593
|
+
paint: {
|
|
1594
|
+
"circle-radius": 17,
|
|
1595
|
+
"circle-color": color,
|
|
1596
|
+
"circle-opacity": VIEW_SHAPE_FILL_OPACITY
|
|
1597
|
+
},
|
|
1598
|
+
source: "selected-map-feature-point",
|
|
1599
|
+
type: "circle"
|
|
1600
|
+
}
|
|
1601
|
+
),
|
|
1602
|
+
/* @__PURE__ */ jsx(
|
|
1603
|
+
Layer,
|
|
1604
|
+
{
|
|
1605
|
+
id: "selected-map-feature-dot",
|
|
1606
|
+
paint: {
|
|
1607
|
+
"circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 1.8, 14, 3.6, 18, 4.8],
|
|
1608
|
+
"circle-color": color
|
|
1609
|
+
},
|
|
1610
|
+
source: "selected-map-feature-point",
|
|
1611
|
+
type: "circle"
|
|
1612
|
+
}
|
|
1613
|
+
)
|
|
1614
|
+
] });
|
|
1615
|
+
};
|
|
1616
|
+
var MAPILLARY_FEATURE_BAR_LABELS = {
|
|
1617
|
+
en: {
|
|
1618
|
+
name: (value) => mapillaryValueName(value, "en"),
|
|
1619
|
+
seen: "Seen",
|
|
1620
|
+
noPhotos: "Mapillary lists no photos for this feature.",
|
|
1621
|
+
daysSummary: (photos, days) => `${photos} photos on ${days} days`,
|
|
1622
|
+
dayPhotos: (photos, canStep) => `${photos} photo${photos === 1 ? "" : "s"}${canStep ? " \u2014 click for the next one" : ""}`
|
|
1623
|
+
},
|
|
1624
|
+
de: {
|
|
1625
|
+
name: (value) => mapillaryValueName(value, "de"),
|
|
1626
|
+
seen: "Gesehen",
|
|
1627
|
+
noPhotos: "Mapillary f\xFChrt keine Fotos zu diesem Objekt.",
|
|
1628
|
+
daysSummary: (photos, days) => `${photos} Fotos an ${days} Tagen`,
|
|
1629
|
+
dayPhotos: (photos, canStep) => `${photos} Foto${photos === 1 ? "" : "s"}${canStep ? " \u2014 klicken f\xFCr das n\xE4chste" : ""}`
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
var MapillaryFeatureBar = ({
|
|
1633
|
+
data,
|
|
1634
|
+
shownImage,
|
|
1635
|
+
onShow,
|
|
1636
|
+
labels: labelOverrides
|
|
1637
|
+
}) => {
|
|
1638
|
+
const { locale } = useStreetImageryI18n();
|
|
1639
|
+
const labels = { ...MAPILLARY_FEATURE_BAR_LABELS[locale], ...labelOverrides };
|
|
1458
1640
|
const { feature, days, images } = data;
|
|
1459
1641
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1460
1642
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", title: feature.value, children: [
|
|
@@ -1509,10 +1691,14 @@ ${labels.dayPhotos(day.images.length, active && day.images.length > 1)}`,
|
|
|
1509
1691
|
children: [
|
|
1510
1692
|
/* @__PURE__ */ jsx("span", { className: "font-medium", children: formatMonth(noon, locale) }),
|
|
1511
1693
|
" ",
|
|
1512
|
-
/* @__PURE__ */
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1694
|
+
/* @__PURE__ */ jsx(
|
|
1695
|
+
PhotoCount,
|
|
1696
|
+
{
|
|
1697
|
+
className: `align-[-1px] ${active ? "text-amber-800" : "text-slate-500"}`,
|
|
1698
|
+
index: active ? shownIndex + 1 : void 0,
|
|
1699
|
+
total: day.images.length
|
|
1700
|
+
}
|
|
1701
|
+
)
|
|
1516
1702
|
]
|
|
1517
1703
|
},
|
|
1518
1704
|
day.day
|
|
@@ -1522,5 +1708,233 @@ ${labels.dayPhotos(day.images.length, active && day.images.length > 1)}`,
|
|
|
1522
1708
|
)
|
|
1523
1709
|
] });
|
|
1524
1710
|
};
|
|
1711
|
+
var DEFAULT_YEAR_LINES = [1, 2, 3, 4];
|
|
1712
|
+
var BINS = 60;
|
|
1713
|
+
var STEP = 1e-3;
|
|
1714
|
+
var CHART_HEIGHT_PX = 36;
|
|
1715
|
+
var RED = "#dc2626";
|
|
1716
|
+
var HANDLE_CLASS = "pointer-events-none absolute inset-0 m-0 h-full w-full cursor-pointer appearance-none bg-transparent focus:outline-none [&::-webkit-slider-runnable-track]:appearance-none [&::-webkit-slider-runnable-track]:bg-transparent [&::-moz-range-track]:bg-transparent [&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:cursor-grab [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white [&::-webkit-slider-thumb]:bg-slate-800 [&::-webkit-slider-thumb]:shadow [&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:box-border [&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:cursor-grab [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-white [&::-moz-range-thumb]:bg-slate-800 [&::-moz-range-thumb]:shadow focus-visible:[&::-webkit-slider-thumb]:ring-2 focus-visible:[&::-webkit-slider-thumb]:ring-sky-500 focus-visible:[&::-moz-range-thumb]:ring-2 focus-visible:[&::-moz-range-thumb]:ring-sky-500";
|
|
1717
|
+
var percent = (position) => `${position * 100}%`;
|
|
1718
|
+
var PhotoDateRangeFilter = ({
|
|
1719
|
+
value,
|
|
1720
|
+
onChange,
|
|
1721
|
+
capturedAt,
|
|
1722
|
+
yearLines = DEFAULT_YEAR_LINES,
|
|
1723
|
+
recommendedMaxAgeYears,
|
|
1724
|
+
markers = [],
|
|
1725
|
+
maxYears,
|
|
1726
|
+
now: nowProp,
|
|
1727
|
+
dateInputs = "collapsed",
|
|
1728
|
+
className
|
|
1729
|
+
}) => {
|
|
1730
|
+
const { locale, messages } = useStreetImageryI18n();
|
|
1731
|
+
const text = messages.dateFilter;
|
|
1732
|
+
const [startNow] = useState(() => Date.now());
|
|
1733
|
+
const now = nowProp ?? startNow;
|
|
1734
|
+
const scale = { now, maxYears };
|
|
1735
|
+
const [dragged, setDragged] = useState(null);
|
|
1736
|
+
const fromInput = useRef(null);
|
|
1737
|
+
const toInput = useRef(null);
|
|
1738
|
+
const positions = dragged ?? dateRangeToSliderPositions(value, scale);
|
|
1739
|
+
const shownRange = dragged ? sliderPositionsToDateRange(dragged, scale) : value;
|
|
1740
|
+
const readPositions = (moved) => {
|
|
1741
|
+
const from = Number(fromInput.current?.value ?? positions.from);
|
|
1742
|
+
const to = Number(toInput.current?.value ?? positions.to);
|
|
1743
|
+
return moved === "from" ? { from: Math.min(from, to), to } : { from, to: Math.max(from, to) };
|
|
1744
|
+
};
|
|
1745
|
+
const commitOnRelease = (moved) => (input) => {
|
|
1746
|
+
if (!input) {
|
|
1747
|
+
return void 0;
|
|
1748
|
+
}
|
|
1749
|
+
const commit = () => {
|
|
1750
|
+
setDragged(null);
|
|
1751
|
+
const next = sliderPositionsToDateRange(readPositions(moved), scale);
|
|
1752
|
+
const other = moved === "from" ? "to" : "from";
|
|
1753
|
+
onChange({ ...next, [other]: value[other] });
|
|
1754
|
+
};
|
|
1755
|
+
input.addEventListener("change", commit);
|
|
1756
|
+
return () => {
|
|
1757
|
+
input.removeEventListener("change", commit);
|
|
1758
|
+
};
|
|
1759
|
+
};
|
|
1760
|
+
const trackDrag = (moved, event) => {
|
|
1761
|
+
if (event.type === "input") {
|
|
1762
|
+
setDragged(readPositions(moved));
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
const bins = capturedAt ? dateSliderBins(capturedAt, scale, BINS) : [];
|
|
1766
|
+
const maxCount = Math.max(1, ...bins);
|
|
1767
|
+
const years = [
|
|
1768
|
+
...new Set(recommendedMaxAgeYears ? [...yearLines, recommendedMaxAgeYears] : yearLines)
|
|
1769
|
+
].map((count) => ({ count, position: dateSliderPosition(yearsAgoMs(count, now), scale) })).filter(({ position }) => position > 0 && position < 1);
|
|
1770
|
+
const shownMarkers = markers.map((marker) => {
|
|
1771
|
+
const time = typeof marker.date === "number" ? marker.date : parseIsoDateStartMs(marker.date);
|
|
1772
|
+
return { ...marker, position: dateSliderPosition(time, scale) };
|
|
1773
|
+
}).filter(({ position }) => position > 0 && position < 1);
|
|
1774
|
+
const day = (isoDate) => formatDate(parseIsoDateStartMs(isoDate), locale);
|
|
1775
|
+
const rangeLabel = shownRange.from && shownRange.to ? text.between(day(shownRange.from), day(shownRange.to)) : shownRange.from ? text.since(day(shownRange.from)) : shownRange.to ? text.until(day(shownRange.to)) : text.allDates;
|
|
1776
|
+
const setYears = (count) => {
|
|
1777
|
+
onChange({ from: new Date(yearsAgoMs(count, now)).toISOString().slice(0, 10) });
|
|
1778
|
+
};
|
|
1779
|
+
const setDay = (key, input) => {
|
|
1780
|
+
if (input === "") {
|
|
1781
|
+
onChange({ ...value, [key]: void 0 });
|
|
1782
|
+
} else if (isIsoDate(input)) {
|
|
1783
|
+
onChange({ ...value, [key]: input });
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
const isAllDates = !shownRange.from && !shownRange.to;
|
|
1787
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
1788
|
+
/* @__PURE__ */ jsx("div", { className: "mx-2", children: /* @__PURE__ */ jsxs(
|
|
1789
|
+
"div",
|
|
1790
|
+
{
|
|
1791
|
+
className: "relative",
|
|
1792
|
+
style: { height: CHART_HEIGHT_PX, marginTop: shownMarkers.length > 0 ? 14 : 0 },
|
|
1793
|
+
children: [
|
|
1794
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "absolute inset-0 flex items-end", children: bins.map((count, index) => {
|
|
1795
|
+
const start = index / BINS;
|
|
1796
|
+
const inRange = start + 1 / BINS > positions.from && start < positions.to;
|
|
1797
|
+
return /* @__PURE__ */ jsx(
|
|
1798
|
+
"div",
|
|
1799
|
+
{
|
|
1800
|
+
className: `flex-1 rounded-t-[1px] ${inRange ? "bg-slate-500" : "bg-slate-300"}`,
|
|
1801
|
+
style: {
|
|
1802
|
+
height: count === 0 ? 0 : Math.max(2, Math.sqrt(count / maxCount) * CHART_HEIGHT_PX)
|
|
1803
|
+
},
|
|
1804
|
+
title: count > 0 ? text.photoCount(count) : void 0
|
|
1805
|
+
},
|
|
1806
|
+
index
|
|
1807
|
+
);
|
|
1808
|
+
}) }),
|
|
1809
|
+
years.map(({ count, position }) => /* @__PURE__ */ jsx(
|
|
1810
|
+
"div",
|
|
1811
|
+
{
|
|
1812
|
+
"aria-hidden": true,
|
|
1813
|
+
className: "pointer-events-none absolute inset-y-0 w-px -translate-x-1/2",
|
|
1814
|
+
style: {
|
|
1815
|
+
left: percent(position),
|
|
1816
|
+
backgroundColor: count === recommendedMaxAgeYears ? RED : "rgb(148 163 184 / 0.7)"
|
|
1817
|
+
}
|
|
1818
|
+
},
|
|
1819
|
+
count
|
|
1820
|
+
)),
|
|
1821
|
+
shownMarkers.map((marker) => /* @__PURE__ */ jsx(
|
|
1822
|
+
"div",
|
|
1823
|
+
{
|
|
1824
|
+
className: "pointer-events-none absolute inset-y-0 w-0 border-l-2 border-dashed",
|
|
1825
|
+
style: { left: percent(marker.position), borderColor: marker.color ?? RED },
|
|
1826
|
+
children: /* @__PURE__ */ jsx(
|
|
1827
|
+
"span",
|
|
1828
|
+
{
|
|
1829
|
+
className: "absolute -top-3.5 -translate-x-1/2 text-[10px] leading-none whitespace-nowrap",
|
|
1830
|
+
style: { color: marker.color ?? RED },
|
|
1831
|
+
children: marker.label
|
|
1832
|
+
}
|
|
1833
|
+
)
|
|
1834
|
+
},
|
|
1835
|
+
`${marker.date}-${marker.label}`
|
|
1836
|
+
))
|
|
1837
|
+
]
|
|
1838
|
+
}
|
|
1839
|
+
) }),
|
|
1840
|
+
/* @__PURE__ */ jsxs("div", { className: "relative h-4", children: [
|
|
1841
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-x-2 top-1/2 h-1 -translate-y-1/2 rounded-full bg-slate-200", children: /* @__PURE__ */ jsx(
|
|
1842
|
+
"div",
|
|
1843
|
+
{
|
|
1844
|
+
className: "absolute inset-y-0 rounded-full bg-slate-700",
|
|
1845
|
+
style: {
|
|
1846
|
+
left: percent(positions.from),
|
|
1847
|
+
right: percent(1 - positions.to)
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
) }),
|
|
1851
|
+
/* @__PURE__ */ jsx(
|
|
1852
|
+
"input",
|
|
1853
|
+
{
|
|
1854
|
+
"aria-label": text.fromHandle,
|
|
1855
|
+
"aria-valuetext": shownRange.from ? day(shownRange.from) : text.allDates,
|
|
1856
|
+
className: HANDLE_CLASS,
|
|
1857
|
+
max: 1,
|
|
1858
|
+
min: 0,
|
|
1859
|
+
onChange: (event) => {
|
|
1860
|
+
trackDrag("from", event.nativeEvent);
|
|
1861
|
+
},
|
|
1862
|
+
ref: (input) => {
|
|
1863
|
+
fromInput.current = input;
|
|
1864
|
+
return commitOnRelease("from")(input);
|
|
1865
|
+
},
|
|
1866
|
+
step: STEP,
|
|
1867
|
+
type: "range",
|
|
1868
|
+
value: positions.from
|
|
1869
|
+
}
|
|
1870
|
+
),
|
|
1871
|
+
/* @__PURE__ */ jsx(
|
|
1872
|
+
"input",
|
|
1873
|
+
{
|
|
1874
|
+
"aria-label": text.toHandle,
|
|
1875
|
+
"aria-valuetext": shownRange.to ? day(shownRange.to) : text.allDates,
|
|
1876
|
+
className: HANDLE_CLASS,
|
|
1877
|
+
max: 1,
|
|
1878
|
+
min: 0,
|
|
1879
|
+
onChange: (event) => {
|
|
1880
|
+
trackDrag("to", event.nativeEvent);
|
|
1881
|
+
},
|
|
1882
|
+
ref: (input) => {
|
|
1883
|
+
toInput.current = input;
|
|
1884
|
+
return commitOnRelease("to")(input);
|
|
1885
|
+
},
|
|
1886
|
+
step: STEP,
|
|
1887
|
+
type: "range",
|
|
1888
|
+
value: positions.to
|
|
1889
|
+
}
|
|
1890
|
+
)
|
|
1891
|
+
] }),
|
|
1892
|
+
/* @__PURE__ */ jsx("div", { className: "relative mx-2 h-4", children: years.map(({ count, position }) => /* @__PURE__ */ jsx(
|
|
1893
|
+
"button",
|
|
1894
|
+
{
|
|
1895
|
+
className: `absolute top-0 -translate-x-1/2 rounded px-0.5 text-[10px] leading-4 whitespace-nowrap hover:underline ${count === recommendedMaxAgeYears ? "font-semibold text-red-600" : "text-slate-500 hover:text-slate-900"}`,
|
|
1896
|
+
onClick: () => {
|
|
1897
|
+
setYears(count);
|
|
1898
|
+
},
|
|
1899
|
+
style: { left: percent(position) },
|
|
1900
|
+
title: count === recommendedMaxAgeYears ? text.recommendedMaxAge(count) : text.lastYears(count),
|
|
1901
|
+
type: "button",
|
|
1902
|
+
children: text.yearsShort(count)
|
|
1903
|
+
},
|
|
1904
|
+
count
|
|
1905
|
+
)) }),
|
|
1906
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-baseline justify-between gap-2 text-xs", children: [
|
|
1907
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-slate-700", children: rangeLabel }),
|
|
1908
|
+
isAllDates ? null : /* @__PURE__ */ jsx(
|
|
1909
|
+
"button",
|
|
1910
|
+
{
|
|
1911
|
+
className: "shrink-0 font-medium text-slate-600 underline decoration-slate-300 underline-offset-2 hover:text-slate-900",
|
|
1912
|
+
onClick: () => {
|
|
1913
|
+
onChange({});
|
|
1914
|
+
},
|
|
1915
|
+
type: "button",
|
|
1916
|
+
children: text.allDates
|
|
1917
|
+
}
|
|
1918
|
+
)
|
|
1919
|
+
] }),
|
|
1920
|
+
dateInputs === "none" ? null : /* @__PURE__ */ jsxs("details", { className: "mt-1 text-xs text-slate-600", open: dateInputs === "open" || void 0, children: [
|
|
1921
|
+
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer select-none hover:text-slate-900", children: text.exactDates }),
|
|
1922
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5 grid grid-cols-2 gap-2", children: ["from", "to"].map((key) => /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
1923
|
+
text[key],
|
|
1924
|
+
/* @__PURE__ */ jsx(
|
|
1925
|
+
"input",
|
|
1926
|
+
{
|
|
1927
|
+
className: "rounded-md border border-slate-200 px-2 py-1.5 text-sm text-slate-800",
|
|
1928
|
+
onChange: (event) => {
|
|
1929
|
+
setDay(key, event.target.value);
|
|
1930
|
+
},
|
|
1931
|
+
type: "date",
|
|
1932
|
+
value: value[key] ?? ""
|
|
1933
|
+
}
|
|
1934
|
+
)
|
|
1935
|
+
] }, key)) })
|
|
1936
|
+
] })
|
|
1937
|
+
] });
|
|
1938
|
+
};
|
|
1525
1939
|
|
|
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 };
|
|
1940
|
+
export { BASE_COLOR, FloatingPhotoViewer, FloatingViewerInfoButton, LocationPickOnMap, MAPILLARY_FEATURE_BAR_LABELS, MapillaryFeatureBar, PhotoCount, PhotoDate, PhotoDateRangeFilter, SELECTION_COLOR, SelectedMapFeatureLayer, 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, useSelectedMapillaryFeature, useViewSuggestions, useViewpointLine, useViewpoints, viewDirectionKeyFromFeatures, viewSuggestionLabel };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { type MapFeatureImages, type TargetImage } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
import { type MapillaryFeatureBarLabels } from './featureBarLabels';
|
|
2
3
|
export type MapillaryFeatureBarProps = {
|
|
3
4
|
data: MapFeatureImages;
|
|
4
5
|
shownImage: TargetImage | null;
|
|
5
6
|
onShow: (image: TargetImage) => void;
|
|
7
|
+
/** Replace single texts, e.g. `name` to map values to your own sign catalogue. */
|
|
8
|
+
labels?: Partial<MapillaryFeatureBarLabels>;
|
|
6
9
|
};
|
|
7
10
|
/**
|
|
8
11
|
* A Mapillary sign or object above its photo: what it is, when Mapillary saw it, and one button
|
|
@@ -10,4 +13,4 @@ export type MapillaryFeatureBarProps = {
|
|
|
10
13
|
* that day's photos. Dates show month and year; the tooltip has the full date and the age.
|
|
11
14
|
* Texts and dates follow the `StreetImageryLocaleProvider`.
|
|
12
15
|
*/
|
|
13
|
-
export declare const MapillaryFeatureBar: ({ data, shownImage, onShow }: MapillaryFeatureBarProps) => import("react").JSX.Element;
|
|
16
|
+
export declare const MapillaryFeatureBar: ({ data, shownImage, onShow, labels: labelOverrides, }: MapillaryFeatureBarProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Bbox, type MapFeatureImages, type ProviderId, type TargetImage } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
export type SelectedMapFeatureLayerProps = {
|
|
3
|
+
/** From `useSelectedMapillaryFeature`. Nothing is drawn without it. */
|
|
4
|
+
data: MapFeatureImages | null;
|
|
5
|
+
/** The shown photo of the feature: a dotted line runs from its camera to the feature. */
|
|
6
|
+
shownImage?: TargetImage | null;
|
|
7
|
+
/** The providers whose map features are drawn, to ring the dot that is on the map. */
|
|
8
|
+
providers: ProviderId[];
|
|
9
|
+
bbox: Bbox | null;
|
|
10
|
+
zoom: number;
|
|
11
|
+
/** Color of the dot and disk. Default: the map features' purple. */
|
|
12
|
+
color?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The selected sign or object on the map: its dot on a light disk, and a dotted line to it from
|
|
16
|
+
* the camera position of the shown photo (Mapillary's computed position, which the feature was
|
|
17
|
+
* located from). Draw it after `StreetLevelImagerySourcesAndLayers`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const SelectedMapFeatureLayer: ({ data, shownImage, providers, bbox, zoom, color, }: SelectedMapFeatureLayerProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type StreetImageryLocale } from '@osm-editor-kit/street-imagery';
|
|
2
|
+
/**
|
|
3
|
+
* Texts of `MapillaryFeatureBar`. Kept out of the package-wide messages, so the German sign names
|
|
4
|
+
* are only bundled by apps that show the feature bar.
|
|
5
|
+
*/
|
|
6
|
+
export type MapillaryFeatureBarLabels = {
|
|
7
|
+
/** Name of a Mapillary value like `regulatory--turn-right-ahead--g1`. */
|
|
8
|
+
name: (value: string) => string;
|
|
9
|
+
/** Line below the name; `first` and `last` are the rendered dates. */
|
|
10
|
+
seen: string;
|
|
11
|
+
noPhotos: string;
|
|
12
|
+
daysSummary: (photos: number, days: number) => string;
|
|
13
|
+
/** Third tooltip line of a day button, below date and age. */
|
|
14
|
+
dayPhotos: (photos: number, canStep: boolean) => string;
|
|
15
|
+
};
|
|
16
|
+
export declare const MAPILLARY_FEATURE_BAR_LABELS: Record<StreetImageryLocale, MapillaryFeatureBarLabels>;
|
|
@@ -34,3 +34,21 @@ export declare const useMapillaryImageDetections: (imageId: string | null | unde
|
|
|
34
34
|
filter?: (value: string) => boolean;
|
|
35
35
|
filterKey?: string;
|
|
36
36
|
}) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("@osm-editor-kit/street-imagery").MapillaryDetection[]>, Error>;
|
|
37
|
+
/**
|
|
38
|
+
* A selected Mapillary map feature (sign, object) for a host's feature + photo selection: all its
|
|
39
|
+
* photos by day, the photo that is shown (`shownPhotoId`), and the one to open first — the best of
|
|
40
|
+
* the newest day, from within `minCapturedAt` (ms) when set. Feed `data` and `shownImage` to
|
|
41
|
+
* `MapillaryFeatureBar` and `SelectedMapFeatureLayer`, and `shownImage.outline` to `lookAt`.
|
|
42
|
+
*/
|
|
43
|
+
export declare const useSelectedMapillaryFeature: ({ featureId, shownPhotoId, minCapturedAt, shape, }: {
|
|
44
|
+
featureId: string | null | undefined;
|
|
45
|
+
shownPhotoId?: string | null;
|
|
46
|
+
minCapturedAt?: number | null;
|
|
47
|
+
shape?: TargetShape;
|
|
48
|
+
}) => {
|
|
49
|
+
data: NonNullable<NoInfer<import("@osm-editor-kit/street-imagery").MapFeatureImages | null>> | null;
|
|
50
|
+
isLoading: boolean;
|
|
51
|
+
isError: boolean;
|
|
52
|
+
shownImage: import("@osm-editor-kit/street-imagery").TargetImage | null;
|
|
53
|
+
firstImage: import("@osm-editor-kit/street-imagery").TargetImage | null;
|
|
54
|
+
};
|
package/dist/photo-details.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useStreetImageryI18n } from './chunk-
|
|
1
|
+
import { useStreetImageryI18n } from './chunk-ZAIXGYAH.js';
|
|
2
2
|
import { providerById, formatLocalDateTime, formatDate, formatRelativeDate } from '@osm-editor-kit/street-imagery';
|
|
3
3
|
import { useRef, useEffect } from 'react';
|
|
4
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ViewSuggestion } from '@osm-editor-kit/street-imagery';
|
|
1
|
+
import type { PhotoCandidate, ViewSuggestion } from '@osm-editor-kit/street-imagery';
|
|
2
2
|
import { type ReactNode } from 'react';
|
|
3
3
|
/** Info button for `titleActions`; `label` is its tooltip and accessible name. */
|
|
4
4
|
export declare const FloatingViewerInfoButton: ({ label, onClick, }: {
|
|
@@ -12,7 +12,13 @@ export type FloatingPhotoViewerProps = {
|
|
|
12
12
|
/** Suggested views (viewpoint × direction); shown as chips. Empty hides the strip. */
|
|
13
13
|
suggestions: ViewSuggestion[];
|
|
14
14
|
activeDirectionKey: string | null;
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* A view button was clicked. `candidate` is the photo to show: the view's best one, or the next
|
|
17
|
+
* one when the active view is clicked again.
|
|
18
|
+
*/
|
|
19
|
+
onSelectSuggestion: (suggestion: ViewSuggestion, candidate: PhotoCandidate) => void;
|
|
20
|
+
/** The photo in the viewer, so the active view can show which of its photos that is. */
|
|
21
|
+
shownPhotoId?: string | null;
|
|
16
22
|
canGoBack: boolean;
|
|
17
23
|
canGoForward: boolean;
|
|
18
24
|
onBack: () => void;
|
|
@@ -51,4 +57,4 @@ export type FloatingPhotoViewerProps = {
|
|
|
51
57
|
* Drag the header to move, drag the left edge to resize. Keys: Esc closes, [ and ] step history,
|
|
52
58
|
* Alt + arrow left/right step along (`step`).
|
|
53
59
|
*/
|
|
54
|
-
export declare const FloatingPhotoViewer: ({ title, titleActions, suggestions, activeDirectionKey, onSelectSuggestion, canGoBack, canGoForward, onBack, onForward, onClose, step, toolbar, status, children, footer, drawer, storageKey, }: FloatingPhotoViewerProps) => import("react").JSX.Element;
|
|
60
|
+
export declare const FloatingPhotoViewer: ({ title, titleActions, suggestions, activeDirectionKey, onSelectSuggestion, shownPhotoId, canGoBack, canGoForward, onBack, onForward, onClose, step, toolbar, status, children, footer, drawer, storageKey, }: FloatingPhotoViewerProps) => import("react").JSX.Element;
|
|
@@ -19,7 +19,10 @@ export declare const useCanGoForward: () => boolean;
|
|
|
19
19
|
export declare const getViewpointSession: () => {
|
|
20
20
|
actions: {
|
|
21
21
|
open: (input: OpenInput) => void;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Show a photo; truncates forward history. For the photo that is shown already, only its
|
|
24
|
+
* direction changes.
|
|
25
|
+
*/
|
|
23
26
|
showPhoto: (entry: ViewpointHistoryEntry) => void;
|
|
24
27
|
back: () => ViewpointHistoryEntry | null;
|
|
25
28
|
forward: () => ViewpointHistoryEntry | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osm-editor-kit/street-imagery-react",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
4
|
"description": "React MapLibre layers and photo viewers for street-level imagery providers.",
|
|
5
5
|
"homepage": "https://github.com/osmberlin/street-space-editor/tree/main/packages/street-imagery-react",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"dist"
|
|
15
15
|
],
|
|
16
16
|
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
17
18
|
"exports": {
|
|
18
19
|
".": {
|
|
19
20
|
"types": "./dist/index.d.ts",
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
"zustand": "^5.0.7"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"@osm-editor-kit/street-imagery": "^0.1.0-alpha.
|
|
59
|
+
"@osm-editor-kit/street-imagery": "^0.1.0-alpha.7",
|
|
59
60
|
"@panoramax/web-viewer": "^5",
|
|
60
61
|
"@tanstack/react-query": "^5",
|
|
61
62
|
"mapillary-js": "^4",
|