forked from MapComplete/MapComplete
chore: automated housekeeping...
This commit is contained in:
parent
79b6927b56
commit
42ded4c1b1
328 changed files with 4062 additions and 1284 deletions
|
|
@ -165,7 +165,7 @@
|
|||
<Page {onlyLink} shown={pg.failedImages} bodyPadding="p-0 pb-4">
|
||||
<svelte:fragment slot="header">
|
||||
<PhotoIcon />
|
||||
<Tr t={Translations.t.imageQueue.menu.Subs({count: $nrOfFailedImages.length})} />
|
||||
<Tr t={Translations.t.imageQueue.menu.Subs({ count: $nrOfFailedImages.length })} />
|
||||
</svelte:fragment>
|
||||
<QueuedImagesView {state} />
|
||||
</Page>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@
|
|||
export let attributionFormat: "minimal" | "medium" | "large" = "medium"
|
||||
let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage
|
||||
export let canZoom = previewedImage !== undefined
|
||||
export let nearbyFeatures: Feature<Geometry, HotspotProperties>[] | Store<Feature<Geometry, HotspotProperties>[]> = []
|
||||
export let nearbyFeatures:
|
||||
| Feature<Geometry, HotspotProperties>[]
|
||||
| Store<Feature<Geometry, HotspotProperties>[]> = []
|
||||
|
||||
let loaded = false
|
||||
let showBigPreview = new UIEventSource(false)
|
||||
|
|
@ -49,7 +51,7 @@
|
|||
previewedImage.addCallbackAndRun((previewedImage) => {
|
||||
showBigPreview.set(
|
||||
previewedImage !== undefined &&
|
||||
(previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url)
|
||||
(previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url)
|
||||
)
|
||||
})
|
||||
)
|
||||
|
|
@ -67,16 +69,15 @@
|
|||
type: "Feature",
|
||||
properties: {
|
||||
id: image.id,
|
||||
rotation: image.rotation
|
||||
rotation: image.rotation,
|
||||
},
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [image.lon, image.lat]
|
||||
}
|
||||
coordinates: [image.lon, image.lat],
|
||||
},
|
||||
}
|
||||
state?.geocodedImages.set([f])
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}>
|
||||
|
|
@ -135,10 +136,12 @@
|
|||
/>
|
||||
|
||||
{#if image.isSpherical}
|
||||
<div class="absolute top-0 left-0 w-full h-full flex justify-center items-center pointer-events-none">
|
||||
<div class="bg-black opacity-50 rounded-full p-[3.25rem]">
|
||||
<div class="w-0 h-0 relative flex items-center justify-center">
|
||||
<Panorama360 class="absolute w-16 h-16" color="#ffffff" />
|
||||
<div
|
||||
class="pointer-events-none absolute left-0 top-0 flex h-full w-full items-center justify-center"
|
||||
>
|
||||
<div class="rounded-full bg-black p-[3.25rem] opacity-50">
|
||||
<div class="relative flex h-0 w-0 items-center justify-center">
|
||||
<Panorama360 class="absolute h-16 w-16" color="#ffffff" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -157,7 +160,6 @@
|
|||
<ImageAttribution {image} {attributionFormat} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{:else if image.status === "hidden"}
|
||||
<div class="subtle">This image has been reported</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@
|
|||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: GeoOperations.centerpointCoordinates(feature)
|
||||
coordinates: GeoOperations.centerpointCoordinates(feature),
|
||||
},
|
||||
properties: {
|
||||
name: layer?.title?.GetRenderValue(feature.properties)?.Subs(feature.properties)?.txt ?? feature?.properties?.name,
|
||||
focus: true
|
||||
}
|
||||
name:
|
||||
layer?.title?.GetRenderValue(feature.properties)?.Subs(feature.properties)?.txt ??
|
||||
feature?.properties?.name,
|
||||
focus: true,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@
|
|||
|
||||
export let image: Partial<ProvidedImage> & { id: string; url: string }
|
||||
export let clss: string = undefined
|
||||
export let nearbyFeatures: Feature<Geometry, HotspotProperties>[] | Store<Feature<Geometry, HotspotProperties>[]> = []
|
||||
export let nearbyFeatures:
|
||||
| Feature<Geometry, HotspotProperties>[]
|
||||
| Store<Feature<Geometry, HotspotProperties>[]> = []
|
||||
|
||||
let isLoaded = new UIEventSource(false)
|
||||
console.log(">>> slots are", $$slots)
|
||||
|
|
@ -37,17 +39,17 @@
|
|||
</div>
|
||||
|
||||
{#if $$slots["dot-menu-actions"]}
|
||||
<DotMenu dotsPosition="top-0 left-0" dotsSize="w-8 h-8" hideBackground>
|
||||
<slot name="dot-menu-actions">
|
||||
<button
|
||||
class="no-image-background pointer-events-auto flex items-center"
|
||||
on:click={() => ImageProvider.offerImageAsDownload(image)}
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 px-2 opacity-100" />
|
||||
<Tr t={Translations.t.general.download.downloadImage} />
|
||||
</button>
|
||||
</slot>
|
||||
</DotMenu>
|
||||
<DotMenu dotsPosition="top-0 left-0" dotsSize="w-8 h-8" hideBackground>
|
||||
<slot name="dot-menu-actions">
|
||||
<button
|
||||
class="no-image-background pointer-events-auto flex items-center"
|
||||
on:click={() => ImageProvider.offerImageAsDownload(image)}
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 px-2 opacity-100" />
|
||||
<Tr t={Translations.t.general.download.downloadImage} />
|
||||
</button>
|
||||
</slot>
|
||||
</DotMenu>
|
||||
{/if}
|
||||
<div
|
||||
class="pointer-events-none absolute bottom-0 left-0 flex w-full flex-wrap items-end justify-between"
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@
|
|||
import type { Feature, Geometry, Point } from "geojson"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
|
||||
|
||||
export let nearbyFeatures: Feature<Geometry, HotspotProperties>[] | Store<Feature<Geometry, HotspotProperties>[]> = []
|
||||
export let nearbyFeatures:
|
||||
| Feature<Geometry, HotspotProperties>[]
|
||||
| Store<Feature<Geometry, HotspotProperties>[]> = []
|
||||
export let image: Partial<ProvidedImage>
|
||||
let panzoomInstance = undefined
|
||||
let panzoomEl: HTMLElement
|
||||
|
|
@ -34,17 +35,15 @@
|
|||
if (Array.isArray(nearbyFeatures)) {
|
||||
viewer.setNearbyFeatures(nearbyFeatures)
|
||||
} else {
|
||||
nearbyFeatures.addCallbackAndRunD(feats => {
|
||||
nearbyFeatures.addCallbackAndRunD((feats) => {
|
||||
viewer.setNearbyFeatures(feats)
|
||||
})
|
||||
}
|
||||
isLoaded.set(true)
|
||||
|
||||
}
|
||||
|
||||
$: {
|
||||
if (image.isSpherical) {
|
||||
|
||||
initPhotosphere()
|
||||
} else if (panzoomEl) {
|
||||
panzoomInstance = panzoom(panzoomEl, {
|
||||
|
|
@ -52,7 +51,7 @@
|
|||
boundsPadding: 0.49,
|
||||
minZoom: 0.1,
|
||||
maxZoom: 25,
|
||||
initialZoom: 1.0
|
||||
initialZoom: 1.0,
|
||||
})
|
||||
} else {
|
||||
panzoomInstance?.dispose()
|
||||
|
|
@ -61,17 +60,17 @@
|
|||
</script>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="./css/pannellum.css">
|
||||
<link rel="stylesheet" href="./css/pannellum.css" />
|
||||
</head>
|
||||
{#if image.isSpherical}
|
||||
<div bind:this={viewerEl} class="w-full h-full" />
|
||||
<div bind:this={viewerEl} class="h-full w-full" />
|
||||
{:else}
|
||||
<img
|
||||
bind:this={panzoomEl}
|
||||
class="panzoom-image h-fit max-w-fit"
|
||||
on:load={() => {
|
||||
isLoaded?.setData(true)
|
||||
}}
|
||||
isLoaded?.setData(true)
|
||||
}}
|
||||
src={image.url_hd ?? image.url}
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
export let linkable = true
|
||||
let targetValue = Object.values(image.osmTags)[0]
|
||||
let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v))
|
||||
isLinked.addCallbackAndRun(linked => {
|
||||
isLinked.addCallbackAndRun((linked) => {
|
||||
if (linked) {
|
||||
MenuState.previewedImage.set(undefined)
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
provider: AllImageProviders.byName(image.provider),
|
||||
date: new Date(image.date),
|
||||
id: Object.values(image.osmTags)[0],
|
||||
isSpherical: image.details.isSpherical
|
||||
isSpherical: image.details.isSpherical,
|
||||
}
|
||||
|
||||
async function applyLink(isLinked: boolean) {
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
if (isLinked) {
|
||||
const action = new LinkImageAction(currentTags.id, key, url, tags, {
|
||||
theme: tags.data._orig_theme ?? state.theme.id,
|
||||
changeType: "link-image"
|
||||
changeType: "link-image",
|
||||
})
|
||||
await state.changes.applyAction(action)
|
||||
} else {
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
if (v === url) {
|
||||
const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, {
|
||||
theme: tags.data._orig_theme ?? state.theme.id,
|
||||
changeType: "remove-image"
|
||||
changeType: "remove-image",
|
||||
})
|
||||
state.changes.applyAction(action)
|
||||
}
|
||||
|
|
@ -99,9 +99,7 @@
|
|||
attributionFormat="minimal"
|
||||
>
|
||||
<svelte:fragment slot="dot-menu-actions">
|
||||
|
||||
<LoginToggle {state} silentFail={true} hiddenFail={true}>
|
||||
|
||||
{#if linkable}
|
||||
<label>
|
||||
<input bind:checked={$isLinked} type="checkbox" />
|
||||
|
|
@ -110,7 +108,6 @@
|
|||
{/if}
|
||||
</LoginToggle>
|
||||
</svelte:fragment>
|
||||
|
||||
</AttributedImage>
|
||||
<LoginToggle {state} silentFail={true}>
|
||||
{#if linkable}
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@
|
|||
(pics: P4CPicture[]) =>
|
||||
pics
|
||||
.filter(
|
||||
(p: P4CPicture) =>
|
||||
!loadedImages.data.has(p.pictureUrl) // We don't show any image which is already linked
|
||||
(p: P4CPicture) => !loadedImages.data.has(p.pictureUrl) // We don't show any image which is already linked
|
||||
)
|
||||
.slice(0, 25),
|
||||
[loadedImages]
|
||||
|
|
@ -60,15 +59,15 @@
|
|||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [p4c.coordinates.lng, p4c.coordinates.lat]
|
||||
coordinates: [p4c.coordinates.lng, p4c.coordinates.lat],
|
||||
},
|
||||
properties: <PanoramaView>{
|
||||
id: p4c.pictureUrl,
|
||||
url: p4c.pictureUrl,
|
||||
northOffset: p4c.direction,
|
||||
rotation: p4c.direction,
|
||||
spherical: p4c.details.isSpherical ? "yes" : "no"
|
||||
}
|
||||
spherical: p4c.details.isSpherical ? "yes" : "no",
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
@ -80,14 +79,14 @@
|
|||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [s.coordinates.lng, s.coordinates.lat]
|
||||
coordinates: [s.coordinates.lng, s.coordinates.lat],
|
||||
},
|
||||
properties: {
|
||||
id: s.pictureUrl,
|
||||
selected: "yes",
|
||||
rotation: s.direction
|
||||
}
|
||||
}
|
||||
rotation: s.direction,
|
||||
},
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
|
|
@ -112,7 +111,7 @@
|
|||
rotation: state.mapProperties.rotation,
|
||||
pitch: state.mapProperties.pitch,
|
||||
zoom: new UIEventSource<number>(16),
|
||||
location: new UIEventSource({ lon, lat })
|
||||
location: new UIEventSource({ lon, lat }),
|
||||
})
|
||||
|
||||
const geocodedImageLayer = new LayerConfig(<LayerConfigJson>geocoded_image)
|
||||
|
|
@ -123,7 +122,7 @@
|
|||
onClick: (feature) => {
|
||||
console.log("CLicked:", feature.properties)
|
||||
highlighted.set(feature.properties.id)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
ShowDataLayer.showMultipleLayers(map, new StaticFeatureSource([feature]), state.theme.layers)
|
||||
|
|
@ -146,24 +145,29 @@
|
|||
layer: geocodedImageLayer,
|
||||
onClick: (feature) => {
|
||||
highlighted.set(feature.properties.id)
|
||||
}
|
||||
},
|
||||
})
|
||||
let nearbyFeatures: Store<Feature[]> = asFeatures.map(nearbyPoints => {
|
||||
return [{
|
||||
type: "Feature",
|
||||
geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) },
|
||||
properties: {
|
||||
name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt,
|
||||
focus: true
|
||||
}
|
||||
}, ...nearbyPoints.filter(p => p.properties.spherical === "yes").map(f => ({
|
||||
...f, properties: {
|
||||
name: "Nearby panorama",
|
||||
pitch: "auto",
|
||||
type: "scene",
|
||||
gotoPanorama: f
|
||||
}
|
||||
}))
|
||||
let nearbyFeatures: Store<Feature[]> = asFeatures.map((nearbyPoints) => {
|
||||
return [
|
||||
{
|
||||
type: "Feature",
|
||||
geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) },
|
||||
properties: {
|
||||
name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt,
|
||||
focus: true,
|
||||
},
|
||||
},
|
||||
...nearbyPoints
|
||||
.filter((p) => p.properties.spherical === "yes")
|
||||
.map((f) => ({
|
||||
...f,
|
||||
properties: {
|
||||
name: "Nearby panorama",
|
||||
pitch: "auto",
|
||||
type: "scene",
|
||||
gotoPanorama: f,
|
||||
},
|
||||
})),
|
||||
]
|
||||
})
|
||||
|
||||
|
|
@ -204,7 +208,16 @@
|
|||
selected.set(undefined)
|
||||
}}
|
||||
>
|
||||
<LinkableImage {tags} {image} {state} {feature} {layer} {linkable} {highlighted} {nearbyFeatures} />
|
||||
<LinkableImage
|
||||
{tags}
|
||||
{image}
|
||||
{state}
|
||||
{feature}
|
||||
{layer}
|
||||
{linkable}
|
||||
{highlighted}
|
||||
{nearbyFeatures}
|
||||
/>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,40 +13,45 @@
|
|||
export let imageArguments: ImageUploadArguments
|
||||
let confirmDelete = new UIEventSource(false)
|
||||
|
||||
|
||||
function del() {
|
||||
queue.delete(imageArguments)
|
||||
}
|
||||
|
||||
const t = Translations.t
|
||||
let src = undefined
|
||||
try{
|
||||
|
||||
try {
|
||||
src = URL.createObjectURL(imageArguments.blob)
|
||||
}catch (e) {
|
||||
} catch (e) {
|
||||
console.error("Could not create an ObjectURL for blob", imageArguments.blob)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="low-interaction rounded border-interactive w-fit p-2 m-1 flex flex-col">
|
||||
|
||||
<img class="max-w-64 w-auto max-h-64 w-auto" {src} />
|
||||
{imageArguments.featureId} {imageArguments.layoutId}
|
||||
<button class="as-link self-end" on:click={() => {confirmDelete.set(true)}}>
|
||||
<div class="low-interaction border-interactive m-1 flex w-fit flex-col rounded p-2">
|
||||
<img class="max-h-64 w-auto w-auto max-w-64" {src} />
|
||||
{imageArguments.featureId}
|
||||
{imageArguments.layoutId}
|
||||
<button
|
||||
class="as-link self-end"
|
||||
on:click={() => {
|
||||
confirmDelete.set(true)
|
||||
}}
|
||||
>
|
||||
<TrashIcon class="w-4" />
|
||||
<Tr t={t.imageQueue.delete} />
|
||||
</button>
|
||||
<Popup shown={confirmDelete} dismissable={true}>
|
||||
<Page shown={confirmDelete}>
|
||||
<svelte:fragment slot="header">
|
||||
<TrashIcon class="w-8 m-1" />
|
||||
<TrashIcon class="m-1 w-8" />
|
||||
<Tr t={t.imageQueue.confirmDeleteTitle} />
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex flex-col ">
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-center">
|
||||
<img class="max-w-128 w-auto max-h-128 w-auto" src={URL.createObjectURL(imageArguments.blob)} />
|
||||
<img
|
||||
class="max-w-128 max-h-128 w-auto w-auto"
|
||||
src={URL.createObjectURL(imageArguments.blob)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -54,8 +59,7 @@
|
|||
<Tr t={t.general.back} />
|
||||
</BackButton>
|
||||
<button on:click={() => del()} class="primary w-full">
|
||||
|
||||
<TrashIcon class="w-8 m-1" />
|
||||
<TrashIcon class="m-1 w-8" />
|
||||
<Tr t={t.imageQueue.confirmDelete} />
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@
|
|||
<Tr t={q.intro} />
|
||||
</div>
|
||||
|
||||
<UploadingImageCounter {state}/>
|
||||
<UploadingImageCounter {state} />
|
||||
|
||||
{#if $isUploading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<button class="primary" on:click={() => state.imageUploadManager.uploadQueue()}>
|
||||
<ArrowPathIcon class="w-8 h-8 m-1" />
|
||||
<ArrowPathIcon class="m-1 h-8 w-8" />
|
||||
<Tr t={q.retryAll} />
|
||||
</button>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,14 @@
|
|||
errs.push(canBeUploaded.error)
|
||||
continue
|
||||
}
|
||||
await state?.imageUploadManager?.uploadImageAndApply(file, tags, targetKey, noBlur, feature, { ignoreGPS })
|
||||
await state?.imageUploadManager?.uploadImageAndApply(
|
||||
file,
|
||||
tags,
|
||||
targetKey,
|
||||
noBlur,
|
||||
feature,
|
||||
{ ignoreGPS }
|
||||
)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
state.reportError(e, "Could not upload image")
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@
|
|||
*/
|
||||
function getCount(input: Store<string[]>): Store<number> {
|
||||
if (featureId == "*") {
|
||||
return input.map(inp => inp.length)
|
||||
return input.map((inp) => inp.length)
|
||||
}
|
||||
return input.map(success => success.filter(item => item === featureId).length)
|
||||
return input.map((success) => success.filter((item) => item === featureId).length)
|
||||
}
|
||||
|
||||
let successfull = getCount(state.imageUploadManager.successfull)
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
const t = Translations.t.image
|
||||
const debugging = state.featureSwitches.featureSwitchIsDebugging
|
||||
let dismissed = 0
|
||||
failed.addCallbackAndRun(failed => {
|
||||
failed.addCallbackAndRun((failed) => {
|
||||
dismissed = Math.min(failed, dismissed)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
{#if $pending - $failed === 1}
|
||||
<Tr t={t.upload.one.uploading} />
|
||||
{:else if $pending - $failed > 1}
|
||||
<Tr t={t.upload.multiple.uploading.Subs({count: $pending})} />
|
||||
<Tr t={t.upload.multiple.uploading.Subs({ count: $pending })} />
|
||||
{/if}
|
||||
</Loading>
|
||||
</div>
|
||||
|
|
@ -70,6 +70,6 @@
|
|||
{#if $successfull === 1}
|
||||
<Tr cls="thanks" t={t.upload.one.done} />
|
||||
{:else if $successfull > 1}
|
||||
<Tr cls="thanks" t={t.upload.multiple.done.Subs({count: $successfull})} />
|
||||
<Tr cls="thanks" t={t.upload.multiple.done.Subs({ count: $successfull })} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -4,38 +4,37 @@ import { Feature, Geometry, Point } from "geojson"
|
|||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import { HotspotProperties, PanoramaView } from "../../Logic/ImageProviders/ImageProvider"
|
||||
|
||||
|
||||
export class PhotoSphereViewerWrapper {
|
||||
|
||||
private imageInfo: Feature<Point, PanoramaView>
|
||||
private readonly viewer: Pannellum.Viewer
|
||||
private nearbyFeatures: Feature<Geometry, HotspotProperties>[] = []
|
||||
|
||||
constructor(container: HTMLElement, imageInfo: Feature<Point, PanoramaView>, nearbyFeatures?: Feature<Geometry, HotspotProperties>[]) {
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
imageInfo: Feature<Point, PanoramaView>,
|
||||
nearbyFeatures?: Feature<Geometry, HotspotProperties>[]
|
||||
) {
|
||||
this.imageInfo = imageInfo
|
||||
this.viewer = pannellum.viewer(container,
|
||||
<any>{
|
||||
default: {
|
||||
firstScene: imageInfo.properties.url,
|
||||
sceneFadeDuration: 250
|
||||
this.viewer = pannellum.viewer(container, <any>{
|
||||
default: {
|
||||
firstScene: imageInfo.properties.url,
|
||||
sceneFadeDuration: 250,
|
||||
},
|
||||
scenes: {
|
||||
[imageInfo.properties.url]: {
|
||||
type: "equirectangular",
|
||||
hfov: 110,
|
||||
panorama: imageInfo.properties.url,
|
||||
autoLoad: true,
|
||||
hotSpots: [],
|
||||
sceneFadeDuration: 250,
|
||||
compass: true,
|
||||
showControls: false,
|
||||
northOffset: imageInfo.properties.northOffset,
|
||||
horizonPitch: imageInfo.properties.pitchOffset,
|
||||
},
|
||||
scenes: {
|
||||
[imageInfo.properties.url]:
|
||||
{
|
||||
type: "equirectangular",
|
||||
hfov: 110,
|
||||
panorama: imageInfo.properties.url,
|
||||
autoLoad: true,
|
||||
hotSpots: [],
|
||||
sceneFadeDuration: 250,
|
||||
compass: true,
|
||||
showControls: false,
|
||||
northOffset: imageInfo.properties.northOffset,
|
||||
horizonPitch: imageInfo.properties.pitchOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
this.setNearbyFeatures(nearbyFeatures)
|
||||
}
|
||||
|
|
@ -43,12 +42,13 @@ export class PhotoSphereViewerWrapper {
|
|||
public calculatePitch(feature: Feature): number {
|
||||
const coors = this.imageInfo.geometry.coordinates
|
||||
const distance = GeoOperations.distanceBetween(
|
||||
<[number, number]>coors, GeoOperations.centerpointCoordinates(feature)
|
||||
<[number, number]>coors,
|
||||
GeoOperations.centerpointCoordinates(feature)
|
||||
)
|
||||
|
||||
// In: -pi/2 up to pi/2
|
||||
const alpha = Math.atan(distance / 4) // in radians
|
||||
const degrees = alpha * 360 / (2 * Math.PI)
|
||||
const degrees = (alpha * 360) / (2 * Math.PI)
|
||||
return -degrees
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ export class PhotoSphereViewerWrapper {
|
|||
this.viewer.addScene(imageInfo.properties.url, <any>{
|
||||
panorama: imageInfo.properties.url,
|
||||
northOffset: imageInfo.properties.northOffset,
|
||||
type: "equirectangular"
|
||||
type: "equirectangular",
|
||||
})
|
||||
|
||||
this.viewer.loadScene(imageInfo.properties.url, 0, imageInfo.properties.northOffset)
|
||||
|
|
@ -70,7 +70,8 @@ export class PhotoSphereViewerWrapper {
|
|||
}
|
||||
|
||||
private clearHotspots() {
|
||||
const hotspots = this.viewer.getConfig()["scenes"][this.imageInfo.properties.url].hotSpots ?? []
|
||||
const hotspots =
|
||||
this.viewer.getConfig()["scenes"][this.imageInfo.properties.url].hotSpots ?? []
|
||||
for (const hotspot of hotspots) {
|
||||
this.viewer.removeHotSpot(hotspot?.id, this.imageInfo.properties.url)
|
||||
}
|
||||
|
|
@ -95,20 +96,21 @@ export class PhotoSphereViewerWrapper {
|
|||
} else if (!isNaN(f.properties.pitch)) {
|
||||
pitch = f.properties.pitch
|
||||
}
|
||||
this.viewer.addHotSpot({
|
||||
type: f.properties.gotoPanorama !== undefined ? "scene" : "info",
|
||||
yaw: (yaw - northOffs) % 360,
|
||||
pitch,
|
||||
text: f.properties.name,
|
||||
clickHandlerFunc: () => {
|
||||
this.setPanorama(f.properties.gotoPanorama)
|
||||
}
|
||||
}, this.imageInfo.properties.url)
|
||||
this.viewer.addHotSpot(
|
||||
{
|
||||
type: f.properties.gotoPanorama !== undefined ? "scene" : "info",
|
||||
yaw: (yaw - northOffs) % 360,
|
||||
pitch,
|
||||
text: f.properties.name,
|
||||
clickHandlerFunc: () => {
|
||||
this.setPanorama(f.properties.gotoPanorama)
|
||||
},
|
||||
},
|
||||
this.imageInfo.properties.url
|
||||
)
|
||||
if (f.properties.focus) {
|
||||
this.viewer.setYaw(yaw - northOffs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
|
||||
/**
|
||||
* Used to quickly calculate a distance by dragging a map (and selecting start- and endpoints)
|
||||
*/
|
||||
|
|
@ -24,71 +23,77 @@
|
|||
|
||||
export let value: UIEventSource<number>
|
||||
export let feature: Feature
|
||||
export let args: { background?: string, zoom?: number }
|
||||
export let args: { background?: string; zoom?: number }
|
||||
export let state: ThemeViewState = undefined
|
||||
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
||||
|
||||
let center = GeoOperations.centerpointCoordinates(feature)
|
||||
export let initialCoordinate: { lon: number, lat: number } = { lon: center[0], lat: center[1] }
|
||||
let mapLocation: UIEventSource<{ lon: number, lat: number }> = new UIEventSource(initialCoordinate)
|
||||
export let initialCoordinate: { lon: number; lat: number } = { lon: center[0], lat: center[1] }
|
||||
let mapLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(
|
||||
initialCoordinate
|
||||
)
|
||||
let bg = args?.background
|
||||
let rasterLayer = state?.mapProperties.rasterLayer
|
||||
if (bg !== undefined) {
|
||||
if (eliCategory.indexOf(bg) >= 0) {
|
||||
const availableLayers = state.availableLayers.store.data
|
||||
const startLayer: RasterLayerPolygon = RasterLayerUtils.SelectBestLayerAccordingTo(availableLayers, bg)
|
||||
const startLayer: RasterLayerPolygon = RasterLayerUtils.SelectBestLayerAccordingTo(
|
||||
availableLayers,
|
||||
bg
|
||||
)
|
||||
rasterLayer = new UIEventSource(startLayer)
|
||||
state?.mapProperties.rasterLayer.addCallbackD(layer => rasterLayer.set(layer))
|
||||
state?.mapProperties.rasterLayer.addCallbackD((layer) => rasterLayer.set(layer))
|
||||
}
|
||||
|
||||
}
|
||||
let mapProperties: Partial<MapProperties> = {
|
||||
rasterLayer: rasterLayer,
|
||||
location: mapLocation,
|
||||
zoom: new UIEventSource(args?.zoom ?? 18)
|
||||
zoom: new UIEventSource(args?.zoom ?? 18),
|
||||
}
|
||||
|
||||
let start: UIEventSource<{ lon: number, lat: number }> = new UIEventSource(undefined)
|
||||
let start: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined)
|
||||
|
||||
function selectStart() {
|
||||
start.set(mapLocation.data)
|
||||
}
|
||||
|
||||
let lengthFeature: Store<Feature[]> = start.map(start => {
|
||||
if (!start) {
|
||||
return []
|
||||
}
|
||||
// A bit of a double task: calculate the actual value _and_ the map rendering
|
||||
const end = mapLocation.data
|
||||
const distance = GeoOperations.distanceBetween([start.lon, start.lat], [end.lon, end.lat])
|
||||
value.set(distance.toFixed(2))
|
||||
|
||||
|
||||
return <Feature[]>[
|
||||
|
||||
{
|
||||
type: "Feature",
|
||||
properties: {
|
||||
id: "distance_line_" + distance,
|
||||
distance: "" + distance
|
||||
},
|
||||
geometry: {
|
||||
type: "LineString",
|
||||
coordinates: [[start.lon, start.lat], [end.lon, end.lat]]
|
||||
}
|
||||
let lengthFeature: Store<Feature[]> = start.map(
|
||||
(start) => {
|
||||
if (!start) {
|
||||
return []
|
||||
}
|
||||
]
|
||||
// A bit of a double task: calculate the actual value _and_ the map rendering
|
||||
const end = mapLocation.data
|
||||
const distance = GeoOperations.distanceBetween([start.lon, start.lat], [end.lon, end.lat])
|
||||
value.set(distance.toFixed(2))
|
||||
|
||||
}, [mapLocation])
|
||||
return <Feature[]>[
|
||||
{
|
||||
type: "Feature",
|
||||
properties: {
|
||||
id: "distance_line_" + distance,
|
||||
distance: "" + distance,
|
||||
},
|
||||
geometry: {
|
||||
type: "LineString",
|
||||
coordinates: [
|
||||
[start.lon, start.lat],
|
||||
[end.lon, end.lat],
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
[mapLocation]
|
||||
)
|
||||
|
||||
new ShowDataLayer(map, {
|
||||
layer: new LayerConfig(conflation),
|
||||
features: new StaticFeatureSource(lengthFeature)
|
||||
features: new StaticFeatureSource(lengthFeature),
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class="relative w-full h-64">
|
||||
<div class="relative h-64 w-full">
|
||||
<LocationInput value={mapLocation} {mapProperties} {map} />
|
||||
<div class="absolute bottom-0 left-0 p-4">
|
||||
<OpenBackgroundSelectorButton {state} {map} />
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export default class Validators {
|
|||
"translation",
|
||||
"url",
|
||||
"velopark",
|
||||
"wikidata"
|
||||
"wikidata",
|
||||
] as const
|
||||
|
||||
public static readonly AllValidators: ReadonlyArray<Validator> = [
|
||||
|
|
@ -93,7 +93,7 @@ export default class Validators {
|
|||
new VeloparkValidator(),
|
||||
new NameSuggestionIndexValidator(),
|
||||
new CurrencyValidator(),
|
||||
new RegexValidator()
|
||||
new RegexValidator(),
|
||||
]
|
||||
|
||||
private static _byType = Validators._byTypeConstructor()
|
||||
|
|
|
|||
|
|
@ -6,17 +6,19 @@ export default class DistanceValidator extends Validator {
|
|||
private readonly docs: string = [
|
||||
"#### Helper-arguments",
|
||||
"Options are:",
|
||||
["````json",
|
||||
" \"background\": \"some_background_id or category, e.g. 'map'\"",
|
||||
" \"zoom\": 20 # initial zoom level of the map",
|
||||
[
|
||||
"````json",
|
||||
' "background": "some_background_id or category, e.g. \'map\'"',
|
||||
' "zoom": 20 # initial zoom level of the map',
|
||||
"}",
|
||||
"```"].join("\n")
|
||||
"```",
|
||||
].join("\n"),
|
||||
].join("\n\n")
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
"distance",
|
||||
"A geographical distance in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `[\"21\", \"map,photo\"]",
|
||||
'A geographical distance in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"]',
|
||||
"decimal"
|
||||
)
|
||||
}
|
||||
|
|
@ -35,14 +37,18 @@ export default class DistanceValidator extends Validator {
|
|||
}
|
||||
|
||||
const optionalKeys = ["background", "zoom"]
|
||||
const keys = Object.keys(args).filter(k => optionalKeys.indexOf(k) < 0)
|
||||
const keys = Object.keys(args).filter((k) => optionalKeys.indexOf(k) < 0)
|
||||
if (keys.length > 0) {
|
||||
return "Unknown key " + keys.join("; ") + "; use " + optionalKeys.join("; ") + " instead"
|
||||
return (
|
||||
"Unknown key " + keys.join("; ") + "; use " + optionalKeys.join("; ") + " instead"
|
||||
)
|
||||
}
|
||||
const bg = args["background"]
|
||||
if (bg && eliCategory.indexOf(bg) < 0) {
|
||||
return "The given background layer is not a recognized ELI-type. Perhaps you meant one of " +
|
||||
Utils.sortedByLevenshteinDistance(bg, eliCategory, x => x).slice(0, 5)
|
||||
return (
|
||||
"The given background layer is not a recognized ELI-type. Perhaps you meant one of " +
|
||||
Utils.sortedByLevenshteinDistance(bg, eliCategory, (x) => x).slice(0, 5)
|
||||
)
|
||||
}
|
||||
if (typeof args["zoom"] !== "number") {
|
||||
return "zoom must be a number, got a " + typeof args["zoom"]
|
||||
|
|
|
|||
|
|
@ -302,9 +302,7 @@
|
|||
</div>
|
||||
</NextButton>
|
||||
</div>
|
||||
<TagHint
|
||||
tags={selectedPreset.preset.tags}
|
||||
/>
|
||||
<TagHint tags={selectedPreset.preset.tags} />
|
||||
</TitledPanel>
|
||||
{:else if _globalFilter?.length > 0 && _globalFilter?.length > checkedOfGlobalFilters}
|
||||
<Tr t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.safetyCheck} cls="mx-12" />
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@
|
|||
|
||||
let selectedTags: UploadableTag[]
|
||||
let changedProperties = undefined
|
||||
$: changedProperties = TagUtils.changeAsProperties(And.construct(selectedTags)?.asChange(tags?.data ?? {}) ?? [])
|
||||
$: changedProperties = TagUtils.changeAsProperties(
|
||||
And.construct(selectedTags)?.asChange(tags?.data ?? {}) ?? []
|
||||
)
|
||||
let isHardDelete = undefined
|
||||
$: isHardDelete = changedProperties[DeleteConfig.deleteReasonKey] !== undefined
|
||||
|
||||
|
|
@ -58,7 +60,9 @@
|
|||
}
|
||||
currentState = "applying"
|
||||
let actionToTake: OsmChangeAction
|
||||
const changedProperties = TagUtils.changeAsProperties(And.construct(selectedTags)?.asChange(tags?.data ?? {}))
|
||||
const changedProperties = TagUtils.changeAsProperties(
|
||||
And.construct(selectedTags)?.asChange(tags?.data ?? {})
|
||||
)
|
||||
const deleteReason = changedProperties[DeleteConfig.deleteReasonKey]
|
||||
if (deleteReason) {
|
||||
let softDeletionTags: UploadableTag
|
||||
|
|
|
|||
|
|
@ -37,21 +37,19 @@
|
|||
|
||||
{#if tagRenderings.length > 0}
|
||||
<div class="mb-8">
|
||||
|
||||
<AccordionSingle>
|
||||
<div slot="header">
|
||||
{#if headerTr}
|
||||
<TagRenderingAnswer {tags} {layer} config={headerTr} {state} {selectedElement} />
|
||||
{:else}
|
||||
{header}
|
||||
{/if}
|
||||
</div>
|
||||
<div slot="header">
|
||||
{#if headerTr}
|
||||
<TagRenderingAnswer {tags} {layer} config={headerTr} {state} {selectedElement} />
|
||||
{:else}
|
||||
{header}
|
||||
{/if}
|
||||
</div>
|
||||
{#each tagRenderings as config (config.id)}
|
||||
{#if config.IsKnown($tags) && (config.condition === undefined || config.condition.matchesProperties($tags))}
|
||||
<TagRenderingEditableDynamic {tags} {config} {state} {selectedElement} {layer} />
|
||||
{/if}
|
||||
{/each}
|
||||
</AccordionSingle>
|
||||
{#if config.IsKnown($tags) && (config.condition === undefined || config.condition.matchesProperties($tags))}
|
||||
<TagRenderingEditableDynamic {tags} {config} {state} {selectedElement} {layer} />
|
||||
{/if}
|
||||
{/each}
|
||||
</AccordionSingle>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@
|
|||
}, 50)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $loginEnabled}
|
||||
<div
|
||||
bind:this={questionboxElem}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
if (config === undefined) {
|
||||
console.error("TagRenderingAnswer: Config is undefined")
|
||||
throw ("Config is undefined in tagRenderingAnswer")
|
||||
throw "Config is undefined in tagRenderingAnswer"
|
||||
}
|
||||
let trs: Store<{ then: Translation; icon?: string; iconClass?: string }[]> = tags.mapD((tags) =>
|
||||
Utils.NoNull(config?.GetRenderValues(tags))
|
||||
|
|
|
|||
|
|
@ -23,11 +23,21 @@
|
|||
export let getCountry = () => "?"
|
||||
|
||||
onMount(() => {
|
||||
console.log("Setting selected unit based on country", getCountry(), "and upstream value:", upstreamValue.data)
|
||||
console.log(
|
||||
"Setting selected unit based on country",
|
||||
getCountry(),
|
||||
"and upstream value:",
|
||||
upstreamValue.data
|
||||
)
|
||||
if (upstreamValue.data === undefined || upstreamValue.data === "") {
|
||||
// Init the selected unit
|
||||
let denomination: Denomination = unit.getDefaultDenomination(getCountry)
|
||||
console.log("Found denom", denomination.canonical, "available denominations are:", unit.denominations.map(denom => denom.canonical))
|
||||
console.log(
|
||||
"Found denom",
|
||||
denomination.canonical,
|
||||
"available denominations are:",
|
||||
unit.denominations.map((denom) => denom.canonical)
|
||||
)
|
||||
selectedUnit.setData(denomination.canonical)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,17 +16,16 @@
|
|||
export let state: SpecialVisualizationState
|
||||
let searchTerm = state.searchState.searchTerm
|
||||
let recentThemes = state.userRelatedState.recentlyVisitedThemes.value.map((themes) => {
|
||||
const recent = themes.filter((th) => th !== state.theme.id).slice(0, 6)
|
||||
const deduped: MinimalThemeInformation[] = []
|
||||
for (const theme of recent) {
|
||||
if (deduped.some(th => th.id === theme.id)) {
|
||||
continue
|
||||
}
|
||||
deduped.push(theme)
|
||||
const recent = themes.filter((th) => th !== state.theme.id).slice(0, 6)
|
||||
const deduped: MinimalThemeInformation[] = []
|
||||
for (const theme of recent) {
|
||||
if (deduped.some((th) => th.id === theme.id)) {
|
||||
continue
|
||||
}
|
||||
return deduped
|
||||
deduped.push(theme)
|
||||
}
|
||||
)
|
||||
return deduped
|
||||
})
|
||||
let themeResults = state.searchState.themeSuggestions
|
||||
|
||||
const t = Translations.t.general.search
|
||||
|
|
|
|||
|
|
@ -79,7 +79,13 @@ export class ImageVisualisations {
|
|||
const estimated = tags.mapD((tags) =>
|
||||
AllImageProviders.estimateNumberOfImages(tags, imagePrefixes)
|
||||
)
|
||||
return new SvelteUIElement(ImageCarousel, { state, tags, images, estimated, feature })
|
||||
return new SvelteUIElement(ImageCarousel, {
|
||||
state,
|
||||
tags,
|
||||
images,
|
||||
estimated,
|
||||
feature,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { SpecialVisualization, SpecialVisualizationState, SpecialVisualizationSvelte } from "../SpecialVisualization"
|
||||
import {
|
||||
SpecialVisualization,
|
||||
SpecialVisualizationState,
|
||||
SpecialVisualizationSvelte,
|
||||
} from "../SpecialVisualization"
|
||||
import Constants from "../../Models/Constants"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Feature } from "geojson"
|
||||
|
|
|
|||
|
|
@ -80,23 +80,22 @@ export class SettingsVisualisations {
|
|||
group: "settings",
|
||||
docs: "Shows the current state of storage",
|
||||
args: [],
|
||||
constr: function(state: SpecialVisualizationState): SvelteUIElement {
|
||||
constr: function (state: SpecialVisualizationState): SvelteUIElement {
|
||||
const data = {}
|
||||
for (const key in localStorage) {
|
||||
data[key] = localStorage[key]
|
||||
}
|
||||
const tags = new UIEventSource(data)
|
||||
|
||||
navigator.storage.estimate().then(estimate => {
|
||||
navigator.storage.estimate().then((estimate) => {
|
||||
data["__usage:current:bytes"] = estimate.usage
|
||||
data["__usage:current:human"] = Utils.toHumanByteSize(estimate.usage)
|
||||
data["__usage:quota:bytes"] = estimate.quota
|
||||
data["__usage:quota:human"] = Utils.toHumanByteSize(estimate.quota)
|
||||
tags.ping()
|
||||
|
||||
})
|
||||
return new SvelteUIElement(AllTagsPanel, { state, tags })
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
funcName: "clear_caches",
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ class QuestionViz implements SpecialVisualizationSvelte {
|
|||
},
|
||||
{
|
||||
name: "blacklisted-labels",
|
||||
doc: "One or more ';'-separated labels of questions which should _not_ be included. Note that the questionbox which is added by default will blacklist 'hidden'"
|
||||
doc: "One or more ';'-separated labels of questions which should _not_ be included. Note that the questionbox which is added by default will blacklist 'hidden'",
|
||||
},
|
||||
{
|
||||
name: "show_all",
|
||||
default: "user-preference",
|
||||
doc: "Either `no`, `yes` or `user-preference`. Indicates if all questions should be shown at once"
|
||||
}
|
||||
doc: "Either `no`, `yes` or `user-preference`. Indicates if all questions should be shown at once",
|
||||
},
|
||||
]
|
||||
svelteBased = true
|
||||
group: "default"
|
||||
|
|
@ -69,7 +69,7 @@ class QuestionViz implements SpecialVisualizationSvelte {
|
|||
state,
|
||||
onlyForLabels: labels,
|
||||
notForLabels: blacklist,
|
||||
showAllQuestionsAtOnce
|
||||
showAllQuestionsAtOnce,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
{
|
||||
name: "key",
|
||||
doc: "The attribute-name containing the link",
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
|
||||
constr(
|
||||
|
|
@ -35,7 +35,7 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
): BaseUIElement {
|
||||
const key = argument[0]
|
||||
return new SvelteUIElement(FediverseLink, { key, tags, state })
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
funcName: "wikipedia",
|
||||
|
|
@ -45,8 +45,8 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
{
|
||||
name: "keyToShowWikipediaFor",
|
||||
doc: "Use the wikidata entry from this key to show the wikipedia article for. Multiple keys can be given (separated by ';'), in which case the first matching value is used",
|
||||
defaultValue: "wikidata;wikipedia"
|
||||
}
|
||||
defaultValue: "wikidata;wikipedia",
|
||||
},
|
||||
],
|
||||
needsUrls: [...Wikidata.neededUrls, ...Wikipedia.neededUrls],
|
||||
|
||||
|
|
@ -59,9 +59,9 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
return tags[key]?.split(";")?.map((id) => id.trim()) ?? []
|
||||
})
|
||||
return new SvelteUIElement(WikipediaPanel, {
|
||||
wikiIds
|
||||
wikiIds,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
funcName: "wikidata_label",
|
||||
|
|
@ -72,8 +72,8 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
{
|
||||
name: "keyToShowWikidataFor",
|
||||
doc: "Use the wikidata entry from this key to show the label",
|
||||
defaultValue: "wikidata"
|
||||
}
|
||||
defaultValue: "wikidata",
|
||||
},
|
||||
],
|
||||
needsUrls: Wikidata.neededUrls,
|
||||
example:
|
||||
|
|
@ -87,7 +87,7 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
)
|
||||
return wikidataIds?.[0]
|
||||
})
|
||||
const entry = id.bind(id => Wikidata.LoadWikidataEntry(id))
|
||||
const entry = id.bind((id) => Wikidata.LoadWikidataEntry(id))
|
||||
|
||||
return new VariableUiElement(
|
||||
entry.map((e) => {
|
||||
|
|
@ -96,8 +96,9 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
}
|
||||
const response = <WikidataResponse>e["success"]
|
||||
return Translation.fromMap(response.labels)
|
||||
}))
|
||||
}
|
||||
})
|
||||
)
|
||||
},
|
||||
},
|
||||
new MapillaryLinkVis(),
|
||||
{
|
||||
|
|
@ -108,29 +109,29 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
{
|
||||
name: "to",
|
||||
doc: "Who to send the email to?",
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "subject",
|
||||
doc: "The subject of the email",
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "body",
|
||||
doc: "The text in the email",
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "button_text",
|
||||
doc: "The text shown on the button in the UI",
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
|
||||
constr(__, tags, args) {
|
||||
return new SvelteUIElement(SendEmail, { args, tags })
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
funcName: "link",
|
||||
|
|
@ -140,29 +141,29 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
{
|
||||
name: "text",
|
||||
doc: "Text to be shown",
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "href",
|
||||
doc: "The URL to link to. Note that this will be URI-encoded before ",
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "class",
|
||||
doc: "CSS-classes to add to the element"
|
||||
doc: "CSS-classes to add to the element",
|
||||
},
|
||||
{
|
||||
name: "download",
|
||||
doc: "Expects a string which denotes the filename to download the contents of `href` into. If set, this link will act as a download-button."
|
||||
doc: "Expects a string which denotes the filename to download the contents of `href` into. If set, this link will act as a download-button.",
|
||||
},
|
||||
{
|
||||
name: "arialabel",
|
||||
doc: "If set, this text will be used as aria-label"
|
||||
doc: "If set, this text will be used as aria-label",
|
||||
},
|
||||
{
|
||||
name: "icon",
|
||||
doc: "If set, show this icon next to the link. You might want to combine this with `class: button`"
|
||||
}
|
||||
doc: "If set, show this icon next to the link. You might want to combine this with `class: button`",
|
||||
},
|
||||
],
|
||||
|
||||
constr(
|
||||
|
|
@ -184,10 +185,10 @@ export class WebAndCommunicationSpecialVisualisations {
|
|||
download: tagSource.map((tags) => Utils.SubstituteKeys(download, tags)),
|
||||
ariaLabel: tagSource.map((tags) => Utils.SubstituteKeys(ariaLabel, tags)),
|
||||
newTab: new ImmutableStore(newTab),
|
||||
icon: tagSource.map((tags) => Utils.SubstituteKeys(icon, tags))
|
||||
icon: tagSource.map((tags) => Utils.SubstituteKeys(icon, tags)),
|
||||
}).setSpan()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { FixedUiElement } from "./Base/FixedUiElement"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
import { default as FeatureTitle } from "./Popup/Title.svelte"
|
||||
import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization"
|
||||
import {
|
||||
RenderingSpecification,
|
||||
SpecialVisualization,
|
||||
SpecialVisualizationState,
|
||||
} from "./SpecialVisualization"
|
||||
import { HistogramViz } from "./Popup/HistogramViz"
|
||||
import { UploadToOsmViz } from "./Popup/UploadToOsmViz"
|
||||
import { MultiApplyViz } from "./Popup/MultiApplyViz"
|
||||
|
|
@ -36,11 +40,8 @@ import { UISpecialVisualisations } from "./SpecialVisualisations/UISpecialVisual
|
|||
import { SettingsVisualisations } from "./SpecialVisualisations/SettingsVisualisations"
|
||||
import { ReviewSpecialVisualisations } from "./SpecialVisualisations/ReviewSpecialVisualisations"
|
||||
import { DataImportSpecialVisualisations } from "./SpecialVisualisations/DataImportSpecialVisualisations"
|
||||
import TagrenderingManipulationSpecialVisualisations
|
||||
from "./SpecialVisualisations/TagrenderingManipulationSpecialVisualisations"
|
||||
import {
|
||||
WebAndCommunicationSpecialVisualisations
|
||||
} from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations"
|
||||
import TagrenderingManipulationSpecialVisualisations from "./SpecialVisualisations/TagrenderingManipulationSpecialVisualisations"
|
||||
import { WebAndCommunicationSpecialVisualisations } from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations"
|
||||
import ClearGPSHistory from "./BigComponents/ClearGPSHistory.svelte"
|
||||
import AllFeaturesStatistics from "./Statistics/AllFeaturesStatistics.svelte"
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@
|
|||
export let layer: LayerConfig
|
||||
export let state: ThemeViewState
|
||||
let bbox = state.mapProperties.bounds
|
||||
let elements: Store<Feature[]> = bbox.mapD(bbox => state.perLayer.get(layer.id).GetFeaturesWithin(bbox))
|
||||
let elements: Store<Feature[]> = bbox.mapD((bbox) =>
|
||||
state.perLayer.get(layer.id).GetFeaturesWithin(bbox)
|
||||
)
|
||||
|
||||
let trs = layer.tagRenderings.filter((tr) => tr.question)
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@
|
|||
return "offline"
|
||||
}
|
||||
}),
|
||||
message: osmApi
|
||||
message: osmApi,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
}
|
||||
const files: string[] = s["success"]["allFiles"]
|
||||
return "Contains " + (files.length ?? "no") + " files"
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
{
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
return "degraded"
|
||||
}
|
||||
}),
|
||||
message: simpleMessage(testDownload(Constants.panoramax.url + "/api"))
|
||||
message: simpleMessage(testDownload(Constants.panoramax.url + "/api")),
|
||||
})
|
||||
}
|
||||
{
|
||||
|
|
@ -123,7 +123,7 @@
|
|||
return "degraded"
|
||||
}
|
||||
}),
|
||||
message: simpleMessage(testDownload(Constants.GeoIpServer + "/ip"))
|
||||
message: simpleMessage(testDownload(Constants.GeoIpServer + "/ip")),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +142,7 @@
|
|||
}
|
||||
return "degraded"
|
||||
}),
|
||||
message: simpleMessage(status)
|
||||
message: simpleMessage(status),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
}
|
||||
return "online"
|
||||
}),
|
||||
message: simpleMessage(status)
|
||||
message: simpleMessage(status),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +200,7 @@
|
|||
|
||||
const json = JSON.stringify(s["success"], null, " ")
|
||||
return "Database is " + Math.floor(timediffDays) + " days out of sync\n\n" + json
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +213,7 @@
|
|||
layer: "food",
|
||||
z: 14,
|
||||
x: 8848,
|
||||
y: 5828
|
||||
y: 5828,
|
||||
})
|
||||
)
|
||||
services.push({
|
||||
|
|
@ -224,7 +224,7 @@
|
|||
}
|
||||
return "online"
|
||||
}),
|
||||
message: new ImmutableStore("See SettingUpPSQL.md to fix")
|
||||
message: new ImmutableStore("See SettingUpPSQL.md to fix"),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -243,7 +243,7 @@
|
|||
}
|
||||
return "degraded"
|
||||
}),
|
||||
message: status.map((s) => JSON.stringify(s))
|
||||
message: status.map((s) => JSON.stringify(s)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +262,7 @@
|
|||
return "online"
|
||||
}
|
||||
return "degraded"
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +281,7 @@
|
|||
}
|
||||
return "degraded"
|
||||
}),
|
||||
message: simpleMessage(status)
|
||||
message: simpleMessage(status),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +307,7 @@
|
|||
|
||||
return "online"
|
||||
}),
|
||||
message: simpleMessage(status)
|
||||
message: simpleMessage(status),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -320,7 +320,7 @@
|
|||
return "online"
|
||||
}
|
||||
return "offline"
|
||||
})
|
||||
}),
|
||||
})
|
||||
|
||||
services.push({
|
||||
|
|
@ -331,35 +331,33 @@
|
|||
}
|
||||
// This code will break in the future. Time to blame past me!
|
||||
const response = JSON.parse(r["error"].substring("other error: , ".length))
|
||||
if (response.message === "\"images\" is required") {
|
||||
if (response.message === '"images" is required') {
|
||||
// Actual expected behaviour
|
||||
return "online"
|
||||
}
|
||||
console.log("R", response)
|
||||
return "offline"
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
{
|
||||
services.push({
|
||||
name: "Version Control Server (Forgéjo)",
|
||||
status: testDownload("https://source.mapcomplete.org", true).mapD(r => {
|
||||
status: testDownload("https://source.mapcomplete.org", true).mapD((r) => {
|
||||
if (r["success"]) {
|
||||
return "online"
|
||||
}
|
||||
return "offline"
|
||||
|
||||
})
|
||||
}),
|
||||
})
|
||||
services.push({
|
||||
name: "Translation service (Weblate)",
|
||||
status: testDownload("https://translate.mapcomplete.org", true).mapD(r => {
|
||||
status: testDownload("https://translate.mapcomplete.org", true).mapD((r) => {
|
||||
if (r["success"]) {
|
||||
return "online"
|
||||
}
|
||||
return "offline"
|
||||
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -433,7 +431,6 @@
|
|||
let now = Math.round(new Date().getTime() / 1000)
|
||||
let twoDaysAgo = now - 2 * 24 * 60 * 60
|
||||
let lastHour = now - 60 * 60
|
||||
|
||||
</script>
|
||||
|
||||
<h1>MapComplete status indicators</h1>
|
||||
|
|
@ -457,16 +454,21 @@
|
|||
|
||||
<h3>Panoramax & OSM.fr Blurring service</h3>
|
||||
<a href="https://status.thibaultmol.link/status/panoramax">Panoramax.MapComplete.org status page</a>
|
||||
<a href="https://munin.openstreetmap.fr/osm37.openstreetmap.fr/blur.vm.openstreetmap.fr/index.html#system"
|
||||
target="_blank" rel="noopener">
|
||||
<a
|
||||
href="https://munin.openstreetmap.fr/osm37.openstreetmap.fr/blur.vm.openstreetmap.fr/index.html#system"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
See more statistics for the blurring service
|
||||
</a>
|
||||
<img
|
||||
style="width: 80rem"
|
||||
src={`https://munin.openstreetmap.fr/munin-cgi/munin-cgi-graph/osm37.openstreetmap.fr/blur.vm.openstreetmap.fr/nvidia_gpu_power-pinpoint=${twoDaysAgo},${now}.png?&lower_limit=&upper_limit=&size_x=800&size_y=400`} />
|
||||
src={`https://munin.openstreetmap.fr/munin-cgi/munin-cgi-graph/osm37.openstreetmap.fr/blur.vm.openstreetmap.fr/nvidia_gpu_power-pinpoint=${twoDaysAgo},${now}.png?&lower_limit=&upper_limit=&size_x=800&size_y=400`}
|
||||
/>
|
||||
<img
|
||||
style="width: 80rem"
|
||||
src={`https://munin.openstreetmap.fr/munin-cgi/munin-cgi-graph/osm37.openstreetmap.fr/blur.vm.openstreetmap.fr/nvidia_gpu_power-pinpoint=${lastHour},${now}.png?&lower_limit=&upper_limit=&size_x=800&size_y=400`} />
|
||||
src={`https://munin.openstreetmap.fr/munin-cgi/munin-cgi-graph/osm37.openstreetmap.fr/blur.vm.openstreetmap.fr/nvidia_gpu_power-pinpoint=${lastHour},${now}.png?&lower_limit=&upper_limit=&size_x=800&size_y=400`}
|
||||
/>
|
||||
<button
|
||||
on:click={() => {
|
||||
fetch(Constants.ErrorReportServer, {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let imageInfo
|
||||
|
|
@ -8,32 +7,25 @@
|
|||
|
||||
let container: HTMLElement
|
||||
|
||||
|
||||
onMount(() => {
|
||||
console.log("Creating viewer...")
|
||||
const features = [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": { "name": "trap" },
|
||||
"geometry": {
|
||||
"coordinates": [
|
||||
3.742395038713312,
|
||||
51.05237592785801
|
||||
],
|
||||
"type": "Point"
|
||||
}
|
||||
}
|
||||
type: "Feature",
|
||||
properties: { name: "trap" },
|
||||
geometry: {
|
||||
coordinates: [3.742395038713312, 51.05237592785801],
|
||||
type: "Point",
|
||||
},
|
||||
},
|
||||
]
|
||||
const viewer = new PhotoSphereViewerWrapper(container, imageInfo, features)
|
||||
|
||||
|
||||
// console.log(panorama, container)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="./node_modules/pannellum/build/pannellum.css">
|
||||
<link rel="stylesheet" href="./node_modules/pannellum/build/pannellum.css" />
|
||||
</head>
|
||||
<div bind:this={container} class="h-screen w-screen border" style="height: 500px"></div>
|
||||
|
||||
<div bind:this={container} class="h-screen w-screen border" style="height: 500px" />
|
||||
|
|
|
|||
|
|
@ -165,7 +165,6 @@ export class Translation extends BaseUIElement {
|
|||
* Which language will be effectively used for the given language of choice?
|
||||
*/
|
||||
public actualLanguage(language: string): "*" | string | undefined {
|
||||
|
||||
const txt = this.translations[language]
|
||||
if (txt !== undefined) {
|
||||
return language
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue