forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			147 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
	
		
			6 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 radiusValue =
 | |
|                 state?.osmConnection?.GetPreference("nearby-images-radius", "300").sync(
 | |
|                     (s) => Number(s),
 | |
|                     [],
 | |
|                     (i) => "" + i
 | |
|                 ) ?? new UIEventSource(300)
 | |
| 
 | |
|             const radius = new Slider(25, 500, {
 | |
|                 value: radiusValue,
 | |
|                 step: 25,
 | |
|             })
 | |
|             const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource)
 | |
|             const options: NearbyImageOptions & { value } = {
 | |
|                 lon,
 | |
|                 lat,
 | |
|                 searchRadius: 500,
 | |
|                 shownRadius: radius.GetValue(),
 | |
|                 value: selectedImage,
 | |
|                 blacklist: alreadyInTheImage,
 | |
|                 towardscenter: towardsCenter.GetValue(),
 | |
|                 maxDaysOld: 365 * 5,
 | |
|             }
 | |
|             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
 | |
|         )
 | |
|     }
 | |
| }
 |