@signalk/charts-plugin 2.3.1 → 3.1.0

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.
Files changed (41) hide show
  1. package/README.md +80 -17
  2. package/package.json +27 -8
  3. package/plugin/charts.d.ts +1 -0
  4. package/plugin/charts.js +243 -0
  5. package/plugin/constants.d.ts +4 -0
  6. package/plugin/constants.js +7 -0
  7. package/plugin/index.d.ts +1 -0
  8. package/plugin/index.js +353 -0
  9. package/plugin/types.d.ts +38 -0
  10. package/plugin/types.js +2 -0
  11. package/.github/workflows/publish.yml +0 -18
  12. package/.github/workflows/release_on_tag.yml +0 -27
  13. package/.github/workflows/require_pr_label.yml +0 -13
  14. package/.github/workflows/test.yml +0 -22
  15. package/index.js +0 -1
  16. package/src/charts.js +0 -175
  17. package/src/constants.js +0 -3
  18. package/src/plugin.js +0 -231
  19. package/test/charts/empty-test/.gitkeep +0 -0
  20. package/test/charts/test.mbtiles +0 -0
  21. package/test/charts/tms-tiles/4/8/10.png +0 -0
  22. package/test/charts/tms-tiles/4/8/11.png +0 -0
  23. package/test/charts/tms-tiles/4/9/10.png +0 -0
  24. package/test/charts/tms-tiles/4/9/11.png +0 -0
  25. package/test/charts/tms-tiles/5/16/21.png +0 -0
  26. package/test/charts/tms-tiles/5/16/22.png +0 -0
  27. package/test/charts/tms-tiles/5/16/23.png +0 -0
  28. package/test/charts/tms-tiles/5/17/21.png +0 -0
  29. package/test/charts/tms-tiles/5/17/22.png +0 -0
  30. package/test/charts/tms-tiles/5/17/23.png +0 -0
  31. package/test/charts/tms-tiles/5/18/21.png +0 -0
  32. package/test/charts/tms-tiles/5/18/22.png +0 -0
  33. package/test/charts/tms-tiles/5/18/23.png +0 -0
  34. package/test/charts/tms-tiles/tilemapresource.xml +0 -14
  35. package/test/charts/unpacked-tiles/3/2/3.png +0 -0
  36. package/test/charts/unpacked-tiles/4/4/6.png +0 -0
  37. package/test/charts/unpacked-tiles/4/5/6.png +0 -0
  38. package/test/charts/unpacked-tiles/metadata.json +0 -15
  39. package/test/charts-2/test2.mbtiles +0 -0
  40. package/test/expected-charts.json +0 -53
  41. package/test/plugin-test.js +0 -196
