-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconvert.ts
117 lines (109 loc) · 4.29 KB
/
convert.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import * as settings from './convert-config.json'
import { createConverter } from "convert-svg-to-png"
import * as D3Node from "d3-node"
import { d3 } from "d3-node"
import * as fs from "fs"
import { Feature, FeatureCollection } from "geojson"
import * as sharp from "sharp"
class Tile {
zoom: number
x: number
y: number
geoJSON: FeatureCollection
constructor(zoom: number, x: number, y: number, geoJSON: FeatureCollection) {
this.zoom = zoom
this.x = x
this.y = y
this.geoJSON = geoJSON
}
}
const convertSvgFiles = async (tile: Tile) => {
// Add map tile's four corners to GeoJSON features
const west = tile.x / Math.pow(2.0, tile.zoom) * 360.0 - 180.0
const east = (tile.x + 1) / Math.pow(2.0, tile.zoom) * 360.0 - 180.0
const n = Math.PI - 2.0 * Math.PI * tile.y / Math.pow(2.0, tile.zoom)
const north = 180.0 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))
const m = Math.PI - 2.0 * Math.PI * (tile.y + 1) / Math.pow(2.0, tile.zoom)
const south = 180.0 / Math.PI * Math.atan(0.5 * (Math.exp(m) - Math.exp(-m)))
const feature = {
type: "Feature",
geometry: {
type: "MultiPoint",
coordinates: [[west,north],[west,south],[east,south],[east,north],[west,north]]
}
} as Feature
const features = tile.geoJSON.features.concat([feature])
// Entry point into main convert GeoJSON to PNG function
const converter = createConverter()
// Using the FeatureCollection, center the region with the selected geographic projection
const projection = d3.geoMercator()
.fitSize([settings.outputWidth, settings.outputHeight], {
"type": "FeatureCollection",
"features": features//tile.geoJSON.features
})
const geoPath = d3.geoPath(projection)
// Go through each feature, or constituency, within the region,
// and render it as SVG with the feature highlighted
// const svgString = await renderSVG(tile, geoPath)
const svgString = await renderSVG(features, geoPath)
try {
// Using the `sharp` library, take the rendered SVG string and generate a PNG
await sharp(Buffer.from(svgString))
.extract({
left: 0,
top: 0,
width: settings.outputWidth,
height: settings.outputHeight
})
.png()
.toFile(`${settings.outputDirectory}/${tile.zoom}.${tile.x}.${tile.y}.png`)
} catch (err) {
console.error(err)
}
await converter.destroy()
}
const renderSVG = async (features, geoPath) => {
// Use D3 on the back-end to create an SVG of the FeatureCollection
const svgString = features
.map(feature => {
const d3N = new D3Node()
let color = "rgba(0,0,0,0)"
if (feature.properties && feature.properties.maxDepth && feature.properties.minDepth) {
const averageDepth = (feature.properties.maxDepth + feature.properties.minDepth) / 2.0
color = settings.colors.find(color => averageDepth >= color.minDepth && averageDepth <= color.maxDepth).code
}
const svg = d3N.createSVG(settings.outputWidth, settings.outputHeight)
svg
.selectAll("path")
.data([feature])
.enter()
.append("path")
.style("fill", color)
.style("shape-rendering", "crispEdges")
.style("stroke", color)
.style("stroke-width", "1px")
.attr("d", geoPath)
return d3N.svgString()
.replace(`<svg xmlns="http://www.w3.org/2000/svg" width="${settings.outputWidth}" height="${settings.outputHeight}">`, "")
.replace("</svg>", "")
})
.reduce((a, b) => { return a + b })
return `<svg xmlns="http://www.w3.org/2000/svg" width="${settings.outputWidth}" height="${settings.outputHeight}">${svgString}</svg>`
}
const createTiles = async () => {
const tiles = fs.readdirSync(settings.geojsonDirectory)
.filter((value) => { return value.endsWith('.geojson') })
.filter((value) => { return value.split('.').length === 4 })
.map((value) => {
const components = value.split('.')
const geoJSON = JSON.parse(fs.readFileSync(`${settings.geojsonDirectory}/${value}`,'utf8')) as FeatureCollection
return new Tile(parseInt(components[0]), parseInt(components[1]), parseInt(components[2]), geoJSON)
})
for (let tile of tiles) {
await convertSvgFiles(tile)
}
process.exit()
}
createTiles()
.catch((reason) => { console.log(reason) })
.finally(() => { console.log("done") })