forked from MapComplete/MapComplete
Refactoring: add advanced buttons to open mapillary and to open other editors
This commit is contained in:
parent
1d543563d7
commit
12eb2a2d55
9 changed files with 152 additions and 143 deletions
|
@ -1,5 +0,0 @@
|
||||||
export default interface Loc {
|
|
||||||
lat: number
|
|
||||||
lon: number
|
|
||||||
zoom: number
|
|
||||||
}
|
|
|
@ -1,113 +1,28 @@
|
||||||
import Combine from "../Base/Combine"
|
import Combine from "../Base/Combine"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
import {Store} from "../../Logic/UIEventSource"
|
||||||
import { FixedUiElement } from "../Base/FixedUiElement"
|
import {FixedUiElement} from "../Base/FixedUiElement"
|
||||||
import licenses from "../../assets/generated/license_info.json"
|
import licenses from "../../assets/generated/license_info.json"
|
||||||
import SmallLicense from "../../Models/smallLicense"
|
import SmallLicense from "../../Models/smallLicense"
|
||||||
import { Utils } from "../../Utils"
|
import {Utils} from "../../Utils"
|
||||||
import Link from "../Base/Link"
|
import Link from "../Base/Link"
|
||||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
import {VariableUiElement} from "../Base/VariableUIElement"
|
||||||
import contributors from "../../assets/contributors.json"
|
import contributors from "../../assets/contributors.json"
|
||||||
import translators from "../../assets/translators.json"
|
import translators from "../../assets/translators.json"
|
||||||
import BaseUIElement from "../BaseUIElement"
|
import BaseUIElement from "../BaseUIElement"
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||||
import Title from "../Base/Title"
|
import Title from "../Base/Title"
|
||||||
import { SubtleButton } from "../Base/SubtleButton"
|
import {BBox} from "../../Logic/BBox"
|
||||||
import Svg from "../../Svg"
|
import {OsmConnection} from "../../Logic/Osm/OsmConnection"
|
||||||
import { BBox } from "../../Logic/BBox"
|
|
||||||
import Toggle from "../Input/Toggle"
|
|
||||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
|
||||||
import Constants from "../../Models/Constants"
|
import Constants from "../../Models/Constants"
|
||||||
import ContributorCount from "../../Logic/ContributorCount"
|
import ContributorCount from "../../Logic/ContributorCount"
|
||||||
import Img from "../Base/Img"
|
import Img from "../Base/Img"
|
||||||
import { TypedTranslation } from "../i18n/Translation"
|
import {TypedTranslation} from "../i18n/Translation"
|
||||||
import GeoIndexedStore from "../../Logic/FeatureSource/Actors/GeoIndexedStore"
|
import GeoIndexedStore from "../../Logic/FeatureSource/Actors/GeoIndexedStore"
|
||||||
import {RasterLayerPolygon} from "../../Models/RasterLayers";
|
import {RasterLayerPolygon} from "../../Models/RasterLayers";
|
||||||
|
|
||||||
export class OpenIdEditor extends VariableUiElement {
|
|
||||||
constructor(
|
|
||||||
mapProperties: { location: Store<{ lon: number; lat: number }>; zoom: Store<number> },
|
|
||||||
iconStyle?: string,
|
|
||||||
objectId?: string
|
|
||||||
) {
|
|
||||||
const t = Translations.t.general.attribution
|
|
||||||
super(
|
|
||||||
mapProperties.location.map(
|
|
||||||
(location) => {
|
|
||||||
let elementSelect = ""
|
|
||||||
if (objectId !== undefined) {
|
|
||||||
const parts = objectId.split("/")
|
|
||||||
const tp = parts[0]
|
|
||||||
if (
|
|
||||||
parts.length === 2 &&
|
|
||||||
!isNaN(Number(parts[1])) &&
|
|
||||||
(tp === "node" || tp === "way" || tp === "relation")
|
|
||||||
) {
|
|
||||||
elementSelect = "&" + tp + "=" + parts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const idLink = `https://www.openstreetmap.org/edit?editor=id${elementSelect}#map=${
|
|
||||||
mapProperties.zoom?.data ?? 0
|
|
||||||
}/${location?.lat ?? 0}/${location?.lon ?? 0}`
|
|
||||||
return new SubtleButton(Svg.pencil_svg().SetStyle(iconStyle), t.editId, {
|
|
||||||
url: idLink,
|
|
||||||
newTab: true,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[mapProperties.zoom]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OpenJosm extends Combine {
|
|
||||||
constructor(osmConnection: OsmConnection, bounds: Store<BBox>, iconStyle?: string) {
|
|
||||||
const t = Translations.t.general.attribution
|
|
||||||
|
|
||||||
const josmState = new UIEventSource<string>(undefined)
|
|
||||||
// Reset after 15s
|
|
||||||
josmState.stabilized(15000).addCallbackD((_) => josmState.setData(undefined))
|
|
||||||
|
|
||||||
const stateIndication = new VariableUiElement(
|
|
||||||
josmState.map((state) => {
|
|
||||||
if (state === undefined) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
state = state.toUpperCase()
|
|
||||||
if (state === "OK") {
|
|
||||||
return t.josmOpened.SetClass("thanks")
|
|
||||||
}
|
|
||||||
return t.josmNotOpened.SetClass("alert")
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const toggle = new Toggle(
|
|
||||||
new SubtleButton(Svg.josm_logo_svg().SetStyle(iconStyle), t.editJosm).onClick(() => {
|
|
||||||
const bbox = bounds.data
|
|
||||||
if (bbox === undefined) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const top = bbox.getNorth()
|
|
||||||
const bottom = bbox.getSouth()
|
|
||||||
const right = bbox.getEast()
|
|
||||||
const left = bbox.getWest()
|
|
||||||
const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}`
|
|
||||||
Utils.download(josmLink)
|
|
||||||
.then((answer) => josmState.setData(answer.replace(/\n/g, "").trim()))
|
|
||||||
.catch((_) => josmState.setData("ERROR"))
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
osmConnection.userDetails.map(
|
|
||||||
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
super([stateIndication, toggle])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attribution panel shown on mobile
|
* The attribution panel in the theme menu.
|
||||||
*/
|
*/
|
||||||
export default class CopyrightPanel extends Combine {
|
export default class CopyrightPanel extends Combine {
|
||||||
private static LicenseObject = CopyrightPanel.GenerateLicenses()
|
private static LicenseObject = CopyrightPanel.GenerateLicenses()
|
||||||
|
|
31
UI/BigComponents/MapillaryLink.svelte
Normal file
31
UI/BigComponents/MapillaryLink.svelte
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
import Translations from "../i18n/Translations"
|
||||||
|
import Svg from "../../Svg"
|
||||||
|
import {Store} from "../../Logic/UIEventSource";
|
||||||
|
import Tr from "../Base/Tr.svelte";
|
||||||
|
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||||
|
|
||||||
|
/*
|
||||||
|
A subtleButton which opens mapillary in a new tab at the current location
|
||||||
|
*/
|
||||||
|
|
||||||
|
export let mapProperties: {
|
||||||
|
readonly zoom: Store<number>,
|
||||||
|
readonly location: Store<{ lon: number, lat: number }>
|
||||||
|
}
|
||||||
|
let location = mapProperties.location
|
||||||
|
let zoom = mapProperties.zoom
|
||||||
|
let mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${
|
||||||
|
$location?.lat ?? 0
|
||||||
|
}&lng=${$location?.lon ?? 0}&z=${Math.max(($zoom ?? 2) - 1, 1)}`
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a class="flex button items-center" href={mapillaryLink} target="_blank">
|
||||||
|
<ToSvelte construct={() =>Svg.mapillary_black_svg().SetClass("w-12 h-12 m-2 mr-4 shrink-0")}/>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<Tr t={Translations.t.general.attribution.openMapillary}/>
|
||||||
|
<Tr cls="subtle" t={ Translations.t.general.attribution.mapillaryHelp}/>
|
||||||
|
</div>
|
||||||
|
</a>
|
|
@ -1,30 +0,0 @@
|
||||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
|
||||||
import { Store } from "../../Logic/UIEventSource"
|
|
||||||
import Loc from "../../Models/Loc"
|
|
||||||
import Translations from "../i18n/Translations"
|
|
||||||
import { SubtleButton } from "../Base/SubtleButton"
|
|
||||||
import Svg from "../../Svg"
|
|
||||||
import Combine from "../Base/Combine"
|
|
||||||
|
|
||||||
export class MapillaryLink extends VariableUiElement {
|
|
||||||
constructor(state: { readonly locationControl: Store<Loc> }, iconStyle?: string) {
|
|
||||||
const t = Translations.t.general.attribution
|
|
||||||
super(
|
|
||||||
state.locationControl.map((location) => {
|
|
||||||
const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${
|
|
||||||
location?.lat ?? 0
|
|
||||||
}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}`
|
|
||||||
return new SubtleButton(
|
|
||||||
Svg.mapillary_black_svg().SetStyle(iconStyle),
|
|
||||||
new Combine([t.openMapillary.SetClass("font-bold"), t.mapillaryHelp]).SetClass(
|
|
||||||
"flex flex-col link-no-underline"
|
|
||||||
),
|
|
||||||
{
|
|
||||||
url: mapillaryLink,
|
|
||||||
newTab: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
33
UI/BigComponents/OpenIdEditor.svelte
Normal file
33
UI/BigComponents/OpenIdEditor.svelte
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {Store} from "../../Logic/UIEventSource";
|
||||||
|
import {PencilIcon} from "@babeard/svelte-heroicons/solid";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
import Tr from "../Base/Tr.svelte";
|
||||||
|
|
||||||
|
export let mapProperties: { location: Store<{ lon: number; lat: number }>; zoom: Store<number> }
|
||||||
|
let location = mapProperties.location
|
||||||
|
let zoom = mapProperties.zoom
|
||||||
|
export let objectId: undefined | string = undefined
|
||||||
|
|
||||||
|
let elementSelect = ""
|
||||||
|
if (objectId !== undefined) {
|
||||||
|
const parts = objectId?.split("/")
|
||||||
|
const tp = parts[0]
|
||||||
|
if (
|
||||||
|
parts.length === 2 &&
|
||||||
|
!isNaN(Number(parts[1])) &&
|
||||||
|
(tp === "node" || tp === "way" || tp === "relation")
|
||||||
|
) {
|
||||||
|
elementSelect = "&" + tp + "=" + parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const idLink = `https://www.openstreetmap.org/edit?editor=id${elementSelect}#map=${
|
||||||
|
$zoom ?? 0
|
||||||
|
}/${$location?.lat ?? 0}/${$location?.lon ?? 0}`
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<a class="flex button items-center" target="_blank" href={idLink}>
|
||||||
|
<PencilIcon class="w-12 h-12 p-2 pr-4"/>
|
||||||
|
<Tr t={ Translations.t.general.attribution.editId}/>
|
||||||
|
</a>
|
57
UI/BigComponents/OpenJosm.ts
Normal file
57
UI/BigComponents/OpenJosm.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import Combine from "../Base/Combine";
|
||||||
|
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||||
|
import {Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||||
|
import {BBox} from "../../Logic/BBox";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
|
import Toggle from "../Input/Toggle";
|
||||||
|
import {SubtleButton} from "../Base/SubtleButton";
|
||||||
|
import Svg from "../../Svg";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
import Constants from "../../Models/Constants";
|
||||||
|
|
||||||
|
export class OpenJosm extends Combine {
|
||||||
|
constructor(osmConnection: OsmConnection, bounds: Store<BBox>, iconStyle?: string) {
|
||||||
|
const t = Translations.t.general.attribution
|
||||||
|
|
||||||
|
const josmState = new UIEventSource<string>(undefined)
|
||||||
|
// Reset after 15s
|
||||||
|
josmState.stabilized(15000).addCallbackD((_) => josmState.setData(undefined))
|
||||||
|
|
||||||
|
const stateIndication = new VariableUiElement(
|
||||||
|
josmState.map((state) => {
|
||||||
|
if (state === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
state = state.toUpperCase()
|
||||||
|
if (state === "OK") {
|
||||||
|
return t.josmOpened.SetClass("thanks")
|
||||||
|
}
|
||||||
|
return t.josmNotOpened.SetClass("alert")
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggle = new Toggle(
|
||||||
|
new SubtleButton(Svg.josm_logo_svg().SetStyle(iconStyle), t.editJosm).onClick(() => {
|
||||||
|
const bbox = bounds.data
|
||||||
|
if (bbox === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const top = bbox.getNorth()
|
||||||
|
const bottom = bbox.getSouth()
|
||||||
|
const right = bbox.getEast()
|
||||||
|
const left = bbox.getWest()
|
||||||
|
const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}`
|
||||||
|
Utils.download(josmLink)
|
||||||
|
.then((answer) => josmState.setData(answer.replace(/\n/g, "").trim()))
|
||||||
|
.catch((_) => josmState.setData("ERROR"))
|
||||||
|
}).SetClass("w-full"),
|
||||||
|
undefined,
|
||||||
|
osmConnection.userDetails.map(
|
||||||
|
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
super([stateIndication, toggle])
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
import {GeoOperations} from "../../Logic/GeoOperations"
|
||||||
import { MapillaryLink } from "../BigComponents/MapillaryLink"
|
import {ImmutableStore, UIEventSource} from "../../Logic/UIEventSource"
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
import {SpecialVisualization, SpecialVisualizationState} from "../SpecialVisualization"
|
||||||
import Loc from "../../Models/Loc"
|
import {Feature} from "geojson"
|
||||||
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
|
|
||||||
import { Feature } from "geojson"
|
|
||||||
import BaseUIElement from "../BaseUIElement"
|
import BaseUIElement from "../BaseUIElement"
|
||||||
|
import SvelteUIElement from "../Base/SvelteUIElement";
|
||||||
|
import MapillaryLink from "../BigComponents/MapillaryLink.svelte";
|
||||||
|
|
||||||
export class MapillaryLinkVis implements SpecialVisualization {
|
export class MapillaryLinkVis implements SpecialVisualization {
|
||||||
funcName = "mapillary_link"
|
funcName = "mapillary_link"
|
||||||
|
@ -28,12 +28,12 @@ export class MapillaryLinkVis implements SpecialVisualization {
|
||||||
if (isNaN(zoom)) {
|
if (isNaN(zoom)) {
|
||||||
zoom = 18
|
zoom = 18
|
||||||
}
|
}
|
||||||
return new MapillaryLink({
|
return new SvelteUIElement(MapillaryLink, {
|
||||||
locationControl: new UIEventSource<Loc>({
|
mapProperties: {
|
||||||
lat,
|
lat,
|
||||||
lon,
|
lon
|
||||||
zoom,
|
},
|
||||||
}),
|
zoom: new ImmutableStore(zoom)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"
|
||||||
import LiveQueryHandler from "../Logic/Web/LiveQueryHandler"
|
import LiveQueryHandler from "../Logic/Web/LiveQueryHandler"
|
||||||
import {SubtleButton} from "./Base/SubtleButton"
|
import {SubtleButton} from "./Base/SubtleButton"
|
||||||
import Svg from "../Svg"
|
import Svg from "../Svg"
|
||||||
import {OpenIdEditor, OpenJosm} from "./BigComponents/CopyrightPanel"
|
|
||||||
import Hash from "../Logic/Web/Hash"
|
import Hash from "../Logic/Web/Hash"
|
||||||
import NoteCommentElement from "./Popup/NoteCommentElement"
|
import NoteCommentElement from "./Popup/NoteCommentElement"
|
||||||
import ImgurUploader from "../Logic/ImageProviders/ImgurUploader"
|
import ImgurUploader from "../Logic/ImageProviders/ImgurUploader"
|
||||||
|
@ -74,6 +73,8 @@ import {PointImportButtonViz} from "./Popup/ImportButtons/PointImportButtonViz";
|
||||||
import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz";
|
import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz";
|
||||||
import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz";
|
import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz";
|
||||||
import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte";
|
import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte";
|
||||||
|
import {OpenJosm} from "./BigComponents/OpenJosm";
|
||||||
|
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte";
|
||||||
|
|
||||||
class NearbyImageVis implements SpecialVisualization {
|
class NearbyImageVis implements SpecialVisualization {
|
||||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||||
|
@ -508,7 +509,7 @@ export default class SpecialVisualizations {
|
||||||
constr(state: SpecialVisualizationState): BaseUIElement {
|
constr(state: SpecialVisualizationState): BaseUIElement {
|
||||||
return new LanguagePicker(
|
return new LanguagePicker(
|
||||||
state.layout.language,
|
state.layout.language,
|
||||||
Translations.t.general.pickLanguage.Clone()
|
state.userRelatedState.language
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -890,7 +891,8 @@ export default class SpecialVisualizations {
|
||||||
docs: "Opens the current view in the iD-editor",
|
docs: "Opens the current view in the iD-editor",
|
||||||
args: [],
|
args: [],
|
||||||
constr: (state, feature) => {
|
constr: (state, feature) => {
|
||||||
return new OpenIdEditor(state.mapProperties, undefined, feature.data.id)
|
return new SvelteUIElement(OpenIdEditor,
|
||||||
|
{ mapProperties: state.mapProperties, objectId: feature.data.id})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,6 +46,9 @@
|
||||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte";
|
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte";
|
||||||
import IfHidden from "./Base/IfHidden.svelte";
|
import IfHidden from "./Base/IfHidden.svelte";
|
||||||
import {onDestroy} from "svelte";
|
import {onDestroy} from "svelte";
|
||||||
|
import {OpenJosm} from "./BigComponents/OpenJosm";
|
||||||
|
import MapillaryLink from "./BigComponents/MapillaryLink.svelte";
|
||||||
|
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte";
|
||||||
|
|
||||||
export let state: ThemeViewState;
|
export let state: ThemeViewState;
|
||||||
let layout = state.layout;
|
let layout = state.layout;
|
||||||
|
@ -359,7 +362,10 @@
|
||||||
|
|
||||||
<Tr slot="title4" t={Translations.t.advanced.title}/>
|
<Tr slot="title4" t={Translations.t.advanced.title}/>
|
||||||
<div class="flex flex-col m-2" slot="content4">
|
<div class="flex flex-col m-2" slot="content4">
|
||||||
<ToSvelte construct={Hotkeys.generateDocumentationDynamic}></ToSvelte>
|
<OpenIdEditor mapProperties={state.mapProperties}/>
|
||||||
|
<ToSvelte construct={() => new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}/>
|
||||||
|
<MapillaryLink mapProperties={state.mapProperties}/>
|
||||||
|
<ToSvelte construct={Hotkeys.generateDocumentationDynamic}/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</TabbedGroup>
|
</TabbedGroup>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue