@unvt/charites 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.all-contributorsrc +24 -0
- package/.eslintignore +2 -0
- package/.eslintrc.js +17 -0
- package/.github/CONTRIBUTING.md +30 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +25 -0
- package/.github/workflows/build-docs.yml +72 -0
- package/.github/workflows/build.yml +1 -0
- package/.prettierrc.js +6 -0
- package/LICENSE +1 -1
- package/README.md +23 -106
- package/dist/cli/build.js +63 -0
- package/dist/cli/convert.js +19 -0
- package/dist/cli/init.js +26 -0
- package/dist/cli/serve.js +34 -0
- package/dist/cli.js +10 -66
- package/dist/commands/build.js +39 -3
- package/dist/commands/convert.js +6 -23
- package/dist/commands/init.js +15 -22
- package/dist/commands/serve.js +8 -3
- package/dist/lib/build-sprite.js +71 -0
- package/dist/lib/defaultValues.js +3 -3
- package/dist/lib/error.js +9 -0
- package/dist/lib/get-sprite-slug.js +16 -0
- package/dist/lib/tileinfo-importer/base-importer.js +41 -0
- package/dist/lib/tileinfo-importer/index.js +6 -0
- package/dist/lib/tileinfo-importer/metadata-importer.js +38 -0
- package/dist/lib/tileinfo-importer/tilejson-importer.js +25 -0
- package/dist/lib/validate-style.js +2 -2
- package/dist/lib/yaml-parser.js +2 -2
- package/dist/lib/yaml-writer.js +48 -0
- package/dist/types/index.js +15 -0
- package/dist/types/metadatajson.js +2 -0
- package/dist/types/tilejson.js +2 -0
- package/dist/types/vector_layers.js +2 -0
- package/docs/.tx/config +62 -0
- package/docs/Makefile +170 -0
- package/docs/Pipfile +20 -0
- package/docs/Pipfile.lock +429 -0
- package/docs/README.md +43 -0
- package/docs/make.bat +35 -0
- package/docs/source/_static/.gitkeep +0 -0
- package/docs/source/_templates/.gitkeep +0 -0
- package/docs/source/conf.py +69 -0
- package/docs/source/development/index.rst +40 -0
- package/docs/source/index.rst +61 -0
- package/docs/source/install/index.rst +10 -0
- package/docs/source/install/install.rst +7 -0
- package/docs/source/install/install_on_nanban.rst +9 -0
- package/docs/source/install/recommended_environment.rst +6 -0
- package/docs/source/usage/commandline_interface.rst +102 -0
- package/docs/source/usage/examples.rst +74 -0
- package/docs/source/usage/global_options.rst +21 -0
- package/docs/source/usage/index.rst +10 -0
- package/package.json +12 -4
- package/provider/default/app.css +10 -0
- package/provider/default/app.js +31 -5
- package/provider/default/index.html +7 -0
- package/provider/geolonia/app.css +10 -0
- package/provider/geolonia/app.js +29 -5
- package/provider/geolonia/index.html +7 -0
- package/provider/mapbox/app.css +10 -0
- package/provider/mapbox/app.js +31 -5
- package/provider/mapbox/index.html +7 -0
- package/src/cli/build.ts +77 -0
- package/src/cli/convert.ts +18 -0
- package/src/cli/init.ts +34 -0
- package/src/cli/serve.ts +39 -0
- package/src/cli.ts +12 -76
- package/src/commands/build.ts +71 -9
- package/src/commands/convert.ts +16 -35
- package/src/commands/init.ts +28 -21
- package/src/commands/serve.ts +70 -57
- package/src/lib/build-sprite.ts +80 -0
- package/src/lib/defaultValues.ts +6 -6
- package/src/lib/error.ts +6 -0
- package/src/lib/get-sprite-slug.ts +18 -0
- package/src/lib/tileinfo-importer/base-importer.ts +57 -0
- package/src/lib/tileinfo-importer/index.ts +2 -0
- package/src/lib/tileinfo-importer/metadata-importer.ts +44 -0
- package/src/lib/tileinfo-importer/tilejson-importer.ts +29 -0
- package/src/lib/validate-style.ts +8 -2
- package/src/lib/yaml-parser.ts +9 -8
- package/src/lib/yaml-writer.ts +68 -0
- package/src/types/index.ts +3 -0
- package/src/types/metadatajson.ts +16 -0
- package/src/types/tilejson.ts +25 -0
- package/src/types/vector_layers.ts +11 -0
- package/test/build-sprite.spec.ts +24 -0
- package/test/build.spec.ts +121 -16
- package/test/convert.spec.ts +7 -7
- package/test/data/icons/aerialway.svg +4 -0
- package/test/data/init/init.yml +6 -0
- package/test/data/init/init_metadata.yml +60 -0
- package/test/data/init/init_tilejson.yml +28 -0
- package/test/data/init/init_tilejson_without_layers.yml +9 -0
- package/test/data/init/tilejson/init_decomposite.yml +13 -0
- package/test/data/init/tilejson/layers/bicycle_parking.yml +6 -0
- package/test/data/init/tilejson/layers/showers.yml +6 -0
- package/test/data/init/tilejson/layers/telephone.yml +6 -0
- package/test/get-sprite-slug.spec.ts +34 -0
- package/test/init.spec.ts +151 -0
- package/test/validate-style.spec.ts +7 -5
- package/test/yaml-parser.spec.ts +15 -15
- package/tsconfig.json +3 -1
package/src/commands/serve.ts
CHANGED
|
@@ -9,13 +9,17 @@ import { parser } from '../lib/yaml-parser'
|
|
|
9
9
|
import { validateStyle } from '../lib/validate-style'
|
|
10
10
|
import { defaultValues } from '../lib/defaultValues'
|
|
11
11
|
|
|
12
|
-
interface
|
|
12
|
+
export interface serveOptions {
|
|
13
13
|
provider?: string
|
|
14
14
|
mapboxAccessToken?: string
|
|
15
|
+
port?: string
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export function serve(source: string, options:
|
|
18
|
-
|
|
18
|
+
export function serve(source: string, options: serveOptions) {
|
|
19
|
+
let port = process.env.PORT || 8080
|
|
20
|
+
if (options.port) {
|
|
21
|
+
port = Number(options.port)
|
|
22
|
+
}
|
|
19
23
|
let sourcePath = path.resolve(process.cwd(), source)
|
|
20
24
|
|
|
21
25
|
let provider = defaultValues.provider
|
|
@@ -28,51 +32,56 @@ export function serve(source: string, options: options) {
|
|
|
28
32
|
sourcePath = source
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
if (!
|
|
35
|
+
if (!fs.existsSync(sourcePath)) {
|
|
32
36
|
throw `${sourcePath}: No such file or directory`
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
const server = http.createServer((req, res) => {
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const url = (req.url || '').replace(/\?.*/, '')
|
|
41
|
+
const dir = path.join(defaultValues.providerDir, provider)
|
|
38
42
|
|
|
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
|
-
|
|
43
|
+
switch (url) {
|
|
44
|
+
case '/':
|
|
45
|
+
res.statusCode = 200
|
|
46
|
+
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
|
47
|
+
const content = fs.readFileSync(path.join(dir, 'index.html'), 'utf-8')
|
|
48
|
+
res.end(content)
|
|
49
|
+
break
|
|
50
|
+
case '/style.json':
|
|
51
|
+
let style
|
|
52
|
+
try {
|
|
53
|
+
style = parser(sourcePath)
|
|
54
|
+
validateStyle(style, provider)
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.log(error)
|
|
57
|
+
}
|
|
58
|
+
res.statusCode = 200
|
|
59
|
+
res.setHeader('Content-Type', 'application/json; charset=UTF-8')
|
|
60
|
+
res.end(JSON.stringify(style))
|
|
61
|
+
break
|
|
62
|
+
case '/app.css':
|
|
63
|
+
res.statusCode = 200
|
|
64
|
+
res.setHeader('Content-Type', 'text/css; charset=UTF-8')
|
|
65
|
+
const css = fs.readFileSync(path.join(dir, 'app.css'), 'utf-8')
|
|
66
|
+
res.end(css)
|
|
67
|
+
break
|
|
68
|
+
case `/app.js`:
|
|
69
|
+
res.statusCode = 200
|
|
70
|
+
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8')
|
|
71
|
+
try {
|
|
72
|
+
const app = fs.readFileSync(path.join(dir, 'app.js'), 'utf-8')
|
|
73
|
+
const js = app
|
|
74
|
+
.replace('___PORT___', `${port}`)
|
|
75
|
+
.replace(
|
|
76
|
+
'___MAPBOX_ACCESS_TOKEN___',
|
|
77
|
+
`${options.mapboxAccessToken || defaultValues.mapboxAccessToken}`,
|
|
78
|
+
)
|
|
79
|
+
res.end(js)
|
|
80
|
+
} catch (e) {
|
|
81
|
+
throw `Invalid provider: ${provider}`
|
|
82
|
+
}
|
|
83
|
+
break
|
|
84
|
+
}
|
|
76
85
|
})
|
|
77
86
|
|
|
78
87
|
server.listen(port, () => {
|
|
@@ -82,24 +91,28 @@ export function serve(source: string, options: options) {
|
|
|
82
91
|
open(`http://localhost:${port}`)
|
|
83
92
|
})
|
|
84
93
|
|
|
85
|
-
const wss = new WebSocketServer({ server })
|
|
94
|
+
const wss = new WebSocketServer({ server })
|
|
86
95
|
|
|
87
96
|
wss.on('connection', (ws) => {
|
|
88
|
-
watch(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
watch(
|
|
98
|
+
path.dirname(sourcePath),
|
|
99
|
+
{ recursive: true, filter: /\.yml$/ },
|
|
100
|
+
(event, file) => {
|
|
101
|
+
console.log(`${(event || '').toUpperCase()}: ${file}`)
|
|
92
102
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
const style = parser(sourcePath)
|
|
104
|
+
try {
|
|
105
|
+
validateStyle(style, provider)
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.log(error)
|
|
108
|
+
}
|
|
109
|
+
ws.send(JSON.stringify(style))
|
|
110
|
+
} catch (e) {
|
|
111
|
+
// Nothing to do
|
|
96
112
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
});
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
})
|
|
103
116
|
|
|
104
117
|
return server
|
|
105
118
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const spritezero = require('@mapbox/spritezero')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const glob = require('glob')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
type spriteOption = {
|
|
7
|
+
imgs: string
|
|
8
|
+
pixelRatio: number
|
|
9
|
+
format: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function generateLayoutAsync(option: spriteOption): Promise<string> {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
spritezero.generateLayout(option, (err: TypeError, result: string) => {
|
|
15
|
+
if (err) reject(err)
|
|
16
|
+
else resolve(result)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function generateImageAsync(option: string): Promise<string> {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
spritezero.generateImage(option, (err: TypeError, result: string) => {
|
|
24
|
+
if (err) reject(err)
|
|
25
|
+
else resolve(result)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function buildSprite(
|
|
31
|
+
svgPath: string,
|
|
32
|
+
publicPath: string,
|
|
33
|
+
iconSlug: string,
|
|
34
|
+
) {
|
|
35
|
+
const pxRatios = [1, 2]
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < pxRatios.length; i++) {
|
|
38
|
+
const pxRatio = pxRatios[i]
|
|
39
|
+
|
|
40
|
+
const svgFiles = glob
|
|
41
|
+
.sync(path.join(svgPath, `*.svg`))
|
|
42
|
+
.map(function (iconPath: string) {
|
|
43
|
+
return {
|
|
44
|
+
svg: fs.readFileSync(iconPath),
|
|
45
|
+
id: path.basename(iconPath).replace('.svg', ''),
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
let file = ''
|
|
50
|
+
if (pxRatio > 1) {
|
|
51
|
+
file = `@${pxRatio}x`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const pngPath = path.join(publicPath, `${iconSlug}${file}.png`)
|
|
55
|
+
const jsonPath = path.join(publicPath, `${iconSlug}${file}.json`)
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const dataLayout = await generateLayoutAsync({
|
|
59
|
+
imgs: svgFiles,
|
|
60
|
+
pixelRatio: pxRatio,
|
|
61
|
+
format: true,
|
|
62
|
+
})
|
|
63
|
+
fs.writeFileSync(jsonPath, JSON.stringify(dataLayout))
|
|
64
|
+
} catch (error) {
|
|
65
|
+
throw `${publicPath}: No such file or directory`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const imageLayout = await generateLayoutAsync({
|
|
70
|
+
imgs: svgFiles,
|
|
71
|
+
pixelRatio: pxRatio,
|
|
72
|
+
format: false,
|
|
73
|
+
})
|
|
74
|
+
const image = await generateImageAsync(imageLayout)
|
|
75
|
+
fs.writeFileSync(pngPath, image)
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw `${publicPath}: No such file or directory`
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/lib/defaultValues.ts
CHANGED
|
@@ -10,27 +10,27 @@ interface Config {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const homedir = os.homedir()
|
|
13
|
-
const defaultProvider =
|
|
13
|
+
const defaultProvider = 'default'
|
|
14
14
|
|
|
15
15
|
const configDir = path.join(homedir, '.charites')
|
|
16
|
-
fs.mkdirSync(configDir, { recursive: true })
|
|
16
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
17
17
|
|
|
18
18
|
const configFile = path.join(configDir, 'config.yml')
|
|
19
|
-
let config: Config = {provider: '', providerDir: '', mapboxAccessToken: ''}
|
|
19
|
+
let config: Config = { provider: '', providerDir: '', mapboxAccessToken: '' }
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
22
|
const yaml = fs.readFileSync(configFile, 'utf-8')
|
|
23
23
|
config = YAML.load(yaml) as Config
|
|
24
|
-
} catch(e) {
|
|
24
|
+
} catch (e) {
|
|
25
25
|
// nothing to do
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export const defaultValues: Config = {
|
|
29
29
|
provider: config.provider || defaultProvider,
|
|
30
30
|
providerDir: path.join(path.dirname(path.dirname(__dirname)), 'provider'),
|
|
31
|
-
mapboxAccessToken: config.mapboxAccessToken || ''
|
|
31
|
+
mapboxAccessToken: config.mapboxAccessToken || '',
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export const defaultSettings = {
|
|
35
|
-
configFile: configFile
|
|
35
|
+
configFile: configFile,
|
|
36
36
|
}
|
package/src/lib/error.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec/types'
|
|
2
|
+
|
|
3
|
+
export function getSpriteSlug(style: StyleSpecification): string | false {
|
|
4
|
+
if (!style.hasOwnProperty('sprite')) {
|
|
5
|
+
return false
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const matchedUrl = style.sprite?.match(
|
|
9
|
+
/^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?(?:([^?#]*\/)([^\/?#]*))?(\?[^#]*)?(?:#.*)?$/,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
if (!matchedUrl || !matchedUrl[2]) {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// icon slug
|
|
17
|
+
return matchedUrl[2]
|
|
18
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { VectorLayer } from '../../types'
|
|
2
|
+
import {
|
|
3
|
+
StyleSpecification,
|
|
4
|
+
SourceSpecification,
|
|
5
|
+
LayerSpecification,
|
|
6
|
+
VectorSourceSpecification,
|
|
7
|
+
} from '@maplibre/maplibre-gl-style-spec/types'
|
|
8
|
+
|
|
9
|
+
export type TileInfoJSONResponse = {
|
|
10
|
+
sources: { [key: string]: SourceSpecification | VectorSourceSpecification }
|
|
11
|
+
layers: LayerSpecification[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export abstract class BaseImporter {
|
|
15
|
+
protected urls: string[]
|
|
16
|
+
|
|
17
|
+
constructor(original_urls: string) {
|
|
18
|
+
const temp_urls = original_urls + ''
|
|
19
|
+
this.urls = temp_urls.split(',')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
abstract getJSON(url: string): Promise<TileInfoJSONResponse>
|
|
23
|
+
|
|
24
|
+
async import(style: StyleSpecification) {
|
|
25
|
+
if (this.urls.length === 0) return
|
|
26
|
+
const responses = await Promise.all(
|
|
27
|
+
this.urls.map((url) => this.getJSON(url)),
|
|
28
|
+
)
|
|
29
|
+
responses.forEach((res) => {
|
|
30
|
+
Object.keys(res.sources).forEach((sourceName) => {
|
|
31
|
+
style.sources[sourceName] = res.sources[sourceName]
|
|
32
|
+
})
|
|
33
|
+
if (res.layers.length > 0) {
|
|
34
|
+
style.layers = style.layers.concat(res.layers)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
return style
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected getVectorLayers(name: string, vector_layers: VectorLayer[]) {
|
|
41
|
+
const layers: LayerSpecification[] = []
|
|
42
|
+
if (vector_layers) {
|
|
43
|
+
vector_layers.forEach((layer) => {
|
|
44
|
+
const layerStyle: LayerSpecification = {
|
|
45
|
+
id: layer.id,
|
|
46
|
+
type: 'fill',
|
|
47
|
+
source: name,
|
|
48
|
+
'source-layer': layer.id,
|
|
49
|
+
layout: {},
|
|
50
|
+
paint: {},
|
|
51
|
+
}
|
|
52
|
+
layers.push(layerStyle)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
return layers
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { MetadataJSON, VectorLayer } from '../../types'
|
|
3
|
+
import {
|
|
4
|
+
LayerSpecification,
|
|
5
|
+
VectorSourceSpecification,
|
|
6
|
+
} from '@maplibre/maplibre-gl-style-spec/types'
|
|
7
|
+
import { BaseImporter, TileInfoJSONResponse } from './base-importer'
|
|
8
|
+
|
|
9
|
+
export class MetadataJSONImporter extends BaseImporter {
|
|
10
|
+
async getJSON(url: string): Promise<TileInfoJSONResponse> {
|
|
11
|
+
const res = await axios.get(url)
|
|
12
|
+
const matadataJSON: MetadataJSON = res.data
|
|
13
|
+
const metadataName: string = matadataJSON.name
|
|
14
|
+
? matadataJSON.name
|
|
15
|
+
: Math.random().toString(32).substring(2)
|
|
16
|
+
|
|
17
|
+
const sources: { [key: string]: VectorSourceSpecification } = {}
|
|
18
|
+
const urlSplited = url.split('/')
|
|
19
|
+
sources[metadataName] = {
|
|
20
|
+
type: 'vector',
|
|
21
|
+
tiles: [
|
|
22
|
+
url.replace(
|
|
23
|
+
new RegExp(urlSplited[urlSplited.length - 1], 'g'),
|
|
24
|
+
'{z}/{x}/{y}.pbf',
|
|
25
|
+
),
|
|
26
|
+
],
|
|
27
|
+
}
|
|
28
|
+
if (matadataJSON.minzoom) {
|
|
29
|
+
sources[metadataName].minzoom = matadataJSON.minzoom
|
|
30
|
+
}
|
|
31
|
+
if (matadataJSON.maxzoom) {
|
|
32
|
+
sources[metadataName].maxzoom = matadataJSON.maxzoom
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let layers: LayerSpecification[] = []
|
|
36
|
+
if (matadataJSON.json) {
|
|
37
|
+
const vector_layers: VectorLayer[] = JSON.parse(
|
|
38
|
+
matadataJSON.json,
|
|
39
|
+
).vector_layers
|
|
40
|
+
layers = this.getVectorLayers(metadataName, vector_layers)
|
|
41
|
+
}
|
|
42
|
+
return { sources, layers }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { TileJSON } from '../../types'
|
|
3
|
+
import {
|
|
4
|
+
SourceSpecification,
|
|
5
|
+
LayerSpecification,
|
|
6
|
+
} from '@maplibre/maplibre-gl-style-spec/types'
|
|
7
|
+
import { BaseImporter, TileInfoJSONResponse } from './base-importer'
|
|
8
|
+
|
|
9
|
+
export class TileJSONImporter extends BaseImporter {
|
|
10
|
+
async getJSON(url: string): Promise<TileInfoJSONResponse> {
|
|
11
|
+
const res = await axios.get(url)
|
|
12
|
+
const tilejson: TileJSON = res.data
|
|
13
|
+
const tilesetName: string = tilejson.name
|
|
14
|
+
? tilejson.name
|
|
15
|
+
: Math.random().toString(32).substring(2)
|
|
16
|
+
|
|
17
|
+
const sources: { [key: string]: SourceSpecification } = {}
|
|
18
|
+
sources[tilesetName] = {
|
|
19
|
+
type: 'vector',
|
|
20
|
+
url: url,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const layers: LayerSpecification[] = this.getVectorLayers(
|
|
24
|
+
tilesetName,
|
|
25
|
+
tilejson.vector_layers,
|
|
26
|
+
)
|
|
27
|
+
return { sources, layers }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
const maplibreStyleSpec = require('@maplibre/maplibre-gl-style-spec')
|
|
2
2
|
const mapboxStyleSpec = require('@mapbox/mapbox-gl-style-spec')
|
|
3
|
+
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec/types'
|
|
3
4
|
|
|
4
|
-
export function validateStyle(
|
|
5
|
+
export function validateStyle(
|
|
6
|
+
style: StyleSpecification,
|
|
7
|
+
provider = 'default',
|
|
8
|
+
): void {
|
|
5
9
|
let result = []
|
|
6
10
|
if ('mapbox' === provider) {
|
|
7
11
|
result = mapboxStyleSpec.validate(style)
|
|
@@ -17,6 +21,8 @@ export function validateStyle(style: object, provider: string = "default"): void
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
if (errors.length) {
|
|
20
|
-
throw `\u001b[31mError:\u001b[0m ${errors.join(
|
|
24
|
+
throw `\u001b[31mError:\u001b[0m ${errors.join(
|
|
25
|
+
'\n\u001b[31mError:\u001b[0m ',
|
|
26
|
+
)}`
|
|
21
27
|
}
|
|
22
28
|
}
|
package/src/lib/yaml-parser.ts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import YAML from 'js-yaml'
|
|
3
|
+
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec/types'
|
|
3
4
|
|
|
4
5
|
const yamlinc = require('yaml-include')
|
|
5
6
|
|
|
6
7
|
interface StyleObject {
|
|
7
|
-
[key: string]:
|
|
8
|
+
[key: string]: string
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function parser(file: string):
|
|
11
|
+
export function parser(file: string): StyleSpecification {
|
|
11
12
|
yamlinc.setBaseFile(file)
|
|
12
13
|
const yaml = fs.readFileSync(file, 'utf8')
|
|
13
14
|
|
|
14
15
|
const obj: StyleObject = YAML.load(yaml, {
|
|
15
16
|
schema: yamlinc.YAML_INCLUDE_SCHEMA,
|
|
16
17
|
filename: file,
|
|
17
|
-
json: true
|
|
18
|
+
json: true,
|
|
18
19
|
}) as StyleObject
|
|
19
20
|
|
|
20
21
|
const styleObj: StyleObject = {}
|
|
21
|
-
|
|
22
|
+
const variables: StyleObject = {}
|
|
22
23
|
|
|
23
|
-
for (const key in obj
|
|
24
|
+
for (const key in obj) {
|
|
24
25
|
if (key.match(/^\$/)) {
|
|
25
26
|
variables[key] = obj[key]
|
|
26
27
|
} else {
|
|
@@ -29,8 +30,8 @@ export function parser(file: string): object {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Handle all nested variables.
|
|
32
|
-
while(JSON.stringify(Object.values(variables)).match(/\$/)) {
|
|
33
|
-
for (const key in variables
|
|
33
|
+
while (JSON.stringify(Object.values(variables)).match(/\$/)) {
|
|
34
|
+
for (const key in variables) {
|
|
34
35
|
for (const variable in variables) {
|
|
35
36
|
let _value = JSON.stringify(variables[key])
|
|
36
37
|
const regex = new RegExp(`\"\\${variable}\"`, 'g')
|
|
@@ -40,7 +41,7 @@ export function parser(file: string): object {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
let style = JSON.stringify(styleObj)
|
|
44
|
+
let style: string = JSON.stringify(styleObj)
|
|
44
45
|
|
|
45
46
|
for (const key in variables) {
|
|
46
47
|
const regex = new RegExp(`\"\\${key}\"`, 'g')
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import YAML from 'js-yaml'
|
|
4
|
+
import {
|
|
5
|
+
StyleSpecification,
|
|
6
|
+
LayerSpecification,
|
|
7
|
+
} from '@maplibre/maplibre-gl-style-spec/types'
|
|
8
|
+
|
|
9
|
+
export const writeYaml = (
|
|
10
|
+
destinationPath: string,
|
|
11
|
+
style: StyleSpecification,
|
|
12
|
+
composite = false,
|
|
13
|
+
) => {
|
|
14
|
+
if (composite === true) {
|
|
15
|
+
writeCompositedYaml(destinationPath, style)
|
|
16
|
+
} else {
|
|
17
|
+
writeDecompositedYaml(destinationPath, style)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const writeCompositedYaml = (
|
|
22
|
+
destinationPath: string,
|
|
23
|
+
style: StyleSpecification,
|
|
24
|
+
) => {
|
|
25
|
+
const styleYAML = YAML.dump(style)
|
|
26
|
+
let stylePath = path.resolve(process.cwd(), destinationPath)
|
|
27
|
+
|
|
28
|
+
// The `source` is absolute path.
|
|
29
|
+
if (destinationPath.match(/^\//)) {
|
|
30
|
+
stylePath = destinationPath
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
fs.writeFileSync(stylePath, styleYAML)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
throw `${stylePath}: Permission denied`
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const writeDecompositedYaml = (
|
|
41
|
+
destinationPath: string,
|
|
42
|
+
style: StyleSpecification,
|
|
43
|
+
) => {
|
|
44
|
+
const layers: LayerSpecification[] = []
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < style.layers.length; i++) {
|
|
47
|
+
const layer = style.layers[i]
|
|
48
|
+
const layerYml = YAML.dump(layer)
|
|
49
|
+
const fileName = `${style.layers[i].id}.yml`
|
|
50
|
+
const dirName = path.join(path.dirname(destinationPath), 'layers')
|
|
51
|
+
fs.mkdirSync(dirName, { recursive: true })
|
|
52
|
+
fs.writeFileSync(path.join(dirName, fileName), layerYml)
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
layers.push(`!!inc/file ${path.join('layers', fileName)}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
style.layers = layers
|
|
58
|
+
|
|
59
|
+
fs.writeFileSync(
|
|
60
|
+
destinationPath,
|
|
61
|
+
YAML.dump(style).replace(
|
|
62
|
+
/'\!\!inc\/file layers\/.+\.yml'/g,
|
|
63
|
+
function (match) {
|
|
64
|
+
return match.replace(/'/g, '')
|
|
65
|
+
},
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Definition of metadata.json
|
|
3
|
+
*/
|
|
4
|
+
export type MetadataJSON = {
|
|
5
|
+
name?: string
|
|
6
|
+
description?: string
|
|
7
|
+
version?: string
|
|
8
|
+
minzoom?: number
|
|
9
|
+
maxzoom?: number
|
|
10
|
+
center?: number[]
|
|
11
|
+
bounds?: number[]
|
|
12
|
+
type?: string
|
|
13
|
+
format: string
|
|
14
|
+
generator?: string
|
|
15
|
+
json?: string
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { VectorLayer } from './vector_layers'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Definition of TileJSON v3.0.0
|
|
5
|
+
* see https://github.com/mapbox/tilejson-spec/blob/master/3.0.0/schema.json
|
|
6
|
+
*/
|
|
7
|
+
export type TileJSON = {
|
|
8
|
+
tilejson: string
|
|
9
|
+
tiles: string[]
|
|
10
|
+
vector_layers: VectorLayer[]
|
|
11
|
+
attribution?: string
|
|
12
|
+
bounds?: number[]
|
|
13
|
+
center?: number[]
|
|
14
|
+
data?: string[]
|
|
15
|
+
description?: string
|
|
16
|
+
fillzoom?: number
|
|
17
|
+
grids?: string[]
|
|
18
|
+
legend?: string
|
|
19
|
+
maxzoom?: number
|
|
20
|
+
minzoom?: number
|
|
21
|
+
name?: string
|
|
22
|
+
scheme?: string
|
|
23
|
+
template?: string
|
|
24
|
+
version?: string
|
|
25
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Definition of vector_layers object
|
|
3
|
+
* see https://github.com/mapbox/tilejson-spec/blob/master/3.0.0/schema.json
|
|
4
|
+
*/
|
|
5
|
+
export type VectorLayer = {
|
|
6
|
+
id: string
|
|
7
|
+
fields: { [key: string]: string }
|
|
8
|
+
description: string
|
|
9
|
+
maxzoom: string
|
|
10
|
+
minzoom: string
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { assert } from 'chai'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import os from 'os'
|
|
5
|
+
|
|
6
|
+
import { buildSprite } from '../src/lib/build-sprite'
|
|
7
|
+
const iconsPath = path.join(__dirname, 'data/icons')
|
|
8
|
+
let tmpdir = ''
|
|
9
|
+
|
|
10
|
+
describe('Test for the `build-sprite.ts`.', () => {
|
|
11
|
+
beforeEach(function () {
|
|
12
|
+
tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'charites-'))
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it("should create icon's json/png at specified path and name ", async () => {
|
|
16
|
+
const expectedJsonPath = path.join(tmpdir, 'basic.json')
|
|
17
|
+
const expectedPngPath = path.join(tmpdir, 'basic.png')
|
|
18
|
+
|
|
19
|
+
await buildSprite(iconsPath, tmpdir, `basic`)
|
|
20
|
+
|
|
21
|
+
assert.deepEqual(true, !!fs.statSync(expectedJsonPath))
|
|
22
|
+
assert.deepEqual(true, !!fs.statSync(expectedPngPath))
|
|
23
|
+
})
|
|
24
|
+
})
|