@signalk/charts-plugin 3.0.0 → 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.
package/README.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  Signal K Node server plugin to provide chart metadata, such as name, description and location of the actual chart tile data.
4
4
 
5
- Supports both v1 and v2 Signal K resources api paths.
5
+ Chart metadata is derived from the following supported chart file types:
6
+ - MBTiles _(.mbtiles)_
7
+ - TMS _(tilemapresource.xml and tiles)_
8
+
9
+ Additionally, chart metadata can be entered via the plugin configuration for other chart sources and types _(e.g. WMS, WMTS, S-57 tiles and tilejson)_.
10
+
11
+ Chart metadata is made available to both v1 and v2 Signal K `resources` api paths.
6
12
 
7
13
  | Server Version | API | Path |
8
14
  |--- |--- |--- |
@@ -10,44 +16,60 @@ Supports both v1 and v2 Signal K resources api paths.
10
16
  | 2.x.x | v2 | `/signalk/v2/api/resources/charts` |
11
17
 
12
18
 
13
- _Note: v2 resource paths will only be made available on Signal K server >= v2._
19
+ _Note: Version 2 resource paths will only be made available on Signal K server v2.0.0 and later_
14
20
 
15
- ### Usage
21
+ ## Usage
16
22
 
17
- 1. Install "Signal K Charts" plugin from Signal K Appstore
23
+ 1. Install `@signalk/signalk-charts` from the Signal K Server Appstore
18
24
 
19
- 2. Configure plugin in **Plugin Config**
25
+ 2. Configure the plugin in the Admin UI _(**Server -> Plugin Config -> Signal K Charts**)_
20
26
 
21
- - Add "Chart paths" which are the paths to the folders where chart files are stored. Defaults to `${signalk-configuration-path}/charts`
27
+ 3. Activate the plugin
22
28
 
29
+ Chart metadata will then be available to client apps via the resources api `/resources/charts` for example:
30
+ - [Freeboard SK](https://www.npmjs.com/package/@signalk/freeboard-sk)
31
+ - [Tuktuk Chart Plotter](https://www.npmjs.com/package/tuktuk-chart-plotter)
23
32
 
24
- 3. Add "Chart paths" in plugin configuration. Defaults to `${signalk-configuration-path}/charts`
25
33
 
26
- <img src="https://user-images.githubusercontent.com/1435910/39382493-57c1e4dc-4a6e-11e8-93e1-cedb4c7662f4.png" alt="Chart paths configuration" width="450"/>
34
+ ## Configuration
27
35
 
28
36
 
29
- 4. Put charts into selected paths
37
+ ### Local Chart Files
30
38
 
31
- 5. Add optional online chart providers
39
+ To use chart files stored on the Signal K Server the plugin needs to know where your local chart files
40
+ are stored to generate the chart metadata.
32
41
 
33
- <img src="https://user-images.githubusercontent.com/1435910/45048136-c65d2e80-b083-11e8-99db-01e8cece9f89.png" alt="Online chart providers configuration" width="450"/>
42
+ You can either:
43
+ 1. Put the chart files in the default location _(`/home/<user>/.signalk/charts`)_
44
+ 2. Add configuration entries for the folders where the chart files are stored.
34
45
 
46
+ <img src="https://user-images.githubusercontent.com/1435910/39382493-57c1e4dc-4a6e-11e8-93e1-cedb4c7662f4.png" alt="Chart paths configuration" width="450"/>
35
47
 
48
+ >**Note:** After chart files have been added to folders they will be processed after the plugin has been restarted! _(disable / enable the plugin)_
36
49
 
37
50
 
38
- _WMS example:_
51
+ ### Online chart providers
39
52
 
40
- <img src="https://user-images.githubusercontent.com/38519157/102832518-90077100-443e-11eb-9a1d-d0806bb2b10b.png" alt="server type configuration" width="450"/>
53
+ If your chart source is not local to the Signal K Server you can add "Online Chart Providers" and enter the required charts metadata for the source.
41
54
 
42
- 6. Activate plugin
55
+ You will need to provide the following information:
56
+ 1. A chart name for client applications to display
57
+ 2. The URL to the chart source
58
+ 3. Select the chart image format
59
+ 4. The minimum and maximum zoom levels where chart data is available.
60
+
61
+ You can also provide a description detailing the chart content.
62
+
63
+ <img src="https://github.com/user-attachments/assets/77cb3aaf-5471-4e55-b05d-aad70cacab6a" alt="Online chart providers configuration" width="450"/>
64
+
65
+ For WMS & WMTS sources you can specify the layers you wish to display.
66
+
67
+ <img src="https://github.com/user-attachments/assets/b9bfba38-8468-4eca-aeb3-96a80fcbc7a6" alt="Online chart provider layers" width="450"/>
43
68
 
44
- 7. Use one of the client apps supporting Signal K charts, for example:
45
- - [Freeboard SK](https://www.npmjs.com/package/@signalk/freeboard-sk)
46
- - [Tuktuk Chart Plotter](https://www.npmjs.com/package/tuktuk-chart-plotter)
47
69
 
48
70
  ### Supported chart formats
49
71
 
50
- - [MBTiles](https://github.com/mapbox/mbtiles-spec) file
72
+ - [MBTiles](https://github.com/mapbox/mbtiles-spec) files
51
73
  - Directory with cached [TMS](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification) tiles and `tilemapresource.xml`
52
74
  - Directory with XYZ tiles and `metadata.json`
53
75
  - Online [TMS](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification)
@@ -57,13 +79,34 @@ Publicly available MBTiles charts can be found from:
57
79
  - [Finnish Transport Agency nautical charts](https://github.com/vokkim/rannikkokartat-mbtiles)
58
80
  - [Signal K World Coastline Map](https://github.com/netAction/signalk-world-coastline-map), download [MBTiles release](https://github.com/netAction/signalk-world-coastline-map/releases/download/v1.0/signalk-world-coastline-map-database.tgz)
59
81
 
82
+
83
+ ---
84
+
60
85
  ### API
61
86
 
62
87
  Plugin adds support for `/resources/charts` endpoints described in [Signal K specification](http://signalk.org/specification/1.0.0/doc/otherBranches.html#resourcescharts):
63
88
 
64
- - `GET /signalk/v1/api/resources/charts/` returns metadata for all available charts
65
- - `GET /signalk/v1/api/resources/charts/${identifier}/` returns metadata for selected chart
66
- - `GET /signalk/v1/api/resources/charts/${identifier}/${z}/${x}/${y}` returns a single tile for selected offline chart. As charts-plugin isn't proxy, online charts is not available via this request. You should look the metadata to find proper request.
89
+ #### List available charts
90
+
91
+ ```bash
92
+ GET /signalk/v2/api/resources/charts/`
93
+ ```
94
+
95
+ #### Return metadata for selected chart
96
+
97
+ ```bash
98
+
99
+ GET /signalk/v2/api/resources/charts/${identifier}`
100
+ ```
101
+
102
+ #### Chart Tiles
103
+ Chart tiles are retrieved using the url defined in the chart metadata.
104
+
105
+ For local chart files located in the Chart Path(s) defined in the plugin configuration, the url will be:
106
+
107
+ ```bash
108
+ /signalk/chart-tiles/${identifier}/${z}/${x}/${y}
109
+ ```
67
110
 
68
111
  License
69
112
  -------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalk/charts-plugin",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Signal K plugin to provide chart support for Signal K server",
5
5
  "main": "plugin/index.js",
6
6
  "scripts": {
@@ -31,29 +31,29 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@mapbox/mbtiles": "^0.12.1",
34
- "@signalk/server-api": "^2.0.0-beta.3",
35
- "baconjs": "1.0.1",
34
+ "@signalk/server-api": "^2.0.0",
36
35
  "bluebird": "3.5.1",
37
36
  "lodash": "^4.17.11",
38
- "xml2js": "0.4.19"
37
+ "xml2js": "0.6.2"
39
38
  },
40
39
  "repository": {
41
40
  "type": "git",
42
41
  "url": "https://github.com/SignalK/charts-plugin"
43
42
  },
44
43
  "devDependencies": {
44
+ "@types/baconjs": "^0.7.34",
45
45
  "@types/express": "^4.17.17",
46
46
  "@types/lodash": "^4.14.191",
47
47
  "@types/node": "^18.14.4",
48
48
  "@typescript-eslint/eslint-plugin": "^5.52.0",
49
49
  "@typescript-eslint/parser": "^5.52.0",
50
- "body-parser": "1.18.2",
50
+ "body-parser": "^1.18.2",
51
51
  "chai": "4.1.2",
52
52
  "chai-http": "^4.2.1",
53
53
  "eslint": "^8.34.0",
54
54
  "eslint-config-prettier": "^8.6.0",
55
- "express": "4.16.2",
56
- "mocha": "5.0.0",
55
+ "express": "^4.19.2",
56
+ "mocha": "^11.7.1",
57
57
  "prettier": "^2.8.4",
58
58
  "typescript": "^4.5.4"
59
59
  }
package/plugin/charts.js CHANGED
@@ -22,15 +22,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
35
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
27
  };
@@ -110,13 +101,13 @@ function openMbtilesFile(file, filename) {
110
101
  type: 'tilelayer',
111
102
  scale: parseInt(res.metadata.scale) || 250000,
112
103
  v1: {
113
- tilemapUrl: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
104
+ tilemapUrl: `~tilePath~/${identifier}/{z}/{x}/{y}`,
114
105
  chartLayers: res.metadata.vector_layers
115
106
  ? parseVectorLayers(res.metadata.vector_layers)
116
107
  : []
117
108
  },
118
109
  v2: {
119
- url: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
110
+ url: `~tilePath~/${identifier}/{z}/{x}/{y}`,
120
111
  layers: res.metadata.vector_layers
121
112
  ? parseVectorLayers(res.metadata.vector_layers)
122
113
  : []
@@ -133,24 +124,22 @@ function parseVectorLayers(layers) {
133
124
  return layers.map((l) => l.id);
134
125
  }
135
126
  function directoryToMapInfo(file, identifier) {
136
- function loadInfo() {
137
- return __awaiter(this, void 0, void 0, function* () {
138
- const tilemapResource = path_1.default.join(file, 'tilemapresource.xml');
139
- const metadataJson = path_1.default.join(file, 'metadata.json');
127
+ async function loadInfo() {
128
+ const tilemapResource = path_1.default.join(file, 'tilemapresource.xml');
129
+ const metadataJson = path_1.default.join(file, 'metadata.json');
130
+ try {
131
+ await fs_1.promises.stat(tilemapResource);
132
+ return parseTilemapResource(tilemapResource);
133
+ }
134
+ catch {
140
135
  try {
141
- yield fs_1.promises.stat(tilemapResource);
142
- return parseTilemapResource(tilemapResource);
136
+ await fs_1.promises.stat(metadataJson);
137
+ return parseMetadataJson(metadataJson);
143
138
  }
144
- catch (_a) {
145
- try {
146
- yield fs_1.promises.stat(metadataJson);
147
- return parseMetadataJson(metadataJson);
148
- }
149
- catch (_b) {
150
- return null;
151
- }
139
+ catch {
140
+ return null;
152
141
  }
153
- });
142
+ }
154
143
  }
155
144
  return loadInfo()
156
145
  .then((info) => {
@@ -163,11 +152,11 @@ function directoryToMapInfo(file, identifier) {
163
152
  (info._fileFormat = 'directory'),
164
153
  (info._filePath = file),
165
154
  (info.v1 = {
166
- tilemapUrl: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
155
+ tilemapUrl: `~tilePath~/${identifier}/{z}/{x}/{y}`,
167
156
  chartLayers: []
168
157
  });
169
158
  info.v2 = {
170
- url: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
159
+ url: `~tilePath~/${identifier}/{z}/{x}/{y}`,
171
160
  layers: []
172
161
  };
173
162
  return info;
package/plugin/index.js CHANGED
@@ -22,15 +22,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
35
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
27
  };
@@ -43,6 +34,7 @@ const charts_1 = require("./charts");
43
34
  const constants_1 = require("./constants");
44
35
  const MIN_ZOOM = 1;
45
36
  const MAX_ZOOM = 24;
37
+ const chartTilesPath = '/signalk/chart-tiles';
46
38
  module.exports = (app) => {
47
39
  let chartProviders = {};
48
40
  let pluginStarted = false;
@@ -52,7 +44,9 @@ module.exports = (app) => {
52
44
  };
53
45
  const configBasePath = app.config.configPath;
54
46
  const defaultChartsPath = path_1.default.join(configBasePath, '/charts');
55
- const serverMajorVersion = app.config.version ? parseInt(app.config.version.split('.')[0]) : '1';
47
+ const serverMajorVersion = app.config.version
48
+ ? parseInt(app.config.version.split('.')[0])
49
+ : '1';
56
50
  ensureDirectoryExists(defaultChartsPath);
57
51
  // ******** REQUIRED PLUGIN DEFINITION *******
58
52
  const CONFIG_SCHEMA = {
@@ -103,7 +97,14 @@ module.exports = (app) => {
103
97
  type: 'string',
104
98
  title: 'Map source / server type',
105
99
  default: 'tilelayer',
106
- enum: ['tilelayer', 'S-57', 'WMS', 'WMTS', 'mapstyleJSON', 'tileJSON'],
100
+ enum: [
101
+ 'tilelayer',
102
+ 'S-57',
103
+ 'WMS',
104
+ 'WMTS',
105
+ 'mapstyleJSON',
106
+ 'tileJSON'
107
+ ],
107
108
  description: 'Map data source type served by the supplied url. (Use tilelayer for xyz / tms tile sources.)'
108
109
  },
109
110
  format: {
@@ -153,8 +154,8 @@ module.exports = (app) => {
153
154
  }
154
155
  };
155
156
  const doStartup = (config) => {
156
- app.debug('** loaded config: ', config);
157
- props = Object.assign({}, config);
157
+ app.debug(`** loaded config: ${config}`);
158
+ props = { ...config };
158
159
  const chartPaths = _.isEmpty(props.chartPaths)
159
160
  ? [defaultChartsPath]
160
161
  : resolveUniqueChartPaths(props.chartPaths, configBasePath);
@@ -168,7 +169,7 @@ module.exports = (app) => {
168
169
  pluginStarted === false && registerRoutes();
169
170
  pluginStarted = true;
170
171
  const urlBase = `${app.config.ssl ? 'https' : 'http'}://localhost:${'getExternalPort' in app.config ? app.config.getExternalPort() : 3000}`;
171
- app.debug('**urlBase**', urlBase);
172
+ app.debug(`**urlBase** ${urlBase}`);
172
173
  app.setPluginStatus('Started');
173
174
  const loadProviders = bluebird
174
175
  .mapSeries(chartPaths, (chartPath) => (0, charts_1.findCharts)(chartPath))
@@ -186,7 +187,7 @@ module.exports = (app) => {
186
187
  };
187
188
  const registerRoutes = () => {
188
189
  app.debug('** Registering API paths **');
189
- app.get(`/signalk/:version(v[1-2])/api/resources/charts/:identifier/:z([0-9]*)/:x([0-9]*)/:y([0-9]*)`, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
190
+ app.get(`${chartTilesPath}/:identifier/:z([0-9]*)/:x([0-9]*)/:y([0-9]*)`, async (req, res) => {
190
191
  const { identifier, z, x, y } = req.params;
191
192
  const provider = chartProviders[identifier];
192
193
  if (!provider) {
@@ -201,7 +202,7 @@ module.exports = (app) => {
201
202
  console.log(`Unknown chart provider fileformat ${provider._fileFormat}`);
202
203
  res.status(500).send();
203
204
  }
204
- }));
205
+ });
205
206
  app.debug('** Registering v1 API paths **');
206
207
  app.get(constants_1.apiRoutePrefix[1] + '/charts/:identifier', (req, res) => {
207
208
  const { identifier } = req.params;
@@ -231,11 +232,11 @@ module.exports = (app) => {
231
232
  type: 'charts',
232
233
  methods: {
233
234
  listResources: (params) => {
234
- app.debug(`** listResources()`, params);
235
+ app.debug(`** listResources() ${params}`);
235
236
  return Promise.resolve(_.mapValues(chartProviders, (provider) => sanitizeProvider(provider, 2)));
236
237
  },
237
238
  getResource: (id) => {
238
- app.debug(`** getResource()`, id);
239
+ app.debug(`** getResource() ${id}`);
239
240
  const provider = chartProviders[id];
240
241
  if (provider) {
241
242
  return Promise.resolve(sanitizeProvider(provider, 2));
@@ -297,11 +298,11 @@ const sanitizeProvider = (provider, version = 1) => {
297
298
  let v;
298
299
  if (version === 1) {
299
300
  v = _.merge({}, provider.v1);
300
- v.tilemapUrl = v.tilemapUrl.replace('~basePath~', constants_1.apiRoutePrefix[1]);
301
+ v.tilemapUrl = v.tilemapUrl.replace('~tilePath~', chartTilesPath);
301
302
  }
302
303
  else if (version === 2) {
303
304
  v = _.merge({}, provider.v2);
304
- v.url = v.url ? v.url.replace('~basePath~', constants_1.apiRoutePrefix[2]) : '';
305
+ v.url = v.url ? v.url.replace('~tilePath~', chartTilesPath) : '';
305
306
  }
306
307
  provider = _.omit(provider, [
307
308
  '_filePath',
package/plugin/types.d.ts CHANGED
@@ -11,11 +11,11 @@ export interface ChartProvider {
11
11
  scale: number;
12
12
  v1?: {
13
13
  tilemapUrl: string;
14
- chartLayers: string[];
14
+ chartLayers?: string[];
15
15
  };
16
16
  v2?: {
17
17
  url: string;
18
- layers: string[];
18
+ layers?: string[];
19
19
  };
20
20
  bounds?: number[];
21
21
  minzoom?: number;