selected_element layer which highlights the selected element
This commit is contained in:
parent
e8ff43312f
commit
42bd301389
13 changed files with 146 additions and 32 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ export default class Constants {
|
|||
]
|
||||
|
||||
public static readonly added_by_default: string[] = [
|
||||
"selected_element",
|
||||
"gps_location",
|
||||
"gps_location_history",
|
||||
"home_location",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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 })
|
||||
})
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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)
|
||||
|
|
20
assets/layers/selected_element/selected_element.json
Normal file
20
assets/layers/selected_element/selected_element.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue