diff --git a/assets/layers/last_click/last_click.json b/assets/layers/last_click/last_click.json index 335731bdaa..618ef63431 100644 --- a/assets/layers/last_click/last_click.json +++ b/assets/layers/last_click/last_click.json @@ -56,6 +56,7 @@ }, "popupInFloatover": true, "titleIcons": [], + "isShown": "mouse_button=right", "pointRendering": [ { "marker": [ @@ -168,6 +169,11 @@ "render": { "*": "{open_note()}" } + }, + { + "id": "debug", + "metacondition": "__featureSwitchIsDebugging=true", + "render": "{all_tags()}" } ], "filter": [ diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index 5ad09e4aa9..556036b291 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -540,7 +540,7 @@ }, { "if": "mapcomplete-more_privacy=no", - "icon": "./assets/svg/eye.svg", + "icon": "cross_bottom_right:red;./assets/svg/eye.svg", "then": { "en": "When making changes to OpenStreetMap, roughly indicate how far away you were from the changed objects. This helps other contributors to understand how you made the change", "de": "Wenn du Änderungen an OpenStreetMap vornimmst, gib grob an, wie weit du von den geänderten Objekten entfernt warst. Das hilft anderen Mitwirkenden zu verstehen, wie du die Änderung vorgenommen hast.", @@ -619,7 +619,7 @@ "mappings": [ { "if": "mapcomplete-translation-mode=false", - "icon": "./assets/layers/usersettings/translate_disabled.svg", + "icon": "cross_bottom_right:red;./assets/svg/translate.svg", "then": { "en": "Don't show a button to quickly change translations", "de": "Keine Schaltfläche zum schnellen Wechseln von Übersetzungen anzeigen", @@ -856,6 +856,7 @@ }, { "if": "mapcomplete-show_debug=no", + "icon": "cross_bottom_right:red;./assets/svg/bug.svg", "then": { "en": "Don't show debug info", "de": "Keine Debug-Informationen anzeigen", diff --git a/package.json b/package.json index f044c01074..3b4f6b0284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.43.3", + "version": "0.44.0", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues", diff --git a/src/Logic/FeatureSource/Sources/LastClickFeatureSource.ts b/src/Logic/FeatureSource/Sources/LastClickFeatureSource.ts index 092a7ab532..a1504fbbf7 100644 --- a/src/Logic/FeatureSource/Sources/LastClickFeatureSource.ts +++ b/src/Logic/FeatureSource/Sources/LastClickFeatureSource.ts @@ -1,24 +1,24 @@ import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig" -import { WritableFeatureSource } from "../FeatureSource" -import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource" +import { ImmutableStore, Store } from "../../UIEventSource" import { Feature, Point } from "geojson" import { TagUtils } from "../../Tags/TagUtils" import BaseUIElement from "../../../UI/BaseUIElement" import { Utils } from "../../../Utils" import { OsmTags } from "../../../Models/OsmFeature" +import { FeatureSource } from "../FeatureSource" /** * Highly specialized feature source. * Based on a lon/lat UIEVentSource, will generate the corresponding feature with the correct properties */ -export class LastClickFeatureSource { +export class LastClickFeatureSource implements FeatureSource{ public readonly renderings: string[] private i: number = 0 private readonly hasPresets: boolean private readonly hasNoteLayer: boolean public static readonly newPointElementId = "new_point_dialog" - - constructor(layout: LayoutConfig) { + public readonly features: Store + constructor(layout: LayoutConfig, clickSource: Store<{lon:number,lat:number,mode:"left"|"right"|"middle"}> ) { this.hasNoteLayer = layout.hasNoteLayer() this.hasPresets = layout.hasPresets() const allPresets: BaseUIElement[] = [] @@ -33,7 +33,7 @@ export class LastClickFeatureSource { } const { html } = rendering.RenderIcon(tags, { noSize: true, - includeBadges: false, + includeBadges: false }) allPresets.push(html) } @@ -43,16 +43,21 @@ export class LastClickFeatureSource { Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML ) ) + + this.features = clickSource.mapD(({lon, lat,mode}) => + [this.createFeature(lon, lat, mode)]) + } - public createFeature(lon: number, lat: number): Feature { + public createFeature(lon: number, lat: number, mode?: "left" | "right" | "middle"): Feature { const properties: OsmTags = { - id: LastClickFeatureSource.newPointElementId, + id: LastClickFeatureSource.newPointElementId + "_" + this.i, has_note_layer: this.hasNoteLayer ? "yes" : "no", has_presets: this.hasPresets ? "yes" : "no", renderings: this.renderings.join(""), number_of_presets: "" + this.renderings.length, first_preset: this.renderings[0], + mouse_button: mode ?? "none" } this.i++ @@ -61,8 +66,8 @@ export class LastClickFeatureSource { properties, geometry: { type: "Point", - coordinates: [lon, lat], - }, + coordinates: [lon, lat] + } } } } diff --git a/src/Logic/Web/LinkedDataLoader.ts b/src/Logic/Web/LinkedDataLoader.ts index de43327f72..bd98d3737c 100644 --- a/src/Logic/Web/LinkedDataLoader.ts +++ b/src/Logic/Web/LinkedDataLoader.ts @@ -236,8 +236,9 @@ export default class LinkedDataLoader { static async fetchJsonLd( url: string, options?: JsonLdLoaderOptions, - mode: "fetch-lod" | "fetch-raw" | "proxy" + mode?: "fetch-lod" | "fetch-raw" | "proxy" ): Promise { + mode ??= "fetch-lod" if (mode === "proxy") { url = Constants.linkedDataProxy.replace("{url}", encodeURIComponent(url)) } diff --git a/src/Models/ThemeConfig/PointRenderingConfig.ts b/src/Models/ThemeConfig/PointRenderingConfig.ts index a83767adca..8faace7fd3 100644 --- a/src/Models/ThemeConfig/PointRenderingConfig.ts +++ b/src/Models/ThemeConfig/PointRenderingConfig.ts @@ -205,6 +205,7 @@ export default class PointRenderingConfig extends WithContextLoader { marker: this.marker, rotation: this.rotation, tags, + emojiHeight: iconH }).SetClass("w-full h-full") : undefined let badges = undefined diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 556bc7604c..7801fc2147 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -5,7 +5,7 @@ import { Store, UIEventSource } from "../Logic/UIEventSource" import { FeatureSource, IndexedFeatureSource, - WritableFeatureSource, + WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource" import { OsmConnection } from "../Logic/Osm/OsmConnection" import { ExportableMap, MapProperties } from "./MapProperties" @@ -51,7 +51,7 @@ import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveF import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor" import NoElementsInViewDetector, { - FeatureViewState, + FeatureViewState } from "../Logic/Actors/NoElementsInViewDetector" import FilteredLayer from "./FilteredLayer" import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector" @@ -64,12 +64,15 @@ import { GeolocationControlState } from "../UI/BigComponents/GeolocationControl" import Zoomcontrol from "../UI/Zoomcontrol" import { SummaryTileSource, - SummaryTileSourceRewriter, + SummaryTileSourceRewriter } from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource" import summaryLayer from "../assets/generated/layers/summary.json" +import last_click_layerconfig from "../assets/generated/layers/last_click.json" + import { LayerConfigJson } from "./ThemeConfig/Json/LayerConfigJson" import Locale from "../UI/i18n/Locale" import Hash from "../Logic/Web/Hash" +import { GeoOperations } from "../Logic/GeoOperations" /** * @@ -173,7 +176,7 @@ export default class ThemeViewState implements SpecialVisualizationState { "oauth_token", undefined, "Used to complete the login" - ), + ) }) this.userRelatedState = new UserRelatedState( this.osmConnection, @@ -248,8 +251,8 @@ export default class ThemeViewState implements SpecialVisualizationState { bbox.asGeoJson({ zoom: this.mapProperties.zoom.data, ...this.mapProperties.location.data, - id: "current_view_" + currentViewIndex, - }), + id: "current_view_" + currentViewIndex + }) ] }) ) @@ -266,7 +269,7 @@ export default class ThemeViewState implements SpecialVisualizationState { featurePropertiesStore: this.featureProperties, osmConnection: this.osmConnection, historicalUserLocations: this.geolocation.historicalUserLocations, - featureSwitches: this.featureSwitches, + featureSwitches: this.featureSwitches }, layout?.isLeftRightSensitive() ?? false ) @@ -293,7 +296,7 @@ export default class ThemeViewState implements SpecialVisualizationState { "leftover features, such as", features[0].properties ) - }, + } } ) this.perLayer = perLayer.perLayer @@ -331,7 +334,7 @@ export default class ThemeViewState implements SpecialVisualizationState { return sorted }) - this.lastClickObject = new LastClickFeatureSource(this.layout) + this.lastClickObject = new LastClickFeatureSource(this.layout, this.mapProperties.lastClickLocation) this.osmObjectDownloader = new OsmObjectDownloader( this.osmConnection.Backend(), @@ -345,7 +348,7 @@ export default class ThemeViewState implements SpecialVisualizationState { { currentZoom: this.mapProperties.zoom, layerState: this.layerState, - bounds: this.visualFeedbackViewportBounds, + bounds: this.visualFeedbackViewportBounds } ) this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView @@ -403,6 +406,7 @@ export default class ThemeViewState implements SpecialVisualizationState { }) return toLocalStorage } + public showNormalDataOn(map: Store): ReadonlyMap { const filteringFeatureSource = new Map() this.perLayer.forEach((fs, layerName) => { @@ -436,7 +440,7 @@ export default class ThemeViewState implements SpecialVisualizationState { doShowLayer, metaTags: this.userRelatedState.preferencesAsTags, selectedElement: this.selectedElement, - fetchStore: (id) => this.featureProperties.getStore(id), + fetchStore: (id) => this.featureProperties.getStore(id) }) }) return filteringFeatureSource @@ -460,7 +464,7 @@ export default class ThemeViewState implements SpecialVisualizationState { doShowLayer: flayerGps.isDisplayed, layer: flayerGps.layerDef, metaTags: this.userRelatedState.preferencesAsTags, - selectedElement: this.selectedElement, + selectedElement: this.selectedElement }) } @@ -552,7 +556,7 @@ export default class ThemeViewState implements SpecialVisualizationState { Hotkeys.RegisterHotkey( { nomod: " ", - onUp: true, + onUp: true }, docs.selectItem, () => { @@ -582,7 +586,7 @@ export default class ThemeViewState implements SpecialVisualizationState { Hotkeys.RegisterHotkey( { nomod: "" + i, - onUp: true, + onUp: true }, doc, () => this.selectClosestAtCenter(i - 1) @@ -595,7 +599,7 @@ export default class ThemeViewState implements SpecialVisualizationState { } Hotkeys.RegisterHotkey( { - nomod: "b", + nomod: "b" }, docs.openLayersPanel, () => { @@ -606,7 +610,7 @@ export default class ThemeViewState implements SpecialVisualizationState { ) Hotkeys.RegisterHotkey( { - nomod: "s", + nomod: "s" }, Translations.t.hotkeyDocumentation.openFilterPanel, () => { @@ -664,7 +668,7 @@ export default class ThemeViewState implements SpecialVisualizationState { Hotkeys.RegisterHotkey( { - shift: "T", + shift: "T" }, Translations.t.hotkeyDocumentation.translationMode, () => { @@ -696,7 +700,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.mapProperties.zoom.map((z) => Math.max(Math.floor(z), 0)), this.mapProperties, { - isActive: this.mapProperties.zoom.map((z) => z <= maxzoom), + isActive: this.mapProperties.zoom.map((z) => z <= maxzoom) } ) return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers) @@ -712,7 +716,7 @@ export default class ThemeViewState implements SpecialVisualizationState { * A listing which maps the layerId onto the featureSource */ const specialLayers: Record< - Exclude | "current_view", + AddedByDefaultTypes | "current_view", FeatureSource > = { home_location: this.userRelatedState.homeLocation, @@ -730,6 +734,7 @@ export default class ThemeViewState implements SpecialVisualizationState { current_view: this.currentView, favourite: this.favourites, summary: this.featureSummary, + last_click: this.lastClickObject } this.closestFeatures.registerSource(specialLayers.favourite, "favourite") @@ -774,7 +779,7 @@ export default class ThemeViewState implements SpecialVisualizationState { if (features === undefined) { return } - if (id === "summary") { + if (id === "summary" || id === "last_click") { return } @@ -784,7 +789,7 @@ export default class ThemeViewState implements SpecialVisualizationState { doShowLayer: flayer.isDisplayed, layer: flayer.layerDef, metaTags: this.userRelatedState.preferencesAsTags, - selectedElement: this.selectedElement, + selectedElement: this.selectedElement }) }) @@ -792,7 +797,33 @@ export default class ThemeViewState implements SpecialVisualizationState { features: specialLayers.summary, layer: new LayerConfig(summaryLayer, "summaryLayer"), // doShowLayer: this.mapProperties.zoom.map((z) => z < maxzoom), - selectedElement: this.selectedElement, + selectedElement: this.selectedElement + }) + + const lastClickLayerConfig = new LayerConfig(last_click_layerconfig, "last_click") + const lastClickFiltered = + lastClickLayerConfig.isShown === undefined ? specialLayers.last_click : + specialLayers.last_click.features.mapD(fs => + fs.filter( + f => { + const matches = lastClickLayerConfig.isShown.matchesProperties(f.properties) + console.log("LastClick ",f,"matches",matches) + return matches + })) + new ShowDataLayer(this.map, { + features: new StaticFeatureSource(lastClickFiltered), + layer: lastClickLayerConfig, + onClick: feature => { + console.log("Last click was clicked", feature) + if (this.mapProperties.zoom.data >= Constants.minZoomLevelToAddNewPoint) { + this.selectedElement.setData(feature) + return + } + this.map.data.flyTo({ + zoom: Constants.minZoomLevelToAddNewPoint, + center: GeoOperations.centerpointCoordinates(feature) + }) + } }) } diff --git a/src/UI/Map/DynamicMarker.svelte b/src/UI/Map/DynamicMarker.svelte index a87807ad7f..2f6b1cb905 100644 --- a/src/UI/Map/DynamicMarker.svelte +++ b/src/UI/Map/DynamicMarker.svelte @@ -15,7 +15,7 @@ /** * Only used in case of emoji */ - export let emojiHeight: number + export let emojiHeight: number = undefined let _rotation: Store = rotation ? tags.map((tags) => rotation.GetRenderValue(tags).Subs(tags).txt) : new ImmutableStore("0deg") diff --git a/src/UI/Map/MapLibreAdaptor.ts b/src/UI/Map/MapLibreAdaptor.ts index ace756d117..648ca7572c 100644 --- a/src/UI/Map/MapLibreAdaptor.ts +++ b/src/UI/Map/MapLibreAdaptor.ts @@ -1,7 +1,5 @@ import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" -import { Map as MLMap } from "maplibre-gl" -import { Map as MlMap, SourceSpecification } from "maplibre-gl" -import maplibregl from "maplibre-gl" +import maplibregl, { Map as MLMap, Map as MlMap, SourceSpecification } from "maplibre-gl" import { RasterLayerPolygon } from "../../Models/RasterLayers" import { Utils } from "../../Utils" import { BBox } from "../../Logic/BBox" @@ -13,7 +11,6 @@ import * as htmltoimage from "html-to-image" import RasterLayerHandler from "./RasterLayerHandler" import Constants from "../../Models/Constants" import { Protocol } from "pmtiles" -import { bool } from "sharp" /** * The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties` @@ -42,7 +39,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { readonly allowMoving: UIEventSource readonly allowRotating: UIEventSource readonly allowZooming: UIEventSource - readonly lastClickLocation: Store + readonly lastClickLocation: Store readonly minzoom: UIEventSource readonly maxzoom: UIEventSource readonly rotation: UIEventSource @@ -95,20 +92,24 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { this.rasterLayer = state?.rasterLayer ?? new UIEventSource(undefined) - const lastClickLocation = new UIEventSource<{ lon: number; lat: number }>(undefined) + const lastClickLocation = new UIEventSource<{lat:number,lon:number,mode: "left" | "right" | "middle"}>(undefined) this.lastClickLocation = lastClickLocation const self = this new RasterLayerHandler(this._maplibreMap, this.rasterLayer) - function handleClick(e) { + const clickmodes = ["left" , "middle", "right"] as const + function handleClick(e: maplibregl.MapMouseEvent, mode?: "left" | "right" | "middle") { if (e.originalEvent["consumed"]) { // Workaround, 'ShowPointLayer' sets this flag return } const lon = e.lngLat.lng const lat = e.lngLat.lat - lastClickLocation.setData({ lon, lat }) + const mouseEvent: MouseEvent = e.originalEvent + mode = mode ?? clickmodes[mouseEvent.button] + + lastClickLocation.setData({ lon, lat, mode }) } maplibreMap.addCallbackAndRunD((map) => { @@ -142,10 +143,19 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { handleClick(e) }) map.on("contextmenu", (e) => { - handleClick(e) + // This one only works on desktop + handleClick(e, "right") + }) + + map._container.addEventListener("contextmenu", (e) => { + const lngLat = map.unproject([e.x, e.y]) + lastClickLocation.setData({lon: lngLat.lng, lat: lngLat.lat, mode: "right"}) }) map.on("dblclick", (e) => { - handleClick(e) + handleClick(e, "left") + }) + map.on("touchend", (e) => { + const touchEvent = e.originalEvent }) map.on("rotateend", (_) => { this.updateStores() @@ -665,4 +675,5 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { } } } + } diff --git a/src/UI/Map/MaplibreMap.svelte b/src/UI/Map/MaplibreMap.svelte index fe34d6acd6..220c2fb430 100644 --- a/src/UI/Map/MaplibreMap.svelte +++ b/src/UI/Map/MaplibreMap.svelte @@ -27,6 +27,7 @@ let _map: Map onMount(() => { + const { lon, lat } = mapProperties?.location?.data ?? { lon: 0, lat: 0 } const rasterLayer: RasterLayerProperties = mapProperties?.rasterLayer?.data?.properties @@ -72,8 +73,13 @@ }) onDestroy(async () => { await Utils.waitFor(250) + try{ + if (_map) _map.remove() map = null + }catch (e) { + console.error("Could not destroy map") + } }) diff --git a/src/UI/Map/RasterLayerHandler.ts b/src/UI/Map/RasterLayerHandler.ts index 933a130cd7..6cbdb636ea 100644 --- a/src/UI/Map/RasterLayerHandler.ts +++ b/src/UI/Map/RasterLayerHandler.ts @@ -122,7 +122,7 @@ class SingleBackgroundHandler { console.warn( "Layer", addLayerBeforeId, - "not foundhttp://127.0.0.1:1234/theme.html?layout=cyclofix&z=14.8&lat=51.05282501324558&lon=3.720591622281745&layer-range=true" + "not found" ) addLayerBeforeId = undefined } diff --git a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte index 8fdaa6d520..62bbaf3afb 100644 --- a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte @@ -118,7 +118,7 @@ {/if} {:else} -
+
{/if}