highlight()}
+ on:mouseleave={() => highlight(false)}
+ >
(loaded = true)}
@@ -68,7 +97,7 @@
{#if canZoom && loaded}
previewedImage.set(image)}>
+ on:click={() => previewedImage.set(image)}>
{/if}
diff --git a/src/UI/Image/ImageCarousel.ts b/src/UI/Image/ImageCarousel.ts
index 9b0ee5d21..8c6d1e5b9 100644
--- a/src/UI/Image/ImageCarousel.ts
+++ b/src/UI/Image/ImageCarousel.ts
@@ -8,7 +8,6 @@ import ImageProvider, { ProvidedImage } from "../../Logic/ImageProviders/ImagePr
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { Changes } from "../../Logic/Osm/Changes"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
-import { Feature } from "geojson"
import SvelteUIElement from "../Base/SvelteUIElement"
import AttributedImage from "./AttributedImage.svelte"
@@ -30,6 +29,7 @@ export class ImageCarousel extends Toggle {
try {
let image: BaseUIElement = new SvelteUIElement(AttributedImage, {
image: url,
+ state,
previewedImage: state?.previewedImage,
})
diff --git a/src/UI/Image/LinkableImage.svelte b/src/UI/Image/LinkableImage.svelte
index b290ecf95..8112abfb7 100644
--- a/src/UI/Image/LinkableImage.svelte
+++ b/src/UI/Image/LinkableImage.svelte
@@ -14,8 +14,8 @@
import AttributedImage from "./AttributedImage.svelte"
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
import LoginToggle from "../Base/LoginToggle.svelte"
- import ImagePreview from "./ImagePreview.svelte"
- import FloatOver from "../Base/FloatOver.svelte"
+ import { onDestroy } from "svelte"
+ import { Utils } from "../../Utils"
export let tags: UIEventSource
export let state: SpecialVisualizationState
@@ -23,6 +23,8 @@
export let feature: Feature
export let layer: LayerConfig
+ export let highlighted: UIEventSource = undefined
+
export let linkable = true
let targetValue = Object.values(image.osmTags)[0]
let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v))
@@ -33,7 +35,7 @@
key: undefined,
provider: AllImageProviders.byName(image.provider),
date: new Date(image.date),
- id: Object.values(image.osmTags)[0],
+ id: Object.values(image.osmTags)[0]
}
async function applyLink(isLinked: boolean) {
@@ -44,7 +46,7 @@
if (isLinked) {
const action = new LinkImageAction(currentTags.id, key, url, tags, {
theme: tags.data._orig_theme ?? state.layout.id,
- changeType: "link-image",
+ changeType: "link-image"
})
await state.changes.applyAction(action)
} else {
@@ -53,7 +55,7 @@
if (v === url) {
const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, {
theme: tags.data._orig_theme ?? state.layout.id,
- changeType: "remove-image",
+ changeType: "remove-image"
})
state.changes.applyAction(action)
}
@@ -62,16 +64,30 @@
}
isLinked.addCallback((isLinked) => applyLink(isLinked))
+
+ let element: HTMLDivElement
+ if (highlighted) {
+
+ onDestroy(
+ highlighted.addCallbackD(highlightedUrl => {
+ if (highlightedUrl === image.pictureUrl) {
+ Utils.scrollIntoView(element)
+ }
+ })
+ )
+ }
diff --git a/src/UI/Image/NearbyImages.svelte b/src/UI/Image/NearbyImages.svelte
index 665490c2b..d70dc2ba5 100644
--- a/src/UI/Image/NearbyImages.svelte
+++ b/src/UI/Image/NearbyImages.svelte
@@ -7,13 +7,23 @@
import type { SpecialVisualizationState } from "../SpecialVisualization"
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
import LinkableImage from "./LinkableImage.svelte"
- import type { Feature } from "geojson"
+ import type { Feature, Point } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import Loading from "../Base/Loading.svelte"
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
import MapillaryLink from "../BigComponents/MapillaryLink.svelte"
+ import MaplibreMap from "../Map/MaplibreMap.svelte"
+ import { Map as MlMap } from "maplibre-gl"
+ import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
+ import ShowDataLayer from "../Map/ShowDataLayer"
+ import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
+ import * as geocoded_image from "../../assets/generated/layers/geocoded_image.json"
+ import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
+ import { onDestroy } from "svelte"
+ import { BBox } from "../../Logic/BBox"
+
export let tags: UIEventSource
export let state: SpecialVisualizationState
@@ -42,12 +52,100 @@
[loadedImages]
)
+ let asFeatures = result.map(p4cs => p4cs.map(p4c => (>{
+ type: "Feature",
+ geometry: {
+ type: "Point",
+ coordinates: [p4c.coordinates.lng, p4c.coordinates.lat]
+ },
+ properties: {
+ id: p4c.pictureUrl,
+ rotation: p4c.direction
+ }
+ })))
+
+ let selected = new UIEventSource(undefined)
+ let selectedAsFeature = selected.mapD(s => {
+ return [>{
+ type: "Feature",
+ geometry: {
+ type: "Point",
+ coordinates: [s.coordinates.lng, s.coordinates.lat]
+ },
+ properties: {
+ id: s.pictureUrl,
+ selected: "yes",
+ rotation: s.direction
+ }
+ }]
+ })
+
let someLoading = imageState.state.mapD((stateRecord) =>
Object.values(stateRecord).some((v) => v === "loading")
)
let errors = imageState.state.mapD((stateRecord) =>
Object.keys(stateRecord).filter((k) => stateRecord[k] === "error")
)
+ let highlighted = new UIEventSource(undefined)
+
+ onDestroy(highlighted.addCallbackD(hl => {
+ const p4c = result.data?.find(i => i.pictureUrl === hl)
+ selected.set(p4c)
+ }
+ ))
+
+ let map: UIEventSource = new UIEventSource(undefined)
+ let mapProperties = new MapLibreAdaptor(map, {
+ rasterLayer: state.mapProperties.rasterLayer,
+ rotation: state.mapProperties.rotation,
+ pitch: state.mapProperties.pitch,
+ zoom: new UIEventSource(16),
+ location: new UIEventSource({ lon, lat }),
+ })
+
+
+ const geocodedImageLayer = new LayerConfig(geocoded_image)
+ new ShowDataLayer(map, {
+ features: new StaticFeatureSource(asFeatures),
+ layer: geocodedImageLayer,
+ zoomToFeatures: true,
+ onClick: (feature) => {
+ highlighted.set(feature.properties.id)
+ }
+ })
+
+
+ ShowDataLayer.showMultipleLayers(
+ map,
+ new StaticFeatureSource([feature]),
+ state.layout.layers
+ )
+
+ onDestroy(
+ asFeatures.addCallbackAndRunD(features => {
+ if(features.length == 0){
+ return
+ }
+ let bbox = BBox.get(features[0])
+ for (const f of features) {
+ bbox = bbox.unionWith(BBox.get(f))
+ }
+ mapProperties.maxbounds.set(bbox.pad(1.1))
+ })
+
+ )
+
+ new ShowDataLayer(map, {
+ features: new StaticFeatureSource(selectedAsFeature),
+ layer: geocodedImageLayer,
+ onClick: (feature) => {
+ highlighted.set(feature.properties.id)
+ }
+ })
+
+
+
+
@@ -62,12 +160,24 @@
{:else}
{#each $result as image (image.pictureUrl)}
-
-
+ {highlighted.set(image.pictureUrl)}}
+ on:mouseleave={() =>{ highlighted.set(undefined); selected.set(undefined)}}
+ >
+
{/each}
{/if}
+
+
+
+
+
+
{#if $someLoading && $result.length > 0}
@@ -80,9 +190,10 @@
/>
{/if}
-
+
+
+
+
+
diff --git a/src/UI/Image/NearbyImagesCollapsed.svelte b/src/UI/Image/NearbyImagesCollapsed.svelte
index 5e647655c..10a7c1c1b 100644
--- a/src/UI/Image/NearbyImagesCollapsed.svelte
+++ b/src/UI/Image/NearbyImagesCollapsed.svelte
@@ -11,8 +11,9 @@
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
import LoginToggle from "../Base/LoginToggle.svelte"
import { ariaLabel } from "../../Utils/ariaLabel"
- import { Accordion, AccordionItem } from "flowbite-svelte"
+ import { Accordion, AccordionItem, Modal } from "flowbite-svelte"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
+ import Popup from "../Base/Popup.svelte"
export let tags: UIEventSource
export let state: SpecialVisualizationState
@@ -24,15 +25,16 @@
export let layer: LayerConfig
const t = Translations.t.image.nearby
- let expanded = false
let enableLogin = state.featureSwitches.featureSwitchEnableLogin
+ export let shown = new UIEventSource(false)
{#if enableLogin.data}
-
-
+