@@ -0,0 +1,353 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const bluebird = __importStar(require("bluebird"));
30
+ const path_1 = __importDefault(require("path"));
31
+ const fs_1 = __importDefault(require("fs"));
32
+ const _ = __importStar(require("lodash"));
33
+ const charts_1 = require("./charts");
34
+ const constants_1 = require("./constants");
35
+ const MIN_ZOOM = 1;
36
+ const MAX_ZOOM = 24;
37
+ const chartTilesPath = '/signalk/chart-tiles';
38
+ module.exports = (app) => {
39
+ let chartProviders = {};
40
+ let pluginStarted = false;
41
+ let props = {
42
+ chartPaths: [],
43
+ onlineChartProviders: []
44
+ };
45
+ const configBasePath = app.config.configPath;
46
+ const defaultChartsPath = path_1.default.join(configBasePath, '/charts');
47
+ const serverMajorVersion = app.config.version
48
+ ? parseInt(app.config.version.split('.')[0])
49
+ : '1';
50
+ ensureDirectoryExists(defaultChartsPath);
51
+ // ******** REQUIRED PLUGIN DEFINITION *******
52
+ const CONFIG_SCHEMA = {
53
+ title: 'Signal K Charts',
54
+ type: 'object',
55
+ properties: {
56
+ chartPaths: {
57
+ type: 'array',
58
+ title: 'Chart paths',
59
+ description: `Add one or more paths to find charts. Defaults to "${defaultChartsPath}"`,
60
+ items: {
61
+ type: 'string',
62
+ title: 'Path',
63
+ description: `Path for chart files, relative to "${configBasePath}"`
64
+ }
65
+ },
66
+ onlineChartProviders: {
67
+ type: 'array',
68
+ title: 'Online chart providers',
69
+ items: {
70
+ type: 'object',
71
+ title: 'Provider',
72
+ required: ['name', 'minzoom', 'maxzoom', 'format', 'url'],
73
+ properties: {
74
+ name: {
75
+ type: 'string',
76
+ title: 'Name'
77
+ },
78
+ description: {
79
+ type: 'string',
80
+ title: 'Description'
81
+ },
82
+ minzoom: {
83
+ type: 'number',
84
+ title: `Minimum zoom level, between [${MIN_ZOOM}, ${MAX_ZOOM}]`,
85
+ maximum: MAX_ZOOM,
86
+ minimum: MIN_ZOOM,
87
+ default: MIN_ZOOM
88
+ },
89
+ maxzoom: {
90
+ type: 'number',
91
+ title: `Maximum zoom level, between [${MIN_ZOOM}, ${MAX_ZOOM}]`,
92
+ maximum: MAX_ZOOM,
93
+ minimum: MIN_ZOOM,
94
+ default: 15
95
+ },
96
+ serverType: {
97
+ type: 'string',
98
+ title: 'Map source / server type',
99
+ default: 'tilelayer',
100
+ enum: [
101
+ 'tilelayer',
102
+ 'S-57',
103
+ 'WMS',
104
+ 'WMTS',
105
+ 'mapstyleJSON',
106
+ 'tileJSON'
107
+ ],
108
+ description: 'Map data source type served by the supplied url. (Use tilelayer for xyz / tms tile sources.)'
109
+ },
110
+ format: {
111
+ type: 'string',
112
+ title: 'Format',
113
+ default: 'png',
114
+ enum: ['png', 'jpg', 'pbf'],
115
+ description: 'Format of map tiles: raster (png, jpg, etc.) / vector (pbf).'
116
+ },
117
+ url: {
118
+ type: 'string',
119
+ title: 'URL',
120
+ description: 'Map URL (for tilelayer include {z}, {x} and {y} parameters, e.g. "http://example.org/{z}/{x}/{y}.png")'
121
+ },
122
+ style: {
123
+ type: 'string',
124
+ title: 'Vector Map Style',
125
+ description: 'Path to file containing map style definitions for Vector maps (e.g. "http://example.org/styles/mymapstyle.json")'
126
+ },
127
+ layers: {
128
+ type: 'array',
129
+ title: 'Layers',
130
+ description: 'List of map layer ids to display. (Use with WMS / WMTS types.)',
131
+ items: {
132
+ title: 'Layer Name',
133
+ description: 'Name of layer to display',
134
+ type: 'string'
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ };
142
+ const CONFIG_UISCHEMA = {};
143
+ const plugin = {
144
+ id: 'charts',
145
+ name: 'Signal K Charts',
146
+ schema: () => CONFIG_SCHEMA,
147
+ uiSchema: () => CONFIG_UISCHEMA,
148
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
+ start: (settings) => {
150
+ return doStartup(settings); // return required for tests
151
+ },
152
+ stop: () => {
153
+ app.setPluginStatus('stopped');
154
+ }
155
+ };
156
+ const doStartup = (config) => {
157
+ app.debug(`** loaded config: ${config}`);
158
+ props = { ...config };
159
+ const chartPaths = _.isEmpty(props.chartPaths)
160
+ ? [defaultChartsPath]
161
+ : resolveUniqueChartPaths(props.chartPaths, configBasePath);
162
+ const onlineProviders = _.reduce(props.onlineChartProviders, (result, data) => {
163
+ const provider = convertOnlineProviderConfig(data);
164
+ result[provider.identifier] = provider;
165
+ return result;
166
+ }, {});
167
+ app.debug(`Start charts plugin. Chart paths: ${chartPaths.join(', ')}, online charts: ${Object.keys(onlineProviders).length}`);
168
+ // Do not register routes if plugin has been started once already
169
+ pluginStarted === false && registerRoutes();
170
+ pluginStarted = true;
171
+ const urlBase = `${app.config.ssl ? 'https' : 'http'}://localhost:${'getExternalPort' in app.config ? app.config.getExternalPort() : 3000}`;
172
+ app.debug(`**urlBase** ${urlBase}`);
173
+ app.setPluginStatus('Started');
174
+ const loadProviders = bluebird
175
+ .mapSeries(chartPaths, (chartPath) => (0, charts_1.findCharts)(chartPath))
176
+ .then((list) => _.reduce(list, (result, charts) => _.merge({}, result, charts), {}));
177
+ return loadProviders
178
+ .then((charts) => {
179
+ app.debug(`Chart plugin: Found ${_.keys(charts).length} charts from ${chartPaths.join(', ')}.`);
180
+ chartProviders = _.merge({}, charts, onlineProviders);
181
+ })
182
+ .catch((e) => {
183
+ console.error(`Error loading chart providers`, e.message);
184
+ chartProviders = {};
185
+ app.setPluginError(`Error loading chart providers`);
186
+ });
187
+ };
188
+ const registerRoutes = () => {
189
+ app.debug('** Registering API paths **');
190
+ app.get(`${chartTilesPath}/:identifier/:z([0-9]*)/:x([0-9]*)/:y([0-9]*)`, async (req, res) => {
191
+ const { identifier, z, x, y } = req.params;
192
+ const provider = chartProviders[identifier];
193
+ if (!provider) {
194
+ return res.sendStatus(404);
195
+ }
196
+ switch (provider._fileFormat) {
197
+ case 'directory':
198
+ return serveTileFromFilesystem(res, provider, parseInt(z), parseInt(x), parseInt(y));
199
+ case 'mbtiles':
200
+ return serveTileFromMbtiles(res, provider, parseInt(z), parseInt(x), parseInt(y));
201
+ default:
202
+ console.log(`Unknown chart provider fileformat ${provider._fileFormat}`);
203
+ res.status(500).send();
204
+ }
205
+ });
206
+ app.debug('** Registering v1 API paths **');
207
+ app.get(constants_1.apiRoutePrefix[1] + '/charts/:identifier', (req, res) => {
208
+ const { identifier } = req.params;
209
+ const provider = chartProviders[identifier];
210
+ if (provider) {
211
+ return res.json(sanitizeProvider(provider));
212
+ }
213
+ else {
214
+ return res.status(404).send('Not found');
215
+ }
216
+ });
217
+ app.get(constants_1.apiRoutePrefix[1] + '/charts', (req, res) => {
218
+ const sanitized = _.mapValues(chartProviders, (provider) => sanitizeProvider(provider));
219
+ res.json(sanitized);
220
+ });
221
+ // v2 routes
222
+ if (serverMajorVersion === 2) {
223
+ app.debug('** Registering v2 API paths **');
224
+ registerAsProvider();
225
+ }
226
+ };
227
+ // Resources API provider registration
228
+ const registerAsProvider = () => {
229
+ app.debug('** Registering as Resource Provider for `charts` **');
230
+ try {
231
+ app.registerResourceProvider({
232
+ type: 'charts',
233
+ methods: {
234
+ listResources: (params) => {
235
+ app.debug(`** listResources() ${params}`);
236
+ return Promise.resolve(_.mapValues(chartProviders, (provider) => sanitizeProvider(provider, 2)));
237
+ },
238
+ getResource: (id) => {
239
+ app.debug(`** getResource() ${id}`);
240
+ const provider = chartProviders[id];
241
+ if (provider) {
242
+ return Promise.resolve(sanitizeProvider(provider, 2));
243
+ }
244
+ else {
245
+ throw new Error('Chart not found!');
246
+ }
247
+ },
248
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
249
+ setResource: (id, value) => {
250
+ throw new Error(`Not implemented!\n Cannot set ${id} to ${value}`);
251
+ },
252
+ deleteResource: (id) => {
253
+ throw new Error(`Not implemented!\n Cannot delete ${id}`);
254
+ }
255
+ }
256
+ });
257
+ }
258
+ catch (error) {
259
+ app.debug('Failed Provider Registration!');
260
+ }
261
+ };
262
+ return plugin;
263
+ };
264
+ const responseHttpOptions = {
265
+ headers: {
266
+ 'Cache-Control': 'public, max-age=7776000' // 90 days
267
+ }
268
+ };
269
+ const resolveUniqueChartPaths = (chartPaths, configBasePath) => {
270
+ const paths = _.map(chartPaths, (chartPath) => path_1.default.resolve(configBasePath, chartPath));
271
+ return _.uniq(paths);
272
+ };
273
+ const convertOnlineProviderConfig = (provider) => {
274
+ const id = _.kebabCase(_.deburr(provider.name));
275
+ const data = {
276
+ identifier: id,
277
+ name: provider.name,
278
+ description: provider.description,
279
+ bounds: [-180, -90, 180, 90],
280
+ minzoom: Math.min(Math.max(1, provider.minzoom), 19),
281
+ maxzoom: Math.min(Math.max(1, provider.maxzoom), 19),
282
+ format: provider.format,
283
+ scale: 250000,
284
+ type: provider.serverType ? provider.serverType : 'tilelayer',
285
+ style: provider.style ? provider.style : null,
286
+ v1: {
287
+ tilemapUrl: provider.url,
288
+ chartLayers: provider.layers ? provider.layers : null
289
+ },
290
+ v2: {
291
+ url: provider.url,
292
+ layers: provider.layers ? provider.layers : null
293
+ }
294
+ };
295
+ return data;
296
+ };
297
+ const sanitizeProvider = (provider, version = 1) => {
298
+ let v;
299
+ if (version === 1) {
300
+ v = _.merge({}, provider.v1);
301
+ v.tilemapUrl = v.tilemapUrl.replace('~tilePath~', chartTilesPath);
302
+ }
303
+ else if (version === 2) {
304
+ v = _.merge({}, provider.v2);
305
+ v.url = v.url ? v.url.replace('~tilePath~', chartTilesPath) : '';
306
+ }
307
+ provider = _.omit(provider, [
308
+ '_filePath',
309
+ '_fileFormat',
310
+ '_mbtilesHandle',
311
+ '_flipY',
312
+ 'v1',
313
+ 'v2'
314
+ ]);
315
+ return _.merge(provider, v);
316
+ };
317
+ const ensureDirectoryExists = (path) => {
318
+ if (!fs_1.default.existsSync(path)) {
319
+ fs_1.default.mkdirSync(path);
320
+ }
321
+ };
322
+ const serveTileFromFilesystem = (res, provider, z, x, y) => {
323
+ const { format, _flipY, _filePath } = provider;
324
+ const flippedY = Math.pow(2, z) - 1 - y;
325
+ const file = _filePath
326
+ ? path_1.default.resolve(_filePath, `${z}/${x}/${_flipY ? flippedY : y}.${format}`)
327
+ : '';
328
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
329
+ res.sendFile(file, responseHttpOptions, (err) => {
330
+ if (err && err.code === 'ENOENT') {
331
+ res.sendStatus(404);
332
+ }
333
+ else if (err) {
334
+ throw err;
335
+ }
336
+ });
337
+ };
338
+ const serveTileFromMbtiles = (res, provider, z, x, y) => {
339
+ provider._mbtilesHandle.getTile(z, x, y, (err, tile, headers) => {
340
+ if (err && err.message && err.message === 'Tile does not exist') {
341
+ res.sendStatus(404);
342
+ }
343
+ else if (err) {
344
+ console.error(`Error fetching tile ${provider.identifier}/${z}/${x}/${y}:`, err);
345
+ res.sendStatus(500);
346
+ }
347
+ else {
348
+ headers['Cache-Control'] = responseHttpOptions.headers['Cache-Control'];
349
+ res.writeHead(200, headers);
350
+ res.end(tile);
351
+ }
352
+ });
353
+ };
@@ -0,0 +1,38 @@
1
+ type MapSourceType = 'tilelayer' | 'S-57' | 'WMS' | 'WMTS' | 'mapstyleJSON' | 'tileJSON';
2
+ export interface ChartProvider {
3
+ _fileFormat?: 'mbtiles' | 'directory';
4
+ _filePath: string;
5
+ _mbtilesHandle?: any;
6
+ _flipY?: boolean;
7
+ identifier: string;
8
+ name: string;
9
+ description: string;
10
+ type: MapSourceType;
11
+ scale: number;
12
+ v1?: {
13
+ tilemapUrl: string;
14
+ chartLayers?: string[];
15
+ };
16
+ v2?: {
17
+ url: string;
18
+ layers?: string[];
19
+ };
20
+ bounds?: number[];
21
+ minzoom?: number;
22
+ maxzoom?: number;
23
+ format?: string;
24
+ style?: string;
25
+ layers?: string[];
26
+ }
27
+ export interface OnlineChartProvider {
28
+ name: string;
29
+ description: string;
30
+ minzoom: number;
31
+ maxzoom: number;
32
+ serverType: MapSourceType;
33
+ format: 'png' | 'jpg';
34
+ url: string;
35
+ style: string;
36
+ layers: string[];
37
+ }
38
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,18 +0,0 @@
1
- name: Publish to npm
2
- on:
3
- workflow_run:
4
- workflows: ["Release on tag"]
5
- types: [completed]
6
-
7
- jobs:
8
- build:
9
- runs-on: ubuntu-latest
10
- steps:
11
- - uses: actions/checkout@v2
12
- - uses: actions/setup-node@v1
13
- with:
14
- node-version: '16.x'
15
- registry-url: 'https://registry.npmjs.org'
16
- - run: npm publish --access public
17
- env:
18
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,27 +0,0 @@
1
- name: 'Release on tag'
2
- on:
3
- push:
4
- tags:
5
- - '*'
6
-
7
- jobs:
8
- release:
9
- permissions:
10
- contents: write
11
- if: startsWith(github.ref, 'refs/tags/')
12
- runs-on: ubuntu-latest
13
- steps:
14
- - name: Build Changelog
15
- id: github_release
16
- uses: mikepenz/release-changelog-builder-action@v1
17
- env:
18
- GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }}
19
-
20
- - name: Create Release
21
- uses: actions/create-release@v1
22
- with:
23
- tag_name: ${{ github.ref }}
24
- release_name: ${{ github.ref }}
25
- body: ${{steps.github_release.outputs.changelog}}
26
- env:
27
- GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }}
@@ -1,13 +0,0 @@
1
- name: Pull Request Labels
2
- on:
3
- pull_request:
4
- types: [opened, labeled, unlabeled, synchronize]
5
- jobs:
6
- label:
7
- runs-on: ubuntu-latest
8
- steps:
9
- - uses: mheap/github-action-required-labels@v1
10
- with:
11
- mode: exactly
12
- count: 1
13
- labels: "fix, feature, doc, chore, test, ignore, other, dependencies"
@@ -1,22 +0,0 @@
1
- name: CI test
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
-
9
- strategy:
10
- matrix:
11
- node-version: [10.x, 16.x]
12
-
13
- steps:
14
- - uses: actions/checkout@v2
15
- - name: Use Node.js ${{ matrix.node-version }}
16
- uses: actions/setup-node@v1
17
- with:
18
- node-version: ${{ matrix.node-version }}
19
- - run: npm install
20
- - run: npm test
21
- env:
22
- CI: true
package/index.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require('./src/plugin')
package/src/charts.js DELETED
@@ -1,175 +0,0 @@
1
- const debug = require('debug')('signalk-charts-plugin')
2
- const Promise = require('bluebird')
3
- const path = require('path')
4
- const MBTiles = require('@mapbox/mbtiles')
5
- const xml2js = require('xml2js')
6
- const fs = Promise.promisifyAll(require('fs'))
7
- const _ = require('lodash')
8
- const {apiRoutePrefix} = require('./constants')
9
-
10
- function findCharts(chartBaseDir) {
11
- return fs
12
- .readdirAsync(chartBaseDir)
13
- .then(files => {
14
- return Promise.mapSeries(files, filename => {
15
- const isMbtilesFile = filename.match(/\.mbtiles$/i)
16
- const file = path.resolve(chartBaseDir, filename)
17
- const isDirectory = fs.statSync(file).isDirectory()
18
- if (isMbtilesFile) {
19
- return openMbtilesFile(file, filename)
20
- } else if (isDirectory) {
21
- return directoryToMapInfo(file, filename)
22
- } else {
23
- return Promise.resolve(null)
24
- }
25
- })
26
- })
27
- .then(result => _.filter(result, _.identity))
28
- .then(charts => _.reduce(charts, (result, chart) => {
29
- result[chart.identifier] = chart
30
- return result
31
- }, {}))
32
- .catch(err => {
33
- console.error(`Error reading charts directory ${chartBaseDir}:${err.message}`)
34
- })
35
- }
36
-
37
- function openMbtilesFile(file, filename) {
38
- return new Promise((resolve, reject) => {
39
- new MBTiles(file, (err, mbtiles) => {
40
- if (err) {
41
- return reject(err)
42
- }
43
- mbtiles.getInfo((err, metadata) => {
44
- if (err) {
45
- return reject(err)
46
- }
47
-
48
- return resolve({mbtiles, metadata})
49
- })
50
- })
51
- }).then(({mbtiles, metadata}) => {
52
- if (_.isEmpty(metadata) || metadata.bounds === undefined) {
53
- return null
54
- }
55
- const identifier = filename.replace(/\.mbtiles$/i, '')
56
- return {
57
- _fileFormat: 'mbtiles',
58
- _mbtilesHandle: mbtiles,
59
- _flipY: false,
60
- identifier,
61
- name: metadata.name || metadata.id,
62
- description: metadata.description,
63
- bounds: metadata.bounds,
64
- minzoom: metadata.minzoom,
65
- maxzoom: metadata.maxzoom,
66
- format: metadata.format,
67
- type: 'tilelayer',
68
- tilemapUrl: `${apiRoutePrefix}/charts/${identifier}/{z}/{x}/{y}`,
69
- scale: metadata.scale || '250000'
70
- }
71
- }).catch(e => {
72
- console.error(`Error loading chart ${file}`, e.message)
73
- return null
74
- })
75
- }
76
-
77
- function parseTilemapResource(tilemapResource) {
78
- return fs
79
- .readFileAsync(tilemapResource)
80
- .then(Promise.promisify(xml2js.parseString))
81
- .then(parsed => {
82
- const result = parsed.TileMap
83
- const name = _.get(result, 'Title.0')
84
- const format = _.get(result, 'TileFormat.0.$.extension')
85
- const scale = _.get(result, 'Metadata.0.$.scale')
86
- const bbox = _.get(result, 'BoundingBox.0.$')
87
- const zoomLevels = _.map(_.get(result, 'TileSets.0.TileSet')||[], set => parseInt(_.get(set, '$.href')))
88
- return {
89
- _flipY: true,
90
- name,
91
- description: name,
92
- bounds: bbox ? [parseFloat(bbox.minx), parseFloat(bbox.miny), parseFloat(bbox.maxx), parseFloat(bbox.maxy)] : undefined,
93
- minzoom: !_.isEmpty(zoomLevels) ? _.min(zoomLevels) : undefined,
94
- maxzoom: !_.isEmpty(zoomLevels) ? _.max(zoomLevels) : undefined,
95
- format,
96
- type: 'tilelayer',
97
- scale: scale || '250000'
98
- }
99
- })
100
- }
101
-
102
- function parseMetadataJson(metadataJson) {
103
- return fs
104
- .readFileAsync(metadataJson)
105
- .then(JSON.parse)
106
- .then(metadata => {
107
- function parseBounds(bounds) {
108
- if (_.isString(bounds)) {
109
- return _.map(bounds.split(','), bound => parseFloat(_.trim(bound)))
110
- } else if (_.isArray(bounds) && bounds.length === 4) {
111
- return bounds
112
- } else {
113
- return undefined
114
- }
115
- }
116
- return {
117
- _flipY: false,
118
- name: metadata.name || metadata.id,
119
- description: metadata.description,
120
- bounds: parseBounds(metadata.bounds),
121
- minzoom: parseIntIfNotUndefined(metadata.minzoom),
122
- maxzoom: parseIntIfNotUndefined(metadata.maxzoom),
123
- format: metadata.format,
124
- type: 'tilelayer',
125
- scale: metadata.scale || '250000'
126
- }
127
- })
128
- }
129
-
130
- function directoryToMapInfo(file, identifier) {
131
- function loadInfo() {
132
- const tilemapResource = path.join(file, 'tilemapresource.xml')
133
- const metadataJson = path.join(file, 'metadata.json')
134
-
135
- const hasTilemapResource = fs.existsSync(tilemapResource)
136
- const hasMetadataJson = fs.existsSync(metadataJson)
137
- if (hasTilemapResource) {
138
- return parseTilemapResource(tilemapResource)
139
- } else if (hasMetadataJson) {
140
- return parseMetadataJson(metadataJson)
141
- } else {
142
- return Promise.resolve(null)
143
- }
144
- }
145
-
146
- return loadInfo()
147
- .then(info => {
148
- if (info) {
149
- if (!info.format) {
150
- console.error(`Missing format metadata for chart ${identifier}`)
151
- return null
152
- }
153
- return _.merge(info, {
154
- identifier,
155
- _fileFormat: 'directory',
156
- _filePath: file,
157
- tilemapUrl: `${apiRoutePrefix}/charts/${identifier}/{z}/{x}/{y}`,
158
- })
159
- }
160
- return null
161
- })
162
- .catch(e => {
163
- console.error(`Error getting charts from ${file}`, e.message)
164
- return undefined
165
- })
166
- }
167
-
168
- function parseIntIfNotUndefined(val) {
169
- const parsed = parseInt(val)
170
- return _.isFinite(parsed) ? parsed : undefined
171
- }
172
-
173
- module.exports = {
174
- findCharts
175
- }
package/src/constants.js DELETED
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- apiRoutePrefix: '/signalk/v1/api/resources'
3
- }