selected_element layer which highlights the selected element

This commit is contained in:
Pieter Vander Vennet 2022-12-09 13:58:41 +01:00
parent e8ff43312f
commit 42bd301389
13 changed files with 146 additions and 32 deletions

View file

@ -160,6 +160,11 @@ export default class FeaturePipeline {
continue
}
if (id === "selected_element") {
handlePriviligedFeatureSource(state.selectedElementsLayer)
continue
}
if (id === "gps_location") {
handlePriviligedFeatureSource(state.currentUserLocation)
continue

View file

@ -1,29 +1,29 @@
import UserRelatedState from "./UserRelatedState"
import { Store, Stores, UIEventSource } from "../UIEventSource"
import {Store, Stores, UIEventSource} from "../UIEventSource"
import BaseLayer from "../../Models/BaseLayer"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import AvailableBaseLayers from "../Actors/AvailableBaseLayers"
import Attribution from "../../UI/BigComponents/Attribution"
import Minimap, { MinimapObj } from "../../UI/Base/Minimap"
import { Tiles } from "../../Models/TileRange"
import Minimap, {MinimapObj} from "../../UI/Base/Minimap"
import {Tiles} from "../../Models/TileRange"
import BaseUIElement from "../../UI/BaseUIElement"
import FilteredLayer, { FilterState } from "../../Models/FilteredLayer"
import FilteredLayer, {FilterState} from "../../Models/FilteredLayer"
import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"
import { QueryParameters } from "../Web/QueryParameters"
import {QueryParameters} from "../Web/QueryParameters"
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer"
import { FeatureSourceForLayer, Tiled } from "../FeatureSource/FeatureSource"
import {FeatureSourceForLayer, Tiled} from "../FeatureSource/FeatureSource"
import SimpleFeatureSource from "../FeatureSource/Sources/SimpleFeatureSource"
import { LocalStorageSource } from "../Web/LocalStorageSource"
import { GeoOperations } from "../GeoOperations"
import {LocalStorageSource} from "../Web/LocalStorageSource"
import {GeoOperations} from "../GeoOperations"
import TitleHandler from "../Actors/TitleHandler"
import { BBox } from "../BBox"
import {BBox} from "../BBox"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { TiledStaticFeatureSource } from "../FeatureSource/Sources/StaticFeatureSource"
import { Translation, TypedTranslation } from "../../UI/i18n/Translation"
import { Tag } from "../Tags/Tag"
import { OsmConnection } from "../Osm/OsmConnection"
import { Feature, GeoJSON, LineString } from "geojson"
import { OsmTags } from "../../Models/OsmFeature"
import {TiledStaticFeatureSource} from "../FeatureSource/Sources/StaticFeatureSource"
import {Translation, TypedTranslation} from "../../UI/i18n/Translation"
import {Tag} from "../Tags/Tag"
import {OsmConnection} from "../Osm/OsmConnection"
import {Feature, LineString} from "geojson"
import {OsmTags} from "../../Models/OsmFeature"
export interface GlobalFilter {
filter: FilterState
@ -93,6 +93,13 @@ export default class MapState extends UserRelatedState {
*/
public homeLocation: FeatureSourceForLayer & Tiled
/**
* A builtin layer which contains the selected element.
* Loads 'selected_element.json'
* This _might_ contain multiple points, e.g. every center of a multipolygon
*/
public selectedElementsLayer: FeatureSourceForLayer & Tiled
public readonly mainMapObject: BaseUIElement & MinimapObj
/**
@ -168,6 +175,7 @@ export default class MapState extends UserRelatedState {
this.initGpsLocation()
this.initUserLocationTrail()
this.initCurrentView()
this.initSelectedElement()
new TitleHandler(this)
}
@ -249,7 +257,7 @@ export default class MapState extends UserRelatedState {
private initGpsLocation() {
// Initialize the gps layer data. This is emtpy for now, the actual writing happens in the Geolocationhandler
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(
const gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(
(l) => l.layerDef.id === "gps_location"
)[0]
if (gpsLayerDef === undefined) {
@ -258,6 +266,29 @@ export default class MapState extends UserRelatedState {
this.currentUserLocation = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0))
}
private initSelectedElement(){
const layerDef: FilteredLayer = this.filteredLayers.data.filter(
(l) => l.layerDef.id === "selected_element"
)[0]
const empty = []
const store = this.selectedElement.map(feature => {
if(feature === undefined || feature === null){
return empty
}
return [{
feature: {
type:"Feature",
properties: {
selected: "yes",
id: "selected" + feature.properties.id
},
geometry:feature.geometry
}
, freshness: new Date()}];
});
this.selectedElementsLayer = new TiledStaticFeatureSource(store,layerDef);
}
private initUserLocationTrail() {
const features = LocalStorageSource.GetParsed<{ feature: any; freshness: Date }[]>(
"gps_location_history",

View file

@ -127,7 +127,8 @@ export default class MangroveReviews {
this._lastUpdate = new Date()
const self = this
mangrove.getReviews({ sub: this.GetSubjectUri() }).then((data) => {
mangrove.getReviews({ sub: this.GetSubjectUri() })
.then((data) => {
const reviews = []
const reviewsByUser = []
for (const review of data.reviews) {
@ -153,6 +154,9 @@ export default class MangroveReviews {
}
self._reviews.setData(reviewsByUser.concat(reviews))
})
.catch(e => {
console.error("Could not download review for ", e);
})
return this._reviews
}

View file

@ -27,6 +27,7 @@ export default class Constants {
]
public static readonly added_by_default: string[] = [
"selected_element",
"gps_location",
"gps_location_history",
"home_location",

View file

@ -64,4 +64,15 @@ export default interface PointRenderingConfigJson {
* Note that, if the wayhandling hides the icon then no label is shown as well.
*/
label?: string | TagRenderingConfigJson
/**
* A snippet of css code
*/
css?: string | TagRenderingConfigJson
/**
* A snippet of css-classes. They can be space-separated
*/
cssClasses?: string | TagRenderingConfigJson
}

View file

@ -301,7 +301,7 @@ export default class LayerConfig extends WithContextLoader {
const hasCenterRendering = this.mapRendering.some(
(r) =>
r.location.has("centroid") || r.location.has("start") || r.location.has("end")
r.location.has("centroid") || r.location.has("projected_centerpoint") || r.location.has("start") || r.location.has("end")
)
if (this.lineRendering.length === 0 && this.mapRendering.length === 0) {

View file

@ -12,6 +12,7 @@ import { FixedUiElement } from "../../UI/Base/FixedUiElement"
import Img from "../../UI/Base/Img"
import Combine from "../../UI/Base/Combine"
import { VariableUiElement } from "../../UI/Base/VariableUIElement"
import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson";
export default class PointRenderingConfig extends WithContextLoader {
private static readonly allowed_location_codes = new Set<string>([
@ -25,11 +26,13 @@ export default class PointRenderingConfig extends WithContextLoader {
"point" | "centroid" | "start" | "end" | "projected_centerpoint" | string
>
public readonly icon: TagRenderingConfig
public readonly icon?: TagRenderingConfig
public readonly iconBadges: { if: TagsFilter; then: TagRenderingConfig }[]
public readonly iconSize: TagRenderingConfig
public readonly label: TagRenderingConfig
public readonly rotation: TagRenderingConfig
public readonly cssDef: TagRenderingConfig
public readonly cssClasses?: TagRenderingConfig
constructor(json: PointRenderingConfigJson, context: string) {
super(json, context)
@ -61,6 +64,10 @@ export default class PointRenderingConfig extends WithContextLoader {
)
}
this.icon = this.tr("icon", undefined)
if(json.css !== undefined){
this.cssDef = this.tr("css", undefined)
}
this.cssClasses = this.tr("cssClasses", undefined)
this.iconBadges = (json.iconBadges ?? []).map((overlay, i) => {
let tr: TagRenderingConfig
if (
@ -240,6 +247,9 @@ export default class PointRenderingConfig extends WithContextLoader {
iconAndBadges.SetClass("w-full h-full")
}
const css= this.cssDef?.GetRenderValue(tags , undefined)?.txt
const cssClasses = this.cssClasses?.GetRenderValue(tags , undefined)?.txt
let label = this.GetLabel(tags)
let htmlEl: BaseUIElement
if (icon === undefined && label === undefined) {
@ -252,6 +262,13 @@ export default class PointRenderingConfig extends WithContextLoader {
htmlEl = new Combine([iconAndBadges, label]).SetStyle("flex flex-col")
}
if(css !== undefined){
htmlEl?.SetStyle(css)
}
if(cssClasses !== undefined){
htmlEl?.SetClass(cssClasses)
}
return {
html: htmlEl,
iconSize: [iconW, iconH],

View file

@ -5,7 +5,7 @@ import Loc from "../../Models/Loc"
import BaseLayer from "../../Models/BaseLayer"
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
import * as L from "leaflet"
import { Map } from "leaflet"
import {LeafletMouseEvent, Map} from "leaflet"
import Minimap, { MinimapObj, MinimapOptions } from "./Minimap"
import { BBox } from "../../Logic/BBox"
import "leaflet-polylineoffset"
@ -316,8 +316,10 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
if (this._options.lastClickLocation) {
const lastClickLocation = this._options.lastClickLocation
map.on("click", function (e) {
// @ts-ignore
map.on("click", function (e: LeafletMouseEvent) {
if(e.originalEvent["dismissed"] ){
return
}
lastClickLocation?.setData({ lat: e.latlng.lat, lon: e.latlng.lng })
})

View file

@ -71,18 +71,22 @@ export default class ScrollableFullScreen {
self.Activate()
} else {
// Some cleanup...
ScrollableFullScreen.collapse()
const fs = document.getElementById("fullscreen")
if (fs !== null) {
ScrollableFullScreen.empty.AttachTo("fullscreen")
fs.classList.add("hidden")
}
ScrollableFullScreen._currentlyOpen?.isShown?.setData(false)
}
})
}
public static collapse(){
const fs = document.getElementById("fullscreen")
if (fs !== null) {
ScrollableFullScreen.empty.AttachTo("fullscreen")
fs.classList.add("hidden")
}
ScrollableFullScreen._currentlyOpen?.isShown?.setData(false)
}
Destroy() {
this._fullscreencomponent.Destroy()
}
@ -99,6 +103,7 @@ export default class ScrollableFullScreen {
fs.classList.remove("hidden")
}
private BuildComponent(title: BaseUIElement, content: BaseUIElement): BaseUIElement {
const returnToTheMap = new Combine([
Svg.back_svg().SetClass("block md:hidden w-12 h-12 p-2 svg-foreground"),

View file

@ -123,6 +123,9 @@ export default class DefaultGUI {
addNewPoint,
hasPresets ? new AddNewMarker(state.filteredLayers) : noteMarker
)
state.LastClickLocation.addCallbackAndRunD(_ => {
ScrollableFullScreen.collapse()
})
}
if (noteLayer !== undefined) {
@ -153,6 +156,16 @@ export default class DefaultGUI {
state,
})
const selectedElement: FilteredLayer = state.filteredLayers.data.filter(
(l) => l.layerDef.id === "selected_element"
)[0]
new ShowDataLayer({
leafletMap: state.leafletMap,
layerToShow: selectedElement.layerDef,
features: state.selectedElementsLayer,
state
})
state.leafletMap.addCallbackAndRunD((_) => {
// Lets assume that all showDataLayers are initialized at this point
state.selectedElement.ping()

View file

@ -684,7 +684,6 @@ export default class TagRenderingQuestion extends Combine {
const tagsData = tags.data
const feature = state?.allElements?.ContainingFeatures?.get(tagsData.id)
const center = feature != undefined ? GeoOperations.centerpointCoordinates(feature) : [0, 0]
console.log("Creating a tr-question with applicableUnit", applicableUnit)
const input: InputElement<string> = ValidatedTextField.ForType(
configuration.freeform.type
)?.ConstructInputElement({

View file

@ -286,7 +286,6 @@ export default class ShowDataLayerImplementation {
// Leaflet cannot handle geojson points natively
// We have to convert them to the appropriate icon
// Click handling is done in the next step
const layer: LayerConfig = this._layerToShow
if (layer === undefined) {
return
@ -337,7 +336,6 @@ export default class ShowDataLayerImplementation {
const self = this
function activate (event: LeafletMouseEvent) {
console.log("Activating!")
if (infobox === undefined) {
const tags =
self.allElements?.getEventSourceById(key) ??
@ -350,6 +348,14 @@ export default class ShowDataLayerImplementation {
})
}
infobox.Activate()
self._selectedElement.setData( self.allElements.ContainingFeatures.get(feature.id) ?? feature )
event?.originalEvent?.preventDefault()
event?.originalEvent?.stopPropagation()
event?.originalEvent?.stopImmediatePropagation()
if(event?.originalEvent){
// This is a total workaround, as 'preventDefault' and everything above seems to be not working
event.originalEvent["dismissed"] = true
}
}
leafletLayer.addEventListener('click', activate)

View file

@ -0,0 +1,20 @@
{
"id": "selected_element",
"description": {
"en": "Highlights the currently selected element. Override this layer to have different colors",
"nl": "Toont het geselecteerde element"
},
"source": {
"osmTags": "selected=yes",
"maxCacheAge": 0
},
"mapRendering": [
{
"icon": "circle:red",
"iconSize": "1,1,center",
"location": ["point","projected_centerpoint"],
"css": "box-shadow: red 0 0 20px 20px; z-index: -1; height: 1px; width: 1px;",
"cssClasses": "block relative rounded-full"
}
]
}