Favourites: improve overview, update all features from OSM when loading
This commit is contained in:
parent
35f8b9d8f2
commit
56a23deb2d
10 changed files with 231 additions and 234 deletions
|
@ -501,147 +501,43 @@ 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 }[]
|
||||
/**
|
||||
* Given a list of points, convert into a GPX-list, e.g. for favourites
|
||||
* @param locations
|
||||
* @param title
|
||||
*/
|
||||
public static toGpxPoints(
|
||||
locations: Feature<Point, { date?: string; altitude?: number | string }>[],
|
||||
title?: string
|
||||
) {
|
||||
title = title?.trim()
|
||||
if (title === undefined || title === "") {
|
||||
title = "Created with MapComplete"
|
||||
}
|
||||
|
||||
// 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<string, edge>()
|
||||
|
||||
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
|
||||
title = Utils.EncodeXmlValue(title)
|
||||
const trackPoints: string[] = []
|
||||
for (const l of locations) {
|
||||
let trkpt = ` <wpt lat="${l.geometry.coordinates[1]}" lon="${l.geometry.coordinates[0]}">`
|
||||
for (const key in l.properties) {
|
||||
const keyCleaned = key.replaceAll(":", "__")
|
||||
trkpt += ` <${keyCleaned}>${l.properties[key]}</${keyCleaned}>\n`
|
||||
if (key === "website") {
|
||||
trkpt += ` <link>${l.properties[key]}</link>\n`
|
||||
}
|
||||
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)
|
||||
}
|
||||
trkpt += " </wpt>\n"
|
||||
trackPoints.push(trkpt)
|
||||
}
|
||||
|
||||
// Lets merge them back together!
|
||||
|
||||
let didMergeSomething = false
|
||||
let allMergedEdges = Array.from(allEdgesByKey.values())
|
||||
const allEdgesByStartPoint = new Map<string, edge[]>()
|
||||
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<edge>()
|
||||
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 []
|
||||
const header =
|
||||
'<gpx version="1.1" creator="mapcomplete.org" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">'
|
||||
return (
|
||||
header +
|
||||
"\n<name>" +
|
||||
title +
|
||||
"</name>\n<trk><trkseg>\n" +
|
||||
trackPoints.join("\n") +
|
||||
"\n</trkseg></trk></gpx>"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue