forked from MapComplete/MapComplete
Feature: add SVG for 3D-printing
This commit is contained in:
parent
8685ec8ccc
commit
8a9650c737
4 changed files with 69 additions and 15 deletions
|
@ -173,7 +173,9 @@
|
||||||
"downloadAsPng": "Download as image",
|
"downloadAsPng": "Download as image",
|
||||||
"downloadAsPngHelper": "Ideal to include in reports",
|
"downloadAsPngHelper": "Ideal to include in reports",
|
||||||
"downloadAsSvg": "Download an SVG of the current map",
|
"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",
|
"downloadCSV": "Download visible data as CSV",
|
||||||
"downloadCSVHelper": "Compatible with LibreOffice Calc, Excel, …",
|
"downloadCSVHelper": "Compatible with LibreOffice Calc, Excel, …",
|
||||||
"downloadFeatureAsGeojson": "Download as GeoJSON-file",
|
"downloadFeatureAsGeojson": "Download as GeoJSON-file",
|
||||||
|
|
|
@ -261,16 +261,16 @@ export class GeoOperations {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the closest point on a way from a given point.
|
* 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
|
* If the passed-in geojson object is a polygon, the outer ring will be used as linestring
|
||||||
*
|
*
|
||||||
* The properties object will contain three values:
|
* The properties object will contain three values:
|
||||||
// - `index`: closest point was found on nth line part,
|
// - `index`: closest point was found on nth line part,
|
||||||
// - `dist`: distance between pt and the closest point (in kilometer),
|
// - `dist`: distance between pt and the closest point (in kilometer),
|
||||||
// `location`: distance along the line between start (of the line) and the closest point.
|
// `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 way The road on which you want to find a point
|
||||||
* @param point Point defined as [lon, lat]
|
* @param point Point defined as [lon, lat]
|
||||||
*/
|
*/
|
||||||
public static nearestPoint(
|
public static nearestPoint(
|
||||||
way: Feature<LineString>,
|
way: Feature<LineString>,
|
||||||
point: [number, number]
|
point: [number, number]
|
||||||
|
@ -449,6 +449,7 @@ export class GeoOperations {
|
||||||
|
|
||||||
return perBbox
|
return perBbox
|
||||||
}
|
}
|
||||||
|
|
||||||
public static toGpx(
|
public static toGpx(
|
||||||
locations:
|
locations:
|
||||||
| Feature<LineString>
|
| Feature<LineString>
|
||||||
|
@ -1052,4 +1053,40 @@ export class GeoOperations {
|
||||||
}
|
}
|
||||||
throw "CalculateIntersection fallthrough: can not calculate an intersection between features"
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import SimpleMetaTagger from "../../Logic/SimpleMetaTagger"
|
import SimpleMetaTagger from "../../Logic/SimpleMetaTagger"
|
||||||
import geojson2svg from "geojson2svg"
|
import geojson2svg from "geojson2svg"
|
||||||
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposes the download-functionality
|
* Exposes the download-functionality
|
||||||
|
@ -82,6 +83,7 @@ export default class DownloadHelper {
|
||||||
height?: 1000 | number
|
height?: 1000 | number
|
||||||
mapExtent?: BBox
|
mapExtent?: BBox
|
||||||
unit?: "px" | "mm" | string
|
unit?: "px" | "mm" | string
|
||||||
|
noSelfIntersectingLines?: boolean
|
||||||
}) {
|
}) {
|
||||||
const perLayer = this._state.perLayer
|
const perLayer = this._state.perLayer
|
||||||
options = options ?? {}
|
options = options ?? {}
|
||||||
|
@ -103,7 +105,7 @@ export default class DownloadHelper {
|
||||||
const elements: string[] = []
|
const elements: string[] = []
|
||||||
|
|
||||||
for (const layer of Array.from(perLayer.keys())) {
|
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) {
|
if (features.length === 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -128,7 +130,9 @@ export default class DownloadHelper {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
if (options.noSelfIntersectingLines) {
|
||||||
|
features = GeoOperations.SplitSelfIntersectingWays(features)
|
||||||
|
}
|
||||||
for (const feature of features) {
|
for (const feature of features) {
|
||||||
const stroke =
|
const stroke =
|
||||||
rendering?.color?.GetRenderValue(feature.properties)?.txt ?? "#ff0000"
|
rendering?.color?.GetRenderValue(feature.properties)?.txt ?? "#ff0000"
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
let metaIsIncluded = false
|
let metaIsIncluded = false
|
||||||
const name = state.layout.id
|
const name = state.layout.id
|
||||||
|
|
||||||
function offerSvg(): string {
|
function offerSvg(noSelfIntersectingLines: boolean): string {
|
||||||
const maindiv = document.getElementById("maindiv")
|
const maindiv = document.getElementById("maindiv")
|
||||||
const layers = state.layout.layers.filter((l) => l.source !== null)
|
const layers = state.layout.layers.filter((l) => l.source !== null)
|
||||||
return downloadHelper.asSvg({
|
return downloadHelper.asSvg({
|
||||||
|
@ -27,6 +27,7 @@
|
||||||
mapExtent: state.mapProperties.bounds.data,
|
mapExtent: state.mapProperties.bounds.data,
|
||||||
width: maindiv.offsetWidth,
|
width: maindiv.offsetWidth,
|
||||||
height: maindiv.offsetHeight,
|
height: maindiv.offsetHeight,
|
||||||
|
noSelfIntersectingLines: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -71,9 +72,19 @@
|
||||||
mimetype="image/svg+xml"
|
mimetype="image/svg+xml"
|
||||||
mainText={t.downloadAsSvg}
|
mainText={t.downloadAsSvg}
|
||||||
helperText={t.downloadAsSvgHelper}
|
helperText={t.downloadAsSvgHelper}
|
||||||
construct={offerSvg}
|
construct={() => offerSvg(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<DownloadButton
|
||||||
|
{state}
|
||||||
|
{metaIsIncluded}
|
||||||
|
extension="svg"
|
||||||
|
mimetype="image/svg+xml"
|
||||||
|
mainText={t.downloadAsSvgLinesOnly}
|
||||||
|
helperText={t.downloadAsSvgLinesOnlyHelper}
|
||||||
|
construct={() => offerSvg(true)}
|
||||||
|
/>
|
||||||
|
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
{state}
|
{state}
|
||||||
{metaIsIncluded}
|
{metaIsIncluded}
|
||||||
|
|
Loading…
Add table
Reference in a new issue