From 1af5e44ad453f665d5bc35ccc6284fa2145ee04f Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 9 Nov 2021 18:41:20 +0100 Subject: [PATCH] Various small fixes --- .../RenderingMultiPlexerFeatureSource.ts | 36 +++-- Logic/GeoOperations.ts | 146 +++++++++++++++++- UI/Base/ScrollableFullScreen.ts | 2 +- UI/Input/LocationInput.ts | 2 +- UI/ShowDataLayer/ShowDataLayer.ts | 4 +- assets/layers/gps_track/gps_track.json | 6 +- 6 files changed, 176 insertions(+), 20 deletions(-) diff --git a/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts index ec6358e62..d2a96e86e 100644 --- a/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts +++ b/Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource.ts @@ -7,7 +7,6 @@ import FeatureSource from "../FeatureSource"; import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig"; import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; - export default class RenderingMultiPlexerFeatureSource { public readonly features: UIEventSource<(any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[]>; @@ -29,7 +28,7 @@ export default class RenderingMultiPlexerFeatureSource { const lineRenderObjects = layer.lineRendering - const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[] = []; + const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined, multiLineStringIndex: number | undefined })[] = []; function addAsPoint(feat, rendering, coordinate) { @@ -69,11 +68,18 @@ export default class RenderingMultiPlexerFeatureSource { const coordinate = coordinates[coordinates.length - 1] addAsPoint(feat, rendering, coordinate) } + for (let i = 0; i < lineRenderObjects.length; i++) { + withIndex.push({ + ...feat, + lineRenderingIndex: i + }) + } } if (feat.geometry.type === "MultiLineString") { - const lineList = feat.geometry.coordinates - for (const coordinates of lineList) { + const lineList: [number, number][][] = feat.geometry.coordinates + for (let i1 = 0; i1 < lineList.length; i1++) { + const coordinates = lineList[i1]; for (const rendering of startRenderings) { const coordinate = coordinates[0] @@ -83,19 +89,25 @@ export default class RenderingMultiPlexerFeatureSource { const coordinate = coordinates[coordinates.length - 1] addAsPoint(feat, rendering, coordinate) } + + + for (let i = 0; i < lineRenderObjects.length; i++) { + const orig = { + ...feat, + lineRenderingIndex: i, + multiLineStringIndex: i1 + } + orig.geometry.coordinates = coordinates + orig.geometry.type = "LineString" + withIndex.push(orig) + } } } - - for (let i = 0; i < lineRenderObjects.length; i++) { - withIndex.push({ - ...feat, - lineRenderingIndex: i - }) - } - } } + + return withIndex; } ); diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index b3ad288e8..3f1c67d94 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -3,7 +3,6 @@ import {BBox} from "./BBox"; import togpx from "togpx" import Constants from "../Models/Constants"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; -import {meta} from "@turf/turf"; export class GeoOperations { @@ -465,6 +464,151 @@ export class GeoOperations { }) } + public static IdentifieCommonSegments(coordinatess: [number,number][][] ): { + originalIndex: number, + segmentShardWith: number[], + coordinates: [] + }[]{ + + // An edge. Note that the edge might be reversed to fix the sorting condition: start[0] < end[0] && (start[0] != end[0] || start[0] < end[1]) + type edge = {start: [number, number], end: [number, number], intermediate: [number,number][], members: {index:number, isReversed: boolean}[]} + + // The strategy: + // 1. Index _all_ edges from _every_ linestring. Index them by starting key, gather which relations run over them + // 2. Join these edges back together - as long as their membership groups are the same + // 3. Convert to results + + const allEdgesByKey = new Map() + + for (let index = 0; index < coordinatess.length; index++){ + const coordinates = coordinatess[index]; + for (let i = 0; i < coordinates.length - 1; i++){ + + const c0 = coordinates[i]; + const c1 = coordinates[i + 1] + const isReversed = (c0[0] > c1[0]) || (c0[0] == c1[0] && c0[1] > c1[1]) + + let key : string + if(isReversed){ + key = ""+c1+";"+c0 + }else{ + key = ""+c0+";"+c1 + } + const member = {index, isReversed} + if(allEdgesByKey.has(key)){ + allEdgesByKey.get(key).members.push(member) + continue + } + + let edge : edge; + if(!isReversed){ + edge = { + start : c0, + end: c1, + members: [member], + intermediate: [] + } + }else{ + edge = { + start : c1, + end: c0, + members: [member], + intermediate: [] + } + } + allEdgesByKey.set(key, edge) + + } + } + + // Lets merge them back together! + + let didMergeSomething = false; + let allMergedEdges = Array.from(allEdgesByKey.values()) + const allEdgesByStartPoint = new Map() + for (const edge of allMergedEdges) { + + edge.members.sort((m0, m1) => m0.index - m1.index) + + const kstart = edge.start+"" + if(!allEdgesByStartPoint.has(kstart)){ + allEdgesByStartPoint.set(kstart, []) + } + allEdgesByStartPoint.get(kstart).push(edge) + } + + + function membersAreCompatible(first:edge, second:edge): boolean{ + // There must be an exact match between the members + if(first.members === second.members){ + return true + } + + if(first.members.length !== second.members.length){ + return false + } + + // Members are sorted and have the same length, so we can check quickly + for (let i = 0; i < first.members.length; i++) { + const m0 = first.members[i] + const m1 = second.members[i] + if(m0.index !== m1.index || m0.isReversed !== m1.isReversed){ + return false + } + } + + // Allrigth, they are the same, lets mark this permanently + second.members = first.members + return true + + } + + do{ + didMergeSomething = false + // We use 'allMergedEdges' as our running list + const consumed = new Set() + for (const edge of allMergedEdges) { + // Can we make this edge longer at the end? + if(consumed.has(edge)){ + continue + } + + console.log("Considering edge", edge) + const matchingEndEdges = allEdgesByStartPoint.get(edge.end+"") + console.log("Matchign endpoints:", matchingEndEdges) + if(matchingEndEdges === undefined){ + continue + } + + + for (let i = 0; i < matchingEndEdges.length; i++){ + const endEdge = matchingEndEdges[i]; + + if(consumed.has(endEdge)){ + continue + } + + if(!membersAreCompatible(edge, endEdge)){ + continue + } + + // We can make the segment longer! + didMergeSomething = true + console.log("Merging ", edge, "with ", endEdge) + edge.intermediate.push(edge.end) + edge.end = endEdge.end + consumed.add(endEdge) + matchingEndEdges.splice(i, 1) + break; + } + } + + allMergedEdges = allMergedEdges.filter(edge => !consumed.has(edge)); + + }while(didMergeSomething) + + return [] + } } diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index 0e28bd445..b5b15229f 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -55,7 +55,7 @@ export default class ScrollableFullScreen extends UIElement { if (!isShown.data) { return; } - if (hash === undefined || hash === "") { + if (hash === undefined || hash === "" || hash !== hashToShow) { isShown.setData(false) } }) diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 880f8810b..df7958de7 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -24,7 +24,7 @@ export default class LocationInput extends InputElement implements MinimapO osmTags: {and: []} }, mapRendering: [{ - location: ["point"], + location: ["point","centroid"], icon: "./assets/svg/crosshair-empty.svg" }] }, "matchpoint icon", true diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 6c74c5f4a..f306cadf2 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -152,7 +152,7 @@ export default class ShowDataLayer { continue } try { - if ((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) { + if (feat.geometry.type === "LineString") { const self = this; const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates) const tagsSource = this.allElements?.addOrGetElement(feat) ?? new UIEventSource(feat.properties); @@ -270,7 +270,7 @@ export default class ShowDataLayer { let infobox: FeatureInfoBox = undefined; - const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this.showDataLayerid}-${this._cleanCount}-${feature.pointRenderingIndex ?? feature.lineRenderingIndex}` + const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this.showDataLayerid}-${this._cleanCount}-${feature.pointRenderingIndex ?? feature.lineRenderingIndex}-${feature.multiLineStringIndex ?? ""}` popup.setContent(`
Popup for ${feature.properties.id} ${feature.geometry.type} ${id} is loading
`) leafletLayer.on("popupopen", () => { if (infobox === undefined) { diff --git a/assets/layers/gps_track/gps_track.json b/assets/layers/gps_track/gps_track.json index c27fa667f..75f18e2e1 100644 --- a/assets/layers/gps_track/gps_track.json +++ b/assets/layers/gps_track/gps_track.json @@ -6,7 +6,7 @@ "osmTags": "user:location=yes", "maxCacheAge": 0 }, - "title": { + "#title": { "render": "Your travelled path" }, "tagRenderings": [ @@ -18,10 +18,10 @@ }, "export_as_gpx" ], - "name": "Your track", + "#name": "Your track", "mapRendering": [ { - "width": 3, + "width": 0, "color": "#bb000077" } ]