Refactoring of GPS-location (uses featureSource too now), factoring out state, add ReplaceGeometryAction and conflation example
This commit is contained in:
parent
1db54f3c8e
commit
2484848cd6
37 changed files with 1035 additions and 467 deletions
28
UI/Base/AsyncLazy.ts
Normal file
28
UI/Base/AsyncLazy.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import BaseUIElement from "../BaseUIElement";
|
||||
import {VariableUiElement} from "./VariableUIElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Loading from "./Loading";
|
||||
|
||||
export default class AsyncLazy extends BaseUIElement{
|
||||
private readonly _f: () => Promise<BaseUIElement>;
|
||||
|
||||
constructor(f: () => Promise<BaseUIElement>) {
|
||||
super();
|
||||
this._f = f;
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
// The caching of the BaseUIElement will guarantee that _f will only be called once
|
||||
|
||||
return new VariableUiElement(
|
||||
UIEventSource.FromPromise(this._f()).map(el => {
|
||||
if(el === undefined){
|
||||
return new Loading()
|
||||
}
|
||||
return el
|
||||
})
|
||||
|
||||
).ConstructElement()
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@ export interface MinimapOptions {
|
|||
export interface MinimapObj {
|
||||
readonly leafletMap: UIEventSource<any>,
|
||||
installBounds(factor: number | BBox, showRange?: boolean) : void
|
||||
TakeScreenshot(): Promise<any>;
|
||||
}
|
||||
|
||||
export default class Minimap {
|
||||
|
|
|
@ -9,6 +9,7 @@ import {Map} from "leaflet";
|
|||
import Minimap, {MinimapObj, MinimapOptions} from "./Minimap";
|
||||
import {BBox} from "../../Logic/BBox";
|
||||
import 'leaflet-polylineoffset'
|
||||
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
|
||||
|
||||
export default class MinimapImplementation extends BaseUIElement implements MinimapObj {
|
||||
private static _nextId = 0;
|
||||
|
@ -278,4 +279,10 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
|
|||
|
||||
this.leafletMap.setData(map)
|
||||
}
|
||||
|
||||
public async TakeScreenshot(){
|
||||
const screenshotter = new SimpleMapScreenshoter();
|
||||
screenshotter.addTo(this.leafletMap.data);
|
||||
return await screenshotter.takeScreen('image')
|
||||
}
|
||||
}
|
|
@ -14,6 +14,9 @@ import Toggle from "../Input/Toggle";
|
|||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||
import {Utils} from "../../Utils";
|
||||
import UserRelatedState from "../../Logic/State/UserRelatedState";
|
||||
import Loc from "../../Models/Loc";
|
||||
import BaseLayer from "../../Models/BaseLayer";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
|
||||
export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
||||
|
||||
|
@ -24,7 +27,10 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
layoutToUse: LayoutConfig,
|
||||
osmConnection: OsmConnection,
|
||||
featureSwitchShareScreen: UIEventSource<boolean>,
|
||||
featureSwitchMoreQuests: UIEventSource<boolean>
|
||||
featureSwitchMoreQuests: UIEventSource<boolean>,
|
||||
locationControl: UIEventSource<Loc>,
|
||||
backgroundLayer: UIEventSource<BaseLayer>,
|
||||
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||
} & UserRelatedState) {
|
||||
const layoutToUse = state.layoutToUse;
|
||||
super(
|
||||
|
@ -39,7 +45,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
layoutToUse: LayoutConfig,
|
||||
osmConnection: OsmConnection,
|
||||
featureSwitchShareScreen: UIEventSource<boolean>,
|
||||
featureSwitchMoreQuests: UIEventSource<boolean>
|
||||
featureSwitchMoreQuests: UIEventSource<boolean>,
|
||||
locationControl: UIEventSource<Loc>, backgroundLayer: UIEventSource<BaseLayer>, filteredLayers: UIEventSource<FilteredLayer[]>
|
||||
} & UserRelatedState,
|
||||
isShown: UIEventSource<boolean>):
|
||||
{ header: string | BaseUIElement; content: BaseUIElement }[] {
|
||||
|
@ -77,7 +84,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
layoutToUse: LayoutConfig,
|
||||
osmConnection: OsmConnection,
|
||||
featureSwitchShareScreen: UIEventSource<boolean>,
|
||||
featureSwitchMoreQuests: UIEventSource<boolean>
|
||||
featureSwitchMoreQuests: UIEventSource<boolean>,
|
||||
locationControl: UIEventSource<Loc>, backgroundLayer: UIEventSource<BaseLayer>, filteredLayers: UIEventSource<FilteredLayer[]>
|
||||
} & UserRelatedState, currentTab: UIEventSource<number>, isShown: UIEventSource<boolean>) {
|
||||
|
||||
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown)
|
||||
|
|
|
@ -9,7 +9,6 @@ import Toggle from "../Input/Toggle";
|
|||
import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction";
|
||||
import {Tag} from "../../Logic/Tags/Tag";
|
||||
import Loading from "../Base/Loading";
|
||||
import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction";
|
||||
import CreateNewWayAction from "../../Logic/Osm/Actions/CreateNewWayAction";
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||
|
@ -26,6 +25,13 @@ import SpecialVisualizations, {SpecialVisualization} from "../SpecialVisualizati
|
|||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import Svg from "../../Svg";
|
||||
import {Utils} from "../../Utils";
|
||||
import Minimap from "../Base/Minimap";
|
||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
||||
import AllKnownLayers from "../../Customizations/AllKnownLayers";
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer";
|
||||
import BaseLayer from "../../Models/BaseLayer";
|
||||
import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction";
|
||||
|
||||
|
||||
export interface ImportButtonState {
|
||||
|
@ -38,6 +44,8 @@ export interface ImportButtonState {
|
|||
feature: any,
|
||||
minZoom: number,
|
||||
state: {
|
||||
backgroundLayer: UIEventSource<BaseLayer>;
|
||||
filteredLayers: UIEventSource<FilteredLayer[]>;
|
||||
featureSwitchUserbadge: UIEventSource<boolean>;
|
||||
featurePipeline: FeaturePipeline;
|
||||
allElements: ElementStorage;
|
||||
|
@ -48,8 +56,14 @@ export interface ImportButtonState {
|
|||
locationControl: UIEventSource<{ zoom: number }>
|
||||
},
|
||||
guiState: { filterViewIsOpened: UIEventSource<boolean> },
|
||||
snapToLayers?: string[],
|
||||
snapToLayersMaxDist?: number
|
||||
|
||||
snapSettings?: {
|
||||
snapToLayers: string[],
|
||||
snapToLayersMaxDist?: number
|
||||
},
|
||||
conflationSettings?: {
|
||||
conflateWayId: string
|
||||
}
|
||||
}
|
||||
|
||||
export class ImportButtonSpecialViz implements SpecialVisualization {
|
||||
|
@ -83,7 +97,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
|
||||
#### Specifying which tags to copy or add
|
||||
|
||||
The first argument of the import button takes a \`;\`-seperated list of tags to add.
|
||||
The argument \`tags\` of the import button takes a \`;\`-seperated list of tags to add.
|
||||
|
||||
${Utils.Special_visualizations_tagsToApplyHelpText}
|
||||
|
||||
|
@ -113,8 +127,9 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
|
|||
doc: "How far the contributor must zoom in before being able to import the point",
|
||||
defaultValue: "18"
|
||||
}, {
|
||||
name: "Snap onto layer(s)",
|
||||
doc: "If a way of the given layer is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list",
|
||||
name: "Snap onto layer(s)/replace geometry with this other way",
|
||||
doc: " - If the value corresponding with this key starts with 'way/' and the feature is a LineString or Polygon, the original OSM-way geometry will be changed to match the new geometry\n" +
|
||||
" - If a way of the given layer(s) is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list",
|
||||
}, {
|
||||
name: "snap max distance",
|
||||
doc: "The maximum distance that this point will move to snap onto a layer (in meters)",
|
||||
|
@ -130,7 +145,7 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
|
|||
const id = tagSource.data.id;
|
||||
const feature = state.allElements.ContainingFeatures.get(id)
|
||||
let minZoom = args[4] == "" ? 18 : Number(args[4])
|
||||
if(isNaN(minZoom)){
|
||||
if (isNaN(minZoom)) {
|
||||
console.warn("Invalid minzoom:", minZoom)
|
||||
minZoom = 18
|
||||
}
|
||||
|
@ -145,13 +160,29 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
|
|||
img = () => Svg.add_ui()
|
||||
}
|
||||
|
||||
const snapToLayers = args[5]?.split(";").filter(s => s !== "")
|
||||
const snapToLayersMaxDist = Number(args[6] ?? 6)
|
||||
|
||||
if (targetLayer === undefined) {
|
||||
const e = "Target layer not defined: error in import button for theme: " + state.layoutToUse.id + ": layer " + args[0] + " not found"
|
||||
console.error(e)
|
||||
return new FixedUiElement(e).SetClass("alert")
|
||||
let snapSettings = undefined
|
||||
let conflationSettings = undefined
|
||||
const possibleWayId = tagSource.data[args[5]]
|
||||
if (possibleWayId?.startsWith("way/")) {
|
||||
// This is a conflation
|
||||
conflationSettings = {
|
||||
conflateWayId: possibleWayId
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
const snapToLayers = args[5]?.split(";").filter(s => s !== "")
|
||||
const snapToLayersMaxDist = Number(args[6] ?? 6)
|
||||
|
||||
if (targetLayer === undefined) {
|
||||
const e = "Target layer not defined: error in import button for theme: " + state.layoutToUse.id + ": layer " + args[0] + " not found"
|
||||
console.error(e)
|
||||
return new FixedUiElement(e).SetClass("alert")
|
||||
}
|
||||
snapSettings = {
|
||||
snapToLayers,
|
||||
snapToLayersMaxDist
|
||||
}
|
||||
}
|
||||
|
||||
return new ImportButton(
|
||||
|
@ -160,8 +191,8 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
|
|||
feature, newTags, message, minZoom,
|
||||
originalTags: tagSource,
|
||||
targetLayer,
|
||||
snapToLayers,
|
||||
snapToLayersMaxDist
|
||||
snapSettings,
|
||||
conflationSettings
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -201,7 +232,7 @@ export default class ImportButton extends Toggle {
|
|||
|
||||
const importClicked = new UIEventSource(false);
|
||||
const importFlow = new Toggle(
|
||||
new Lazy(() => ImportButton.createConfirmPanel(o, isImported, importClicked)),
|
||||
ImportButton.createConfirmPanel(o, isImported, importClicked),
|
||||
importButton,
|
||||
importClicked
|
||||
)
|
||||
|
@ -228,7 +259,121 @@ export default class ImportButton extends Toggle {
|
|||
)
|
||||
}
|
||||
|
||||
public static createConfirmPanel(
|
||||
public static createConfirmPanel(o: ImportButtonState,
|
||||
isImported: UIEventSource<boolean>,
|
||||
importClicked: UIEventSource<boolean>) {
|
||||
const geometry = o.feature.geometry
|
||||
if (geometry.type === "Point") {
|
||||
return new Lazy(() => ImportButton.createConfirmPanelForPoint(o, isImported, importClicked))
|
||||
}
|
||||
|
||||
|
||||
if (geometry.type === "Polygon" || geometry.type == "LineString") {
|
||||
return new Lazy(() => ImportButton.createConfirmForWay(o, isImported, importClicked))
|
||||
}
|
||||
console.error("Invalid type to import", geometry.type)
|
||||
return new FixedUiElement("Invalid geometry type:" + geometry.type).SetClass("alert")
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static createConfirmForWay(o: ImportButtonState,
|
||||
isImported: UIEventSource<boolean>,
|
||||
importClicked: UIEventSource<boolean>): BaseUIElement {
|
||||
|
||||
const confirmationMap = Minimap.createMiniMap({
|
||||
allowMoving: false,
|
||||
background: o.state.backgroundLayer
|
||||
})
|
||||
confirmationMap.SetStyle("height: 20rem; overflow: hidden").SetClass("rounded-xl")
|
||||
|
||||
const relevantFeatures = Utils.NoNull([o.feature, o.state.allElements?.ContainingFeatures?.get(o.conflationSettings?.conflateWayId)])
|
||||
// SHow all relevant data - including (eventually) the way of which the geometry will be replaced
|
||||
new ShowDataMultiLayer({
|
||||
leafletMap: confirmationMap.leafletMap,
|
||||
enablePopups: false,
|
||||
zoomToFeatures: true,
|
||||
features: new StaticFeatureSource(relevantFeatures, false),
|
||||
allElements: o.state.allElements,
|
||||
layers: o.state.filteredLayers
|
||||
})
|
||||
|
||||
const theme = o.state.layoutToUse.id
|
||||
|
||||
|
||||
const changes = o.state.changes
|
||||
let confirm: () => Promise<string>
|
||||
if (o.conflationSettings !== undefined) {
|
||||
|
||||
let replaceGeometryAction = new ReplaceGeometryAction(
|
||||
o.state,
|
||||
o.feature,
|
||||
o.conflationSettings.conflateWayId,
|
||||
{
|
||||
theme: o.state.layoutToUse.id,
|
||||
newTags: o.newTags.data
|
||||
}
|
||||
)
|
||||
|
||||
replaceGeometryAction.GetPreview().then(changePreview => {
|
||||
new ShowDataLayer({
|
||||
leafletMap: confirmationMap.leafletMap,
|
||||
enablePopups: false,
|
||||
zoomToFeatures: false,
|
||||
features: changePreview,
|
||||
allElements: o.state.allElements,
|
||||
layerToShow: AllKnownLayers.sharedLayers.get("conflation")
|
||||
})
|
||||
})
|
||||
|
||||
confirm = async () => {
|
||||
changes.applyAction (replaceGeometryAction)
|
||||
return o.feature.properties.id
|
||||
}
|
||||
|
||||
} else {
|
||||
confirm = async () => {
|
||||
const geom = o.feature.geometry
|
||||
let coordinates: [number, number][]
|
||||
if (geom.type === "LineString") {
|
||||
coordinates = geom.coordinates
|
||||
} else if (geom.type === "Polygon") {
|
||||
coordinates = geom.coordinates[0]
|
||||
}
|
||||
const action = new CreateNewWayAction(o.newTags.data, coordinates.map(lngLat => ({
|
||||
lat: lngLat[1],
|
||||
lon: lngLat[0]
|
||||
})), {theme})
|
||||
return action.newElementId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const confirmButton = new SubtleButton(o.image(), o.message)
|
||||
confirmButton.onClick(async () => {
|
||||
{
|
||||
if (isImported.data) {
|
||||
return
|
||||
}
|
||||
o.originalTags.data["_imported"] = "yes"
|
||||
o.originalTags.ping() // will set isImported as per its definition
|
||||
|
||||
const idToSelect = await confirm()
|
||||
|
||||
o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get(idToSelect))
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
const cancel = new SubtleButton(Svg.close_ui(), Translations.t.general.cancel).onClick(() => {
|
||||
importClicked.setData(false)
|
||||
})
|
||||
|
||||
|
||||
return new Combine([confirmationMap, confirmButton, cancel]).SetClass("flex flex-col")
|
||||
}
|
||||
|
||||
public static createConfirmPanelForPoint(
|
||||
o: ImportButtonState,
|
||||
isImported: UIEventSource<boolean>,
|
||||
importClicked: UIEventSource<boolean>): BaseUIElement {
|
||||
|
@ -239,39 +384,43 @@ export default class ImportButton extends Toggle {
|
|||
}
|
||||
o.originalTags.data["_imported"] = "yes"
|
||||
o.originalTags.ping() // will set isImported as per its definition
|
||||
const newElementAction = ImportButton.createAddActionForFeature(o.newTags.data, o.feature, o.state.layoutToUse.id)
|
||||
const geometry = o.feature.geometry
|
||||
const lat = geometry.coordinates[1]
|
||||
const lon = geometry.coordinates[0];
|
||||
const newElementAction = new CreateNewNodeAction(o.newTags.data, lat, lon, {
|
||||
theme: o.state.layoutToUse.id,
|
||||
changeType: "import"
|
||||
})
|
||||
|
||||
await o.state.changes.applyAction(newElementAction)
|
||||
o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get(
|
||||
newElementAction.newElementId
|
||||
))
|
||||
console.log("Did set selected element to", o.state.allElements.ContainingFeatures.get(
|
||||
newElementAction.newElementId
|
||||
))
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
importClicked.setData(false)
|
||||
}
|
||||
|
||||
if (o.feature.geometry.type === "Point") {
|
||||
const presetInfo = <PresetInfo>{
|
||||
tags: o.newTags.data,
|
||||
icon: o.image,
|
||||
description: o.description,
|
||||
layerToAddTo: o.targetLayer,
|
||||
name: o.message,
|
||||
title: o.message,
|
||||
preciseInput: { snapToLayers: o.snapToLayers,
|
||||
maxSnapDistance: o.snapToLayersMaxDist}
|
||||
const presetInfo = <PresetInfo>{
|
||||
tags: o.newTags.data,
|
||||
icon: o.image,
|
||||
description: o.description,
|
||||
layerToAddTo: o.targetLayer,
|
||||
name: o.message,
|
||||
title: o.message,
|
||||
preciseInput: {
|
||||
snapToLayers: o.snapSettings?.snapToLayers,
|
||||
maxSnapDistance: o.snapSettings?.snapToLayersMaxDist
|
||||
}
|
||||
|
||||
const [lon, lat] = o.feature.geometry.coordinates
|
||||
console.log("Creating an import dialog at location", lon, lat)
|
||||
return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), {
|
||||
lon,
|
||||
lat
|
||||
}, confirm, cancel)
|
||||
}
|
||||
|
||||
const [lon, lat] = o.feature.geometry.coordinates
|
||||
return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), {
|
||||
lon,
|
||||
lat
|
||||
}, confirm, cancel)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -279,41 +428,4 @@ export default class ImportButton extends Toggle {
|
|||
const type = feature.geometry.type
|
||||
return type === "Point" || type === "LineString" || (type === "Polygon" && feature.geometry.coordinates.length === 1)
|
||||
}
|
||||
|
||||
private static createAddActionForFeature(newTags: Tag[], feature: any, theme: string):
|
||||
OsmChangeAction & { newElementId: string } {
|
||||
const geometry = feature.geometry
|
||||
const type = geometry.type
|
||||
if (type === "Point") {
|
||||
const lat = geometry.coordinates[1]
|
||||
const lon = geometry.coordinates[0];
|
||||
return new CreateNewNodeAction(newTags, lat, lon, {
|
||||
theme,
|
||||
changeType: "import"
|
||||
})
|
||||
}
|
||||
|
||||
if (type === "LineString") {
|
||||
return new CreateNewWayAction(
|
||||
newTags,
|
||||
geometry.coordinates.map(coor => ({lon: coor[0], lat: coor[1]})),
|
||||
{
|
||||
theme
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (type === "Polygon") {
|
||||
return new CreateNewWayAction(
|
||||
newTags,
|
||||
geometry.coordinates[0].map(coor => ({lon: coor[0], lat: coor[1]})),
|
||||
{
|
||||
theme
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@ import Loc from "../../Models/Loc";
|
|||
import {BBox} from "../../Logic/BBox";
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
import BaseLayer from "../../Models/BaseLayer";
|
||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||
|
||||
export default class LeftControls extends Combine {
|
||||
|
||||
|
@ -26,7 +28,9 @@ export default class LeftControls extends Combine {
|
|||
featureSwitchEnableExport: UIEventSource<boolean>,
|
||||
featureSwitchExportAsPdf: UIEventSource<boolean>,
|
||||
filteredLayers: UIEventSource<FilteredLayer[]>,
|
||||
featureSwitchFilter: UIEventSource<boolean>
|
||||
featureSwitchFilter: UIEventSource<boolean>,
|
||||
backgroundLayer: UIEventSource<BaseLayer>,
|
||||
osmConnection: OsmConnection
|
||||
},
|
||||
guiState: {
|
||||
downloadControlIsOpened: UIEventSource<boolean>,
|
||||
|
|
|
@ -4,17 +4,30 @@ import MapControlButton from "../MapControlButton";
|
|||
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
|
||||
import Svg from "../../Svg";
|
||||
import MapState from "../../Logic/State/MapState";
|
||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
||||
import AllKnownLayers from "../../Customizations/AllKnownLayers";
|
||||
|
||||
export default class RightControls extends Combine {
|
||||
|
||||
constructor(state:MapState) {
|
||||
|
||||
const geolocatioHandler = new GeoLocationHandler(
|
||||
state.currentGPSLocation,
|
||||
state.leafletMap,
|
||||
state.layoutToUse
|
||||
)
|
||||
|
||||
new ShowDataLayer({
|
||||
layerToShow: AllKnownLayers.sharedLayers.get("gps_location"),
|
||||
leafletMap: state.leafletMap,
|
||||
enablePopups: true,
|
||||
features: geolocatioHandler.currentLocation
|
||||
})
|
||||
|
||||
const geolocationButton = new Toggle(
|
||||
new MapControlButton(
|
||||
new GeoLocationHandler(
|
||||
state.currentGPSLocation,
|
||||
state.leafletMap,
|
||||
state.layoutToUse
|
||||
), {
|
||||
geolocatioHandler
|
||||
, {
|
||||
dontStyle: true
|
||||
}
|
||||
),
|
||||
|
|
|
@ -8,11 +8,14 @@ import Toggle from "../Input/Toggle";
|
|||
import Translations from "../i18n/Translations";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import MapState from "../../Logic/State/MapState";
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||
import Loc from "../../Models/Loc";
|
||||
import BaseLayer from "../../Models/BaseLayer";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
|
||||
export default class ShareScreen extends Combine {
|
||||
|
||||
constructor(state: MapState) {
|
||||
constructor(state: {layoutToUse: LayoutConfig, locationControl: UIEventSource<Loc>, backgroundLayer: UIEventSource<BaseLayer>, filteredLayers: UIEventSource<FilteredLayer[]>}) {
|
||||
const layout = state?.layoutToUse;
|
||||
const tr = Translations.t.general.sharescreen;
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@ import FullWelcomePaneWithTabs from "./BigComponents/FullWelcomePaneWithTabs";
|
|||
import MapControlButton from "./MapControlButton";
|
||||
import Svg from "../Svg";
|
||||
import Toggle from "./Input/Toggle";
|
||||
import Hash from "../Logic/Web/Hash";
|
||||
import {QueryParameters} from "../Logic/Web/QueryParameters";
|
||||
import Constants from "../Models/Constants";
|
||||
import UserBadge from "./BigComponents/UserBadge";
|
||||
import SearchAndGo from "./BigComponents/SearchAndGo";
|
||||
import Link from "./Base/Link";
|
||||
|
@ -24,77 +21,7 @@ import Translations from "./i18n/Translations";
|
|||
import SimpleAddUI from "./BigComponents/SimpleAddUI";
|
||||
import StrayClickHandler from "../Logic/Actors/StrayClickHandler";
|
||||
import Lazy from "./Base/Lazy";
|
||||
|
||||
export class DefaultGuiState {
|
||||
public readonly welcomeMessageIsOpened : UIEventSource<boolean>;
|
||||
public readonly downloadControlIsOpened: UIEventSource<boolean>;
|
||||
public readonly filterViewIsOpened: UIEventSource<boolean>;
|
||||
public readonly copyrightViewIsOpened: UIEventSource<boolean>;
|
||||
public readonly welcomeMessageOpenedTab: UIEventSource<number>
|
||||
public readonly allFullScreenStates: UIEventSource<boolean>[] = []
|
||||
static state: DefaultGuiState;
|
||||
|
||||
constructor() {
|
||||
|
||||
|
||||
|
||||
this.welcomeMessageOpenedTab = UIEventSource.asFloat(QueryParameters.GetQueryParameter(
|
||||
"tab",
|
||||
"0",
|
||||
`The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`
|
||||
));
|
||||
this.welcomeMessageIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"welcome-control-toggle",
|
||||
"false",
|
||||
"Whether or not the welcome panel is shown"
|
||||
)
|
||||
this.downloadControlIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"download-control-toggle",
|
||||
"false",
|
||||
"Whether or not the download panel is shown"
|
||||
)
|
||||
this.filterViewIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"filter-toggle",
|
||||
"false",
|
||||
"Whether or not the filter view is shown"
|
||||
)
|
||||
this.copyrightViewIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"copyright-toggle",
|
||||
"false",
|
||||
"Whether or not the copyright view is shown"
|
||||
)
|
||||
if(Hash.hash.data === "download"){
|
||||
this.downloadControlIsOpened.setData(true)
|
||||
}
|
||||
if(Hash.hash.data === "filters"){
|
||||
this.filterViewIsOpened.setData(true)
|
||||
}
|
||||
if(Hash.hash.data === "copyright"){
|
||||
this.copyrightViewIsOpened.setData(true)
|
||||
}
|
||||
if(Hash.hash.data === "" || Hash.hash.data === undefined || Hash.hash.data === "welcome"){
|
||||
this.welcomeMessageIsOpened.setData(true)
|
||||
}
|
||||
|
||||
this.allFullScreenStates.push(this.downloadControlIsOpened, this.filterViewIsOpened, this.copyrightViewIsOpened, this.welcomeMessageIsOpened)
|
||||
|
||||
for (let i = 0; i < this.allFullScreenStates.length; i++){
|
||||
const fullScreenState = this.allFullScreenStates[i];
|
||||
for (let j = 0; j < this.allFullScreenStates.length; j++){
|
||||
if(i == j){
|
||||
continue
|
||||
}
|
||||
const otherState = this.allFullScreenStates[j];
|
||||
fullScreenState.addCallbackAndRunD(isOpened => {
|
||||
if(isOpened){
|
||||
otherState.setData(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
import {DefaultGuiState} from "./DefaultGuiState";
|
||||
|
||||
|
||||
/**
|
||||
|
|
74
UI/DefaultGuiState.ts
Normal file
74
UI/DefaultGuiState.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {QueryParameters} from "../Logic/Web/QueryParameters";
|
||||
import Constants from "../Models/Constants";
|
||||
import Hash from "../Logic/Web/Hash";
|
||||
|
||||
export class DefaultGuiState {
|
||||
public readonly welcomeMessageIsOpened: UIEventSource<boolean>;
|
||||
public readonly downloadControlIsOpened: UIEventSource<boolean>;
|
||||
public readonly filterViewIsOpened: UIEventSource<boolean>;
|
||||
public readonly copyrightViewIsOpened: UIEventSource<boolean>;
|
||||
public readonly welcomeMessageOpenedTab: UIEventSource<number>
|
||||
public readonly allFullScreenStates: UIEventSource<boolean>[] = []
|
||||
static state: DefaultGuiState;
|
||||
|
||||
constructor() {
|
||||
|
||||
|
||||
this.welcomeMessageOpenedTab = UIEventSource.asFloat(QueryParameters.GetQueryParameter(
|
||||
"tab",
|
||||
"0",
|
||||
`The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`
|
||||
));
|
||||
this.welcomeMessageIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"welcome-control-toggle",
|
||||
"false",
|
||||
"Whether or not the welcome panel is shown"
|
||||
)
|
||||
this.downloadControlIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"download-control-toggle",
|
||||
"false",
|
||||
"Whether or not the download panel is shown"
|
||||
)
|
||||
this.filterViewIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"filter-toggle",
|
||||
"false",
|
||||
"Whether or not the filter view is shown"
|
||||
)
|
||||
this.copyrightViewIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"copyright-toggle",
|
||||
"false",
|
||||
"Whether or not the copyright view is shown"
|
||||
)
|
||||
if (Hash.hash.data === "download") {
|
||||
this.downloadControlIsOpened.setData(true)
|
||||
}
|
||||
if (Hash.hash.data === "filters") {
|
||||
this.filterViewIsOpened.setData(true)
|
||||
}
|
||||
if (Hash.hash.data === "copyright") {
|
||||
this.copyrightViewIsOpened.setData(true)
|
||||
}
|
||||
if (Hash.hash.data === "" || Hash.hash.data === undefined || Hash.hash.data === "welcome") {
|
||||
this.welcomeMessageIsOpened.setData(true)
|
||||
}
|
||||
|
||||
this.allFullScreenStates.push(this.downloadControlIsOpened, this.filterViewIsOpened, this.copyrightViewIsOpened, this.welcomeMessageIsOpened)
|
||||
|
||||
for (let i = 0; i < this.allFullScreenStates.length; i++) {
|
||||
const fullScreenState = this.allFullScreenStates[i];
|
||||
for (let j = 0; j < this.allFullScreenStates.length; j++) {
|
||||
if (i == j) {
|
||||
continue
|
||||
}
|
||||
const otherState = this.allFullScreenStates[j];
|
||||
fullScreenState.addCallbackAndRunD(isOpened => {
|
||||
if (isOpened) {
|
||||
otherState.setData(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
|
||||
|
||||
import jsPDF from "jspdf";
|
||||
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Minimap from "./Base/Minimap";
|
||||
import Minimap, {MinimapObj} from "./Base/Minimap";
|
||||
import Loc from "../Models/Loc";
|
||||
import BaseLayer from "../Models/BaseLayer";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
|
@ -14,7 +11,6 @@ import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
|||
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline";
|
||||
import ShowDataLayer from "./ShowDataLayer/ShowDataLayer";
|
||||
import {BBox} from "../Logic/BBox";
|
||||
import ShowOverlayLayer from "./ShowDataLayer/ShowOverlayLayer";
|
||||
/**
|
||||
* Creates screenshoter to take png screenshot
|
||||
* Creates jspdf and downloads it
|
||||
|
@ -63,14 +59,12 @@ export default class ExportPDF {
|
|||
location: new UIEventSource<Loc>(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot
|
||||
background: options.background,
|
||||
allowMoving: false,
|
||||
|
||||
|
||||
onFullyLoaded: leaflet => window.setTimeout(() => {
|
||||
onFullyLoaded: _ => window.setTimeout(() => {
|
||||
if (self._screenhotTaken) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
self.CreatePdf(leaflet)
|
||||
self.CreatePdf(minimap)
|
||||
.then(() => self.cleanup())
|
||||
.catch(() => self.cleanup())
|
||||
} catch (e) {
|
||||
|
@ -112,20 +106,17 @@ export default class ExportPDF {
|
|||
this._screenhotTaken = true;
|
||||
}
|
||||
|
||||
private async CreatePdf(leaflet: L.Map) {
|
||||
private async CreatePdf(minimap: MinimapObj) {
|
||||
|
||||
|
||||
|
||||
console.log("PDF creation started")
|
||||
const t = Translations.t.general.pdf;
|
||||
const layout = this._layout
|
||||
const screenshotter = new SimpleMapScreenshoter();
|
||||
//minimap op index.html -> hidden daar alles op doen en dan weg
|
||||
//minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline
|
||||
screenshotter.addTo(leaflet);
|
||||
|
||||
|
||||
let doc = new jsPDF('landscape');
|
||||
|
||||
|
||||
const image = (await screenshotter.takeScreen('image'))
|
||||
const image = await minimap.TakeScreenshot()
|
||||
// @ts-ignore
|
||||
doc.addImage(image, 'PNG', 0, 0, this.mapW, this.mapH);
|
||||
|
||||
|
|
|
@ -167,6 +167,9 @@ export default class LocationInput extends InputElement<Loc> implements MinimapO
|
|||
installBounds(factor: number | BBox, showRange?: boolean): void {
|
||||
this.map.installBounds(factor, showRange)
|
||||
}
|
||||
TakeScreenshot(): Promise<any> {
|
||||
return this.map.TakeScreenshot()
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
try {
|
||||
|
|
|
@ -58,7 +58,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
for (const groupName of allGroupNames) {
|
||||
const questions = layerConfig.tagRenderings.filter(tr => tr.group === groupName)
|
||||
const questionBox = new QuestionBox(tags, questions, layerConfig.units);
|
||||
console.log("Groupname:", groupName)
|
||||
questionBoxes.set(groupName, questionBox)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,6 @@ export default class ShowDataLayer {
|
|||
continue
|
||||
}
|
||||
try {
|
||||
|
||||
if ((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) {
|
||||
const self = this;
|
||||
const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates)
|
||||
|
@ -190,9 +189,10 @@ export default class ShowDataLayer {
|
|||
|
||||
if (options.zoomToFeatures ?? false) {
|
||||
try {
|
||||
mp.fitBounds(this.geoLayer.getBounds(), {animate: false})
|
||||
const bounds = this.geoLayer.getBounds()
|
||||
mp.fitBounds(bounds, {animate: false})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.debug("Invalid bounds",e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import Histogram from "./BigComponents/Histogram";
|
|||
import Loc from "../Models/Loc";
|
||||
import {Utils} from "../Utils";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
import ImportButton, {ImportButtonSpecialViz} from "./BigComponents/ImportButton";
|
||||
import {ImportButtonSpecialViz} from "./BigComponents/ImportButton";
|
||||
import {Tag} from "../Logic/Tags/Tag";
|
||||
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||
import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer";
|
||||
|
@ -38,9 +38,9 @@ import {SubtleButton} from "./Base/SubtleButton";
|
|||
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction";
|
||||
import {And} from "../Logic/Tags/And";
|
||||
import Toggle from "./Input/Toggle";
|
||||
import {DefaultGuiState} from "./DefaultGUI";
|
||||
import Img from "./Base/Img";
|
||||
import FilteredLayer from "../Models/FilteredLayer";
|
||||
import {DefaultGuiState} from "./DefaultGuiState";
|
||||
|
||||
export interface SpecialVisualization {
|
||||
funcName: string,
|
||||
|
|
|
@ -8,7 +8,7 @@ import {Utils} from "../Utils";
|
|||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import Combine from "./Base/Combine";
|
||||
import BaseUIElement from "./BaseUIElement";
|
||||
import {DefaultGuiState} from "./DefaultGUI";
|
||||
import {DefaultGuiState} from "./DefaultGuiState";
|
||||
|
||||
export class SubstitutedTranslation extends VariableUiElement {
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue