Fix: tweaking addNewPoint-flow

This commit is contained in:
Pieter Vander Vennet 2023-05-19 01:37:31 +02:00
parent 63ffa11238
commit d0e0abdece
8 changed files with 149 additions and 62 deletions

View file

@ -701,6 +701,20 @@ export class GeoOperations {
return turf.bearing(a, b) return turf.bearing(a, b)
} }
public static along(a: Coord, b: Coord, distanceMeter: number): Coord {
return turf.along(
<any> {
type:"Feature",
geometry:{
type:"LineString",
coordinates: [a, b]
}
}, distanceMeter, {units: "meters"}
).geometry.coordinates
}
/** /**
* Returns 'true' if one feature contains the other feature * Returns 'true' if one feature contains the other feature
* *

View file

@ -31,14 +31,11 @@ export default interface LineRenderingConfigJson {
*/ */
lineCap?: "round" | "square" | "butt" | string | TagRenderingConfigJson lineCap?: "round" | "square" | "butt" | string | TagRenderingConfigJson
/**
* Whether or not to fill polygons
*/
fill?: "yes" | "no" | TagRenderingConfigJson
/** /**
* The color to fill a polygon with. * The color to fill a polygon with.
* If undefined, this will be slightly more opaque version of the stroke line * If undefined, this will be slightly more opaque version of the stroke line.
* Use '#00000000' to make the fill invisible
*/ */
fillColor?: string | TagRenderingConfigJson fillColor?: string | TagRenderingConfigJson

View file

@ -8,7 +8,6 @@
import type {MapProperties} from "../../Models/MapProperties"; import type {MapProperties} from "../../Models/MapProperties";
import ShowDataLayer from "../Map/ShowDataLayer"; import ShowDataLayer from "../Map/ShowDataLayer";
import type {FeatureSource, FeatureSourceForLayer} from "../../Logic/FeatureSource/FeatureSource"; import type {FeatureSource, FeatureSourceForLayer} from "../../Logic/FeatureSource/FeatureSource";
import SnappingFeatureSource from "../../Logic/FeatureSource/Sources/SnappingFeatureSource"; import SnappingFeatureSource from "../../Logic/FeatureSource/Sources/SnappingFeatureSource";
import FeatureSourceMerger from "../../Logic/FeatureSource/Sources/FeatureSourceMerger"; import FeatureSourceMerger from "../../Logic/FeatureSource/Sources/FeatureSourceMerger";
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
@ -31,6 +30,7 @@
export let maxSnapDistance: number = undefined; export let maxSnapDistance: number = undefined;
export let snappedTo: UIEventSource<string | undefined>; export let snappedTo: UIEventSource<string | undefined>;
export let value: UIEventSource<{ lon: number, lat: number }>; export let value: UIEventSource<{ lon: number, lat: number }>;
if (value.data === undefined) { if (value.data === undefined) {
value.setData(coordinate); value.setData(coordinate);
@ -39,7 +39,8 @@
let preciseLocation: UIEventSource<{ lon: number, lat: number }> = new UIEventSource<{ let preciseLocation: UIEventSource<{ lon: number, lat: number }> = new UIEventSource<{
lon: number; lon: number;
lat: number lat: number
}>(coordinate); }>(undefined);
const xyz = Tiles.embedded_tile(coordinate.lat, coordinate.lon, 16); const xyz = Tiles.embedded_tile(coordinate.lat, coordinate.lon, 16);
const map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined); const map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
let initialMapProperties: Partial<MapProperties> = { let initialMapProperties: Partial<MapProperties> = {
@ -52,14 +53,19 @@
bounds: new UIEventSource<BBox>(undefined), bounds: new UIEventSource<BBox>(undefined),
allowMoving: new UIEventSource<boolean>(true), allowMoving: new UIEventSource<boolean>(true),
allowZooming: new UIEventSource<boolean>(true), allowZooming: new UIEventSource<boolean>(true),
minzoom: new UIEventSource<number>(18) minzoom: new UIEventSource<number>(18),
rasterLayer: UIEventSource.feedFrom(state.mapProperties.rasterLayer)
}; };
initialMapProperties.bounds.addCallbackAndRunD((bounds: BBox) => {
const max = bounds.pad(3).squarify(); const featuresForLayer = state.perLayer.get(targetLayer.id)
initialMapProperties.maxbounds.setData(max); if(featuresForLayer){
return true; // unregister new ShowDataLayer(map, {
}); layer: targetLayer,
features: featuresForLayer
})
}
if (snapToLayers?.length > 0) { if (snapToLayers?.length > 0) {
@ -99,4 +105,4 @@
<LocationInput {map} mapProperties={initialMapProperties} <LocationInput {map} mapProperties={initialMapProperties}
value={preciseLocation}></LocationInput> value={preciseLocation} initialCoordinate={{...coordinate}} maxDistanceInMeters=50 />

View file

@ -1,38 +1,82 @@
<script lang="ts"> <script lang="ts">
import { Store, UIEventSource } from "../../../Logic/UIEventSource"; import {Store, UIEventSource} from "../../../Logic/UIEventSource";
import type { MapProperties } from "../../../Models/MapProperties"; import type {MapProperties} from "../../../Models/MapProperties";
import { Map as MlMap } from "maplibre-gl"; import {Map as MlMap} from "maplibre-gl";
import { MapLibreAdaptor } from "../../Map/MapLibreAdaptor"; import {MapLibreAdaptor} from "../../Map/MapLibreAdaptor";
import MaplibreMap from "../../Map/MaplibreMap.svelte"; import MaplibreMap from "../../Map/MaplibreMap.svelte";
import DragInvitation from "../../Base/DragInvitation.svelte"; import DragInvitation from "../../Base/DragInvitation.svelte";
import {GeoOperations} from "../../../Logic/GeoOperations";
import ShowDataLayer from "../../Map/ShowDataLayer";
import * as boundsdisplay from "../../../assets/generated/layers/range.json"
import StaticFeatureSource from "../../../Logic/FeatureSource/Sources/StaticFeatureSource";
import * as turf from "@turf/turf"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
import {onDestroy} from "svelte";
/** /**
* A visualisation to pick a direction on a map background * A visualisation to pick a direction on a map background
*/ */
export let value: UIEventSource<{lon: number, lat: number}>; export let value: UIEventSource<{ lon: number, lat: number }>;
export let mapProperties: Partial<MapProperties> & { readonly location: UIEventSource<{ lon: number; lat: number }> } = undefined; export let initialCoordinate : {lon: number, lat :number}
/** initialCoordinate = initialCoordinate ?? value .data
* Called when setup is done, can be used to add more layers to the map export let maxDistanceInMeters: number = undefined
*/ export let mapProperties: Partial<MapProperties> & {
export let onCreated : (value: Store<{lon: number, lat: number}> , map: Store<MlMap>, mapProperties: MapProperties ) => void = undefined readonly location: UIEventSource<{ lon: number; lat: number }>
} = undefined;
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined); /**
let mla = new MapLibreAdaptor(map, mapProperties); * Called when setup is done, can be used to add more layers to the map
mapProperties.location.syncWith(value) */
if(onCreated){ export let onCreated: (value: Store<{
onCreated(value, map, mla) lon: number,
} lat: number
}>, map: Store<MlMap>, mapProperties: MapProperties) => void = undefined
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
let mla = new MapLibreAdaptor(map, mapProperties);
mapProperties.location.syncWith(value)
if (onCreated) {
onCreated(value, map, mla)
}
let rangeIsShown = false
if (maxDistanceInMeters) {
onDestroy(mla.location.addCallbackD(newLocation => {
const l = [newLocation.lon, newLocation.lat]
const c: [number, number] = [initialCoordinate.lon, initialCoordinate.lat]
const d = GeoOperations.distanceBetween(l, c)
console.log("distance is", d, l, c)
if (d <= maxDistanceInMeters) {
return
}
// This is too far away - let's move back
const correctLocation = GeoOperations.along(c, l, maxDistanceInMeters - 10)
window.setTimeout(() => {
mla.location.setData({lon: correctLocation[0], lat: correctLocation[1]})
}, 25)
if (!rangeIsShown) {
new ShowDataLayer(map, {
layer: new LayerConfig(boundsdisplay),
features: new StaticFeatureSource(
[turf.circle(c, maxDistanceInMeters, {units: "meters", properties: {"range":"yes", id: "0"}}, )]
)
})
rangeIsShown = true
}
}))
}
</script> </script>
<div class="relative h-full min-h-32 cursor-pointer overflow-hidden"> <div class="relative h-full min-h-32 cursor-pointer overflow-hidden">
<div class="w-full h-full absolute top-0 left-0 cursor-pointer"> <div class="w-full h-full absolute top-0 left-0 cursor-pointer">
<MaplibreMap {map} attribution={false}></MaplibreMap> <MaplibreMap {map}/>
</div> </div>
<div class="w-full h-full absolute top-0 left-0 p-8 pointer-events-none opacity-50 flex items-center"> <div class="w-full h-full absolute top-0 left-0 p-8 pointer-events-none opacity-50 flex items-center">
<img src="./assets/svg/move-arrows.svg" class="h-full max-h-24"/> <img class="h-full max-h-24" src="./assets/svg/move-arrows.svg"/>
</div> </div>
<DragInvitation hideSignal={mla.location.stabilized(3000)}></DragInvitation> <DragInvitation hideSignal={mla.location.stabilized(3000)}></DragInvitation>
</div> </div>

View file

@ -228,30 +228,46 @@ class LineRenderingLayer {
features.features.addCallbackAndRunD(() => self.update(features.features)) features.features.addCallbackAndRunD(() => self.update(features.features))
} }
public destruct(): void {
this._map.removeLayer(this._layername + "_polygon")
}
/**
* Calculate the feature-state for maplibre
* @param properties
* @private
*/
private calculatePropsFor( private calculatePropsFor(
properties: Record<string, string> properties: Record<string, string>
): Partial<Record<typeof LineRenderingLayer.lineConfigKeys[number], string>> { ): Partial<Record<typeof LineRenderingLayer.lineConfigKeys[number], string>> {
const calculatedProps = {}
const config = this._config const config = this._config
const calculatedProps: Record<string, string | number> = {}
for (const key of LineRenderingLayer.lineConfigKeys) { for (const key of LineRenderingLayer.lineConfigKeys) {
calculatedProps[key] = config[key]?.GetRenderValue(properties)?.Subs(properties).txt calculatedProps[key] = config[key]?.GetRenderValue(properties)?.Subs(properties).txt
} }
calculatedProps.fillColor = calculatedProps.fillColor ?? calculatedProps.lineColor
for (const key of LineRenderingLayer.lineConfigKeysColor) { for (const key of LineRenderingLayer.lineConfigKeysColor) {
let v = config[key]?.GetRenderValue(properties)?.Subs(properties).txt let v = <string>calculatedProps[key]
if (v === undefined) { if (v === undefined) {
continue continue
} }
if (v.length == 9 && v.startsWith("#")) { if (v.length == 9 && v.startsWith("#")) {
// This includes opacity // This includes opacity
calculatedProps[key + "-opacity"] = parseInt(v.substring(7), 16) / 256 calculatedProps[`${key}-opacity`] = parseInt(v.substring(7), 16) / 256
v = v.substring(0, 7) v = v.substring(0, 7)
if (v.length == 9 && v.startsWith("#")) {
// This includes opacity
calculatedProps[`${key}-opacity`] = parseInt(v.substring(7), 16) / 256
v = v.substring(0, 7)
}
} }
calculatedProps[key] = v
} }
calculatedProps["fillColor-opacity"] = calculatedProps["fillColor-opacity"] ?? 0.1
for (const key of LineRenderingLayer.lineConfigKeysNumber) { for (const key of LineRenderingLayer.lineConfigKeysNumber) {
const v = config[key]?.GetRenderValue(properties)?.Subs(properties).txt calculatedProps[key] = Number(calculatedProps[key])
calculatedProps[key] = Number(v)
} }
return calculatedProps return calculatedProps
@ -303,6 +319,7 @@ class LineRenderingLayer {
this._onClick(e.features[0]) this._onClick(e.features[0])
}) })
const polylayer = this._layername + "_polygon" const polylayer = this._layername + "_polygon"
map.addLayer({ map.addLayer({
source: this._layername, source: this._layername,
id: polylayer, id: polylayer,
@ -311,13 +328,15 @@ class LineRenderingLayer {
layout: {}, layout: {},
paint: { paint: {
"fill-color": ["feature-state", "fillColor"], "fill-color": ["feature-state", "fillColor"],
"fill-opacity": 0.1, "fill-opacity": ["feature-state", "fillColor-opacity"],
}, },
}) })
map.on("click", polylayer, (e) => { if (this._onClick) {
e.originalEvent["consumed"] = true map.on("click", polylayer, (e) => {
this._onClick(e.features[0]) e.originalEvent["consumed"] = true
}) this._onClick(e.features[0])
})
}
this._visibility?.addCallbackAndRunD((visible) => { this._visibility?.addCallbackAndRunD((visible) => {
try { try {
@ -388,6 +407,8 @@ export default class ShowDataLayer {
drawLines?: true | boolean drawLines?: true | boolean
} }
private onDestroy: (() => void)[] = []
constructor( constructor(
map: Store<MlMap>, map: Store<MlMap>,
options: ShowDataLayerOptions & { options: ShowDataLayerOptions & {
@ -399,7 +420,7 @@ export default class ShowDataLayer {
this._map = map this._map = map
this._options = options this._options = options
const self = this const self = this
map.addCallbackAndRunD((map) => self.initDrawFeatures(map)) this.onDestroy.push(map.addCallbackAndRunD((map) => self.initDrawFeatures(map)))
} }
public static showMultipleLayers( public static showMultipleLayers(
@ -437,6 +458,10 @@ export default class ShowDataLayer {
}) })
} }
public destruct() {
}
private zoomToCurrentFeatures(map: MlMap) { private zoomToCurrentFeatures(map: MlMap) {
if (this._options.zoomToFeatures) { if (this._options.zoomToFeatures) {
const features = this._options.features.features.data const features = this._options.features.features.data
@ -458,7 +483,7 @@ export default class ShowDataLayer {
if (this._options.drawLines !== false) { if (this._options.drawLines !== false) {
for (let i = 0; i < this._options.layer.lineRendering.length; i++) { for (let i = 0; i < this._options.layer.lineRendering.length; i++) {
const lineRenderingConfig = this._options.layer.lineRendering[i] const lineRenderingConfig = this._options.layer.lineRendering[i]
new LineRenderingLayer( const l = new LineRenderingLayer(
map, map,
features, features,
this._options.layer.id + "_linerendering_" + i, this._options.layer.id + "_linerendering_" + i,
@ -467,6 +492,7 @@ export default class ShowDataLayer {
fetchStore, fetchStore,
onClick onClick
) )
this.onDestroy.push(l.destruct)
} }
} }
if (this._options.drawMarkers !== false) { if (this._options.drawMarkers !== false) {

View file

@ -31,6 +31,7 @@
import BackButton from "../../Base/BackButton.svelte"; import BackButton from "../../Base/BackButton.svelte";
import ToSvelte from "../../Base/ToSvelte.svelte"; import ToSvelte from "../../Base/ToSvelte.svelte";
import Svg from "../../../Svg"; import Svg from "../../../Svg";
import RasterLayerOverview from "../../Map/RasterLayerOverview.svelte";
export let coordinate: { lon: number, lat: number }; export let coordinate: { lon: number, lat: number };
export let state: SpecialVisualizationState; export let state: SpecialVisualizationState;
@ -281,6 +282,9 @@
</div> </div>
</NextButton> </NextButton>
</div> </div>
<RasterLayerOverview />
{:else} {:else}
<Loading>Creating point...</Loading> <Loading>Creating point...</Loading>
{/if} {/if}

View file

@ -77,7 +77,7 @@
], ],
"render": "<div class='relative'> <img src='./assets/svg/add_pin.svg' class='absolute' style='height: 50px'> <div class='absolute top-0 left-0 rounded-full overflow-hidden noselect' style='width: 40px; height: 40px'><div class='flex slide min-w-min' style='animation: slide linear {number_of_presets}s infinite; width: calc( (1 + {number_of_presets}) * 40px ); height: 40px'>{renderings}{first_preset}</div></div></div>" "render": "<div class='relative'> <img src='./assets/svg/add_pin.svg' class='absolute' style='height: 50px'> <div class='absolute top-0 left-0 rounded-full overflow-hidden noselect' style='width: 40px; height: 40px'><div class='flex slide min-w-min' style='animation: slide linear {number_of_presets}s infinite; width: calc( (1 + {number_of_presets}) * 40px ); height: 40px'>{renderings}{first_preset}</div></div></div>"
}, },
"labelCssClasses": "text-sm min-w-min pl-1 pr-1 rounded-3xl text-white opacity-65 whitespace-nowrap block-ruby", "labelCssClasses": "text-sm min-w-min px-2 rounded-full text-white opacity-65 whitespace-nowrap block-ruby",
"labelCss": "background: #00000088", "labelCss": "background: #00000088",
"label": { "label": {
"render": { "render": {

View file

@ -1245,10 +1245,6 @@ video {
resize: both; resize: both;
} }
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-1 { .grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr)); grid-template-columns: repeat(1, minmax(0, 1fr));
} }