diff --git a/langs/en.json b/langs/en.json index 39b3f40c1..517f735ba 100644 --- a/langs/en.json +++ b/langs/en.json @@ -173,7 +173,9 @@ "downloadAsPng": "Download as image", "downloadAsPngHelper": "Ideal to include in reports", "downloadAsSvg": "Download an SVG of the current map", - "downloadAsSvgHelper": "Compatible Inkscape or Adobe Illustrator; will need further processing", + "downloadAsSvgHelper": "Compatible with Inkscape or Adobe Illustrator; will need further processing", + "downloadAsSvgLinesOnly": "Download an SVG of the current map only containing lines", + "downloadAsSvgLinesOnlyHelper": "Self-intersecting lines are broken up, can be used with some 3D-software", "downloadCSV": "Download visible data as CSV", "downloadCSVHelper": "Compatible with LibreOffice Calc, Excel, …", "downloadFeatureAsGeojson": "Download as GeoJSON-file", diff --git a/src/Logic/GeoOperations.ts b/src/Logic/GeoOperations.ts index 1625f8e0c..e03cc13b1 100644 --- a/src/Logic/GeoOperations.ts +++ b/src/Logic/GeoOperations.ts @@ -261,16 +261,16 @@ export class GeoOperations { } /** - * Generates the closest point on a way from a given point. - * If the passed-in geojson object is a polygon, the outer ring will be used as linestring - * - * The properties object will contain three values: - // - `index`: closest point was found on nth line part, - // - `dist`: distance between pt and the closest point (in kilometer), - // `location`: distance along the line between start (of the line) and the closest point. - * @param way The road on which you want to find a point - * @param point Point defined as [lon, lat] - */ + * Generates the closest point on a way from a given point. + * If the passed-in geojson object is a polygon, the outer ring will be used as linestring + * + * The properties object will contain three values: + // - `index`: closest point was found on nth line part, + // - `dist`: distance between pt and the closest point (in kilometer), + // `location`: distance along the line between start (of the line) and the closest point. + * @param way The road on which you want to find a point + * @param point Point defined as [lon, lat] + */ public static nearestPoint( way: Feature, point: [number, number] @@ -449,6 +449,7 @@ export class GeoOperations { return perBbox } + public static toGpx( locations: | Feature @@ -1052,4 +1053,40 @@ export class GeoOperations { } throw "CalculateIntersection fallthrough: can not calculate an intersection between features" } + + public static SplitSelfIntersectingWays(features: Feature[]): Feature[] { + const result: Feature[] = [] + + for (const feature of features) { + if (feature.geometry.type === "LineString") { + let coors = feature.geometry.coordinates + for (let i = coors.length - 1; i >= 0; i--) { + // Go back, to nick of the back when needed + const ci = coors[i] + for (let j = i + 1; j < coors.length; j++) { + const cj = coors[j] + if ( + Math.abs(ci[0] - cj[0]) <= 0.000001 && + Math.abs(ci[1] - cj[1]) <= 0.0000001 + ) { + // Found a self-intersecting way! + console.debug("SPlitting way", feature.properties.id) + result.push({ + ...feature, + geometry: { ...feature.geometry, coordinates: coors.slice(i + 1) }, + }) + coors = coors.slice(0, i + 1) + break + } + } + } + result.push({ + ...feature, + geometry: { ...feature.geometry, coordinates: coors }, + }) + } + } + + return result + } } diff --git a/src/UI/DownloadFlow/DownloadHelper.ts b/src/UI/DownloadFlow/DownloadHelper.ts index 035f5cf13..80906a1b1 100644 --- a/src/UI/DownloadFlow/DownloadHelper.ts +++ b/src/UI/DownloadFlow/DownloadHelper.ts @@ -5,6 +5,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import { Utils } from "../../Utils" import SimpleMetaTagger from "../../Logic/SimpleMetaTagger" import geojson2svg from "geojson2svg" +import { GeoOperations } from "../../Logic/GeoOperations" /** * Exposes the download-functionality @@ -82,6 +83,7 @@ export default class DownloadHelper { height?: 1000 | number mapExtent?: BBox unit?: "px" | "mm" | string + noSelfIntersectingLines?: boolean }) { const perLayer = this._state.perLayer options = options ?? {} @@ -103,7 +105,7 @@ export default class DownloadHelper { const elements: string[] = [] for (const layer of Array.from(perLayer.keys())) { - const features = perLayer.get(layer).features.data + let features = perLayer.get(layer).features.data if (features.length === 0) { continue } @@ -128,7 +130,9 @@ export default class DownloadHelper { }, ], }) - + if (options.noSelfIntersectingLines) { + features = GeoOperations.SplitSelfIntersectingWays(features) + } for (const feature of features) { const stroke = rendering?.color?.GetRenderValue(feature.properties)?.txt ?? "#ff0000" diff --git a/src/UI/DownloadFlow/DownloadPanel.svelte b/src/UI/DownloadFlow/DownloadPanel.svelte index 234f7dccf..05688a5e6 100644 --- a/src/UI/DownloadFlow/DownloadPanel.svelte +++ b/src/UI/DownloadFlow/DownloadPanel.svelte @@ -19,7 +19,7 @@ let metaIsIncluded = false const name = state.layout.id - function offerSvg(): string { + function offerSvg(noSelfIntersectingLines: boolean): string { const maindiv = document.getElementById("maindiv") const layers = state.layout.layers.filter((l) => l.source !== null) return downloadHelper.asSvg({ @@ -27,6 +27,7 @@ mapExtent: state.mapProperties.bounds.data, width: maindiv.offsetWidth, height: maindiv.offsetHeight, + noSelfIntersectingLines: true }) } @@ -71,9 +72,19 @@ mimetype="image/svg+xml" mainText={t.downloadAsSvg} helperText={t.downloadAsSvgHelper} - construct={offerSvg} + construct={() => offerSvg(false)} /> + offerSvg(true)} + /> +