153 lines
6.3 KiB
TypeScript
153 lines
6.3 KiB
TypeScript
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
|
import { DefaultGuiState } from "../DefaultGuiState"
|
|
import BaseUIElement from "../BaseUIElement"
|
|
import Translations from "../i18n/Translations"
|
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
|
import NearbyImages, { NearbyImageOptions, P4CPicture, SelectOneNearbyImage } from "./NearbyImages"
|
|
import { SubstitutedTranslation } from "../SubstitutedTranslation"
|
|
import { Tag } from "../../Logic/Tags/Tag"
|
|
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
|
import { And } from "../../Logic/Tags/And"
|
|
import { SaveButton } from "./SaveButton"
|
|
import Lazy from "../Base/Lazy"
|
|
import { CheckBox } from "../Input/Checkboxes"
|
|
import Slider from "../Input/Slider"
|
|
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
|
import Combine from "../Base/Combine"
|
|
import { VariableUiElement } from "../Base/VariableUIElement"
|
|
import Toggle from "../Input/Toggle"
|
|
import Title from "../Base/Title"
|
|
import { MapillaryLinkVis } from "./MapillaryLinkVis"
|
|
import { SpecialVisualization } from "../SpecialVisualization"
|
|
|
|
export class NearbyImageVis implements SpecialVisualization {
|
|
args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [
|
|
{
|
|
name: "mode",
|
|
defaultValue: "expandable",
|
|
doc: "Indicates how this component is initialized. Options are: \n\n- `open`: always show and load the pictures\n- `collapsable`: show the pictures, but a user can collapse them\n- `expandable`: shown by default; but a user can collapse them.",
|
|
},
|
|
{
|
|
name: "mapillary",
|
|
defaultValue: "true",
|
|
doc: "If 'true', includes a link to mapillary on this location.",
|
|
},
|
|
]
|
|
docs =
|
|
"A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature"
|
|
funcName = "nearby_images"
|
|
|
|
constr(
|
|
state: FeaturePipelineState,
|
|
tagSource: UIEventSource<any>,
|
|
args: string[],
|
|
guistate: DefaultGuiState
|
|
): BaseUIElement {
|
|
const t = Translations.t.image.nearbyPictures
|
|
const mode: "open" | "expandable" | "collapsable" = <any>args[0]
|
|
const feature = state.allElements.ContainingFeatures.get(tagSource.data.id)
|
|
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
|
const id: string = tagSource.data["id"]
|
|
const canBeEdited: boolean = !!id?.match("(node|way|relation)/-?[0-9]+")
|
|
const selectedImage = new UIEventSource<P4CPicture>(undefined)
|
|
|
|
let saveButton: BaseUIElement = undefined
|
|
if (canBeEdited) {
|
|
const confirmText: BaseUIElement = new SubstitutedTranslation(
|
|
t.confirm,
|
|
tagSource,
|
|
state
|
|
)
|
|
|
|
const onSave = async () => {
|
|
console.log("Selected a picture...", selectedImage.data)
|
|
const osmTags = selectedImage.data.osmTags
|
|
const tags: Tag[] = []
|
|
for (const key in osmTags) {
|
|
tags.push(new Tag(key, osmTags[key]))
|
|
}
|
|
await state?.changes?.applyAction(
|
|
new ChangeTagAction(id, new And(tags), tagSource.data, {
|
|
theme: state?.layoutToUse.id,
|
|
changeType: "link-image",
|
|
})
|
|
)
|
|
}
|
|
saveButton = new SaveButton(
|
|
selectedImage,
|
|
state.osmConnection,
|
|
confirmText,
|
|
t.noImageSelected
|
|
)
|
|
.onClick(onSave)
|
|
.SetClass("flex justify-end")
|
|
}
|
|
|
|
const nearby = new Lazy(() => {
|
|
const towardsCenter = new CheckBox(t.onlyTowards, false)
|
|
|
|
const maxSearchRadius = 100
|
|
const stepSize = 10
|
|
const defaultValue = Math.floor(maxSearchRadius / (2 * stepSize)) * stepSize
|
|
const fromOsmPreferences = state?.osmConnection
|
|
?.GetPreference("nearby-images-radius", "" + defaultValue)
|
|
.sync(
|
|
(s) => Number(s),
|
|
[],
|
|
(i) => "" + i
|
|
)
|
|
const radiusValue = new UIEventSource(fromOsmPreferences.data)
|
|
radiusValue.addCallbackAndRunD((v) => fromOsmPreferences.setData(v))
|
|
|
|
const radius = new Slider(stepSize, maxSearchRadius, {
|
|
value: radiusValue,
|
|
step: 10,
|
|
})
|
|
const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource)
|
|
const options: NearbyImageOptions & { value } = {
|
|
lon,
|
|
lat,
|
|
searchRadius: maxSearchRadius,
|
|
shownRadius: radius.GetValue(),
|
|
value: selectedImage,
|
|
blacklist: alreadyInTheImage,
|
|
towardscenter: towardsCenter.GetValue(),
|
|
maxDaysOld: 365 * 3,
|
|
}
|
|
const slideshow = canBeEdited
|
|
? new SelectOneNearbyImage(options, state)
|
|
: new NearbyImages(options, state)
|
|
const controls = new Combine([
|
|
towardsCenter,
|
|
new Combine([
|
|
new VariableUiElement(
|
|
radius.GetValue().map((radius) => t.withinRadius.Subs({ radius }))
|
|
),
|
|
radius,
|
|
]).SetClass("flex justify-between"),
|
|
]).SetClass("flex flex-col")
|
|
return new Combine([
|
|
slideshow,
|
|
controls,
|
|
saveButton,
|
|
new MapillaryLinkVis().constr(state, tagSource, []).SetClass("mt-6"),
|
|
])
|
|
})
|
|
|
|
let withEdit: BaseUIElement = nearby
|
|
if (canBeEdited) {
|
|
withEdit = new Combine([t.hasMatchingPicture, nearby]).SetClass("flex flex-col")
|
|
}
|
|
|
|
if (mode === "open") {
|
|
return withEdit
|
|
}
|
|
const toggleState = new UIEventSource<boolean>(mode === "collapsable")
|
|
return new Toggle(
|
|
new Combine([new Title(t.title), withEdit]),
|
|
new Title(t.browseNearby).onClick(() => toggleState.setData(true)),
|
|
toggleState
|
|
)
|
|
}
|
|
}
|