import {SpecialVisualizationState} from "../SpecialVisualization"; import {Feature, FeatureCollection} from "geojson"; import {BBox} from "../../Logic/BBox"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import {Utils} from "../../Utils"; import SimpleMetaTagger from "../../Logic/SimpleMetaTagger" import geojson2svg from "geojson2svg" /** * Exposes the download-functionality */ export default class DownloadHelper { private readonly _state: SpecialVisualizationState; constructor(state: SpecialVisualizationState) { this._state = state; } /** * Returns a new feature of which all the metatags are deleted */ private static cleanFeature(f: Feature): Feature { f = { type: f.type, geometry: {...f.geometry}, properties: {...f.properties}, } for (const key in f.properties) { if (key === "_lon" || key === "_lat") { continue } if (key.startsWith("_")) { delete f.properties[key] } } const datedKeys = [].concat( SimpleMetaTagger.metatags .filter((tagging) => tagging.includesDates) .map((tagging) => tagging.keys) ) for (const key of datedKeys) { delete f.properties[key] } return f } public getCleanGeoJson( includeMetaData: boolean ): FeatureCollection { const featuresPerLayer = this.getCleanGeoJsonPerLayer(includeMetaData) const features = [].concat(...Array.from(featuresPerLayer.values())) return { type: "FeatureCollection", features, } } /** * Converts a geojson to an SVG * * * import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; * * const feature: Feature = { * "type": "Feature", * "properties": {}, * "geometry": { * "type": "LineString", * "coordinates": [ * [-180, 80], * [180, -80] * ] * } * } * const features = new StaticFeatureSource([feature]) * const perLayer = new Map() * perLayer.set("testlayer", features) * new DownloadHelper( {perLayer}).asSvg().replace(/\n/g, "") // => ` ` */ public asSvg( options?: { layers?: LayerConfig[] width?: 1000 | number height?: 1000 | number mapExtent?: BBox unit?: "px" | "mm" | string } ) { const perLayer = this._state.perLayer options = options ?? {} const width = options.width ?? 1000 const height = options.height ?? 1000 if (width <= 0 || height <= 0) { throw "Invalid width of height, they should be > 0" } const unit = options.unit ?? "px" const mapExtent = {left: -180, bottom: -90, right: 180, top: 90} if (options.mapExtent !== undefined) { const bbox = options.mapExtent mapExtent.left = bbox.minLon mapExtent.right = bbox.maxLon mapExtent.bottom = bbox.minLat mapExtent.top = bbox.maxLat } console.log("Generateing svg, extent:", {mapExtent, width, height}) const elements: string[] = [] for (const layer of Array.from(perLayer.keys())) { const features = perLayer.get(layer).features.data if (features.length === 0) { continue } const layerDef = options?.layers?.find((l) => l.id === layer) const rendering = layerDef?.lineRendering[0] const converter = geojson2svg({ viewportSize: {width, height}, mapExtent, output: "svg", attributes: [ { property: "style", type: "static", value: "fill:none;stroke-width:1", }, { property: "properties.stroke", type: "dynamic", key: "stroke", }, ], }) for (const feature of features) { const stroke = rendering?.color?.GetRenderValue(feature.properties)?.txt ?? "#ff0000" feature.properties.stroke = Utils.colorAsHex(Utils.color(stroke)) } const groupPaths: string[] = converter.convert({type: "FeatureCollection", features}) const group = ` \n` + groupPaths.map((p) => " " + p).join("\n") + "\n " elements.push(group) } const w = width const h = height const header = `` return header + "\n" + elements.join("\n") + "\n" } public getCleanGeoJsonPerLayer( includeMetaData: boolean ): Map { const state = this._state const featuresPerLayer = new Map() const neededLayers = state.layout.layers.filter((l) => l.source !== null).map((l) => l.id) const bbox = state.mapProperties.bounds.data for (const neededLayer of neededLayers) { const indexedFeatureSource = state.perLayer.get(neededLayer) let features = indexedFeatureSource.GetFeaturesWithin(bbox) // The 'indexedFeatureSources' contains _all_ features, they are not filtered yet const filter = state.layerState.filteredLayers.get(neededLayer) features = features.filter((f) => filter.isShown(f.properties, state.layerState.globalFilters.data) ) if (!includeMetaData) { features = features.map((f) => DownloadHelper.cleanFeature(f)) } featuresPerLayer.set(neededLayer, features) } return featuresPerLayer } /** * Creates an image for the given key. * @param key * @param width (in mm) * @param height (in mm) */ createImage(key: string, width: string, height: string): HTMLImageElement { const img = document.createElement("img") const sources = { "layouticon":this._state.layout.icon } img.src = sources[key] if(!img.src){ throw "Invalid key for 'createImage': "+key+"; try one of: "+Object.keys(sources).join(", ") } img.style.width = width img.style.height = height console.log("Fetching an image with src", img.src) return img; } }