Skip to content

Commit e11a360

Browse files
authored
Serve map tiles from url that is not under resources/charts API endpoint (#37)
To better support the use of multiple chart providers with the Signal K v2 Resources API, this PR moves the endpoint that serves tile images out from under the path /signalk/{v1 | v2}/api/resources/charts to /signalk/chart-tiles. This provides more flexibility for chart providers and also moves the serving of tile images out from under the Resources API which is based on JSON formatted responses.
1 parent 1fdd79a commit e11a360

File tree

8 files changed

+118
-64
lines changed

8 files changed

+118
-64
lines changed

README.md

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,74 @@
22

33
Signal K Node server plugin to provide chart metadata, such as name, description and location of the actual chart tile data.
44

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.
612

713
| Server Version | API | Path |
814
|--- |--- |--- |
915
| 1.x.x | v1 | `/signalk/v1/api/resources/charts` |
1016
| 2.x.x | v2 | `/signalk/v2/api/resources/charts` |
1117

1218

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_
1420

15-
### Usage
21+
## Usage
1622

17-
1. Install "Signal K Charts" plugin from Signal K Appstore
23+
1. Install `@signalk/signalk-charts` from the Signal K Server Appstore
1824

19-
2. Configure plugin in **Plugin Config**
25+
2. Configure the plugin in the Admin UI _(**Server -> Plugin Config -> Signal K Charts**)_
2026

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
2228

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)
2332

24-
3. Add "Chart paths" in plugin configuration. Defaults to `${signalk-configuration-path}/charts`
2533

26-
<img src="https://user-images.githubusercontent.com/1435910/39382493-57c1e4dc-4a6e-11e8-93e1-cedb4c7662f4.png" alt="Chart paths configuration" width="450"/>
34+
## Configuration
2735

2836

29-
4. Put charts into selected paths
37+
### Local Chart Files
3038

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.
3241

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.
3445

46+
<img src="https://user-images.githubusercontent.com/1435910/39382493-57c1e4dc-4a6e-11e8-93e1-cedb4c7662f4.png" alt="Chart paths configuration" width="450"/>
3547

48+
>**Note:** After chart files have been added to folders they will be processed after the plugin has been restarted! _(disable / enable the plugin)_
3649
3750

38-
_WMS example:_
51+
### Online chart providers
3952

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.
4154

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"/>
4368

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)
4769

4870
### Supported chart formats
4971

50-
- [MBTiles](https://github.com/mapbox/mbtiles-spec) file
72+
- [MBTiles](https://github.com/mapbox/mbtiles-spec) files
5173
- Directory with cached [TMS](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification) tiles and `tilemapresource.xml`
5274
- Directory with XYZ tiles and `metadata.json`
5375
- Online [TMS](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification)
@@ -57,13 +79,34 @@ Publicly available MBTiles charts can be found from:
5779
- [Finnish Transport Agency nautical charts](https://github.com/vokkim/rannikkokartat-mbtiles)
5880
- [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)
5981

82+
83+
---
84+
6085
### API
6186

6287
Plugin adds support for `/resources/charts` endpoints described in [Signal K specification](http://signalk.org/specification/1.0.0/doc/otherBranches.html#resourcescharts):
6388

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+
```
67110

68111
License
69112
-------

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,29 @@
3131
},
3232
"dependencies": {
3333
"@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",
3635
"bluebird": "3.5.1",
3736
"lodash": "^4.17.11",
38-
"xml2js": "0.4.19"
37+
"xml2js": "0.6.2"
3938
},
4039
"repository": {
4140
"type": "git",
4241
"url": "https://github.com/SignalK/charts-plugin"
4342
},
4443
"devDependencies": {
44+
"@types/baconjs": "^0.7.34",
4545
"@types/express": "^4.17.17",
4646
"@types/lodash": "^4.14.191",
4747
"@types/node": "^18.14.4",
4848
"@typescript-eslint/eslint-plugin": "^5.52.0",
4949
"@typescript-eslint/parser": "^5.52.0",
50-
"body-parser": "1.18.2",
50+
"body-parser": "^1.18.2",
5151
"chai": "4.1.2",
5252
"chai-http": "^4.2.1",
5353
"eslint": "^8.34.0",
5454
"eslint-config-prettier": "^8.6.0",
55-
"express": "4.19.2",
56-
"mocha": "5.0.0",
55+
"express": "^4.19.2",
56+
"mocha": "^11.7.1",
5757
"prettier": "^2.8.4",
5858
"typescript": "^4.5.4"
5959
}

src/charts.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ function openMbtilesFile(file: string, filename: string) {
8080
type: 'tilelayer',
8181
scale: parseInt(res.metadata.scale) || 250000,
8282
v1: {
83-
tilemapUrl: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
83+
tilemapUrl: `~tilePath~/${identifier}/{z}/{x}/{y}`,
8484
chartLayers: res.metadata.vector_layers
8585
? parseVectorLayers(res.metadata.vector_layers)
8686
: []
8787
},
8888
v2: {
89-
url: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
89+
url: `~tilePath~/${identifier}/{z}/{x}/{y}`,
9090
layers: res.metadata.vector_layers
9191
? parseVectorLayers(res.metadata.vector_layers)
9292
: []
@@ -133,11 +133,11 @@ function directoryToMapInfo(file: string, identifier: string) {
133133
;(info._fileFormat = 'directory'),
134134
(info._filePath = file),
135135
(info.v1 = {
136-
tilemapUrl: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
136+
tilemapUrl: `~tilePath~/${identifier}/{z}/{x}/{y}`,
137137
chartLayers: []
138138
})
139139
info.v2 = {
140-
url: `~basePath~/charts/${identifier}/{z}/{x}/{y}`,
140+
url: `~tilePath~/${identifier}/{z}/{x}/{y}`,
141141
layers: []
142142
}
143143

src/index.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,19 @@ import { Request, Response, Application } from 'express'
99
import { OutgoingHttpHeaders } from 'http'
1010
import {
1111
Plugin,
12-
PluginServerApp,
12+
ServerAPI,
1313
ResourceProviderRegistry
1414
} from '@signalk/server-api'
1515

16-
const MIN_ZOOM = 1
17-
const MAX_ZOOM = 24
18-
1916
interface Config {
2017
chartPaths: string[]
2118
onlineChartProviders: OnlineChartProvider[]
2219
}
2320

2421
interface ChartProviderApp
25-
extends PluginServerApp,
22+
extends ServerAPI,
2623
ResourceProviderRegistry,
2724
Application {
28-
statusMessage?: () => string
29-
error: (msg: string) => void
30-
debug: (...msg: unknown[]) => void
31-
setPluginStatus: (pluginId: string, status?: string) => void
32-
setPluginError: (pluginId: string, status?: string) => void
3325
config: {
3426
ssl: boolean
3527
configPath: string
@@ -38,6 +30,10 @@ interface ChartProviderApp
3830
}
3931
}
4032

33+
const MIN_ZOOM = 1
34+
const MAX_ZOOM = 24
35+
const chartTilesPath = '/signalk/chart-tiles'
36+
4137
module.exports = (app: ChartProviderApp): Plugin => {
4238
let chartProviders: { [key: string]: ChartProvider } = {}
4339
let pluginStarted = false
@@ -47,7 +43,9 @@ module.exports = (app: ChartProviderApp): Plugin => {
4743
}
4844
const configBasePath = app.config.configPath
4945
const defaultChartsPath = path.join(configBasePath, '/charts')
50-
const serverMajorVersion = app.config.version ? parseInt(app.config.version.split('.')[0]) : '1'
46+
const serverMajorVersion = app.config.version
47+
? parseInt(app.config.version.split('.')[0])
48+
: '1'
5149
ensureDirectoryExists(defaultChartsPath)
5250

5351
// ******** REQUIRED PLUGIN DEFINITION *******
@@ -99,7 +97,14 @@ module.exports = (app: ChartProviderApp): Plugin => {
9997
type: 'string',
10098
title: 'Map source / server type',
10199
default: 'tilelayer',
102-
enum: ['tilelayer', 'S-57', 'WMS', 'WMTS', 'mapstyleJSON', 'tileJSON'],
100+
enum: [
101+
'tilelayer',
102+
'S-57',
103+
'WMS',
104+
'WMTS',
105+
'mapstyleJSON',
106+
'tileJSON'
107+
],
103108
description:
104109
'Map data source type served by the supplied url. (Use tilelayer for xyz / tms tile sources.)'
105110
},
@@ -157,7 +162,7 @@ module.exports = (app: ChartProviderApp): Plugin => {
157162
}
158163

159164
const doStartup = (config: Config) => {
160-
app.debug('** loaded config: ', config)
165+
app.debug(`** loaded config: ${config}`)
161166
props = { ...config }
162167

163168
const chartPaths = _.isEmpty(props.chartPaths)
@@ -185,7 +190,7 @@ module.exports = (app: ChartProviderApp): Plugin => {
185190
const urlBase = `${app.config.ssl ? 'https' : 'http'}://localhost:${
186191
'getExternalPort' in app.config ? app.config.getExternalPort() : 3000
187192
}`
188-
app.debug('**urlBase**', urlBase)
193+
app.debug(`**urlBase** ${urlBase}`)
189194
app.setPluginStatus('Started')
190195

191196
const loadProviders = bluebird
@@ -214,7 +219,7 @@ module.exports = (app: ChartProviderApp): Plugin => {
214219
app.debug('** Registering API paths **')
215220

216221
app.get(
217-
`/signalk/:version(v[1-2])/api/resources/charts/:identifier/:z([0-9]*)/:x([0-9]*)/:y([0-9]*)`,
222+
`${chartTilesPath}/:identifier/:z([0-9]*)/:x([0-9]*)/:y([0-9]*)`,
218223
async (req: Request, res: Response) => {
219224
const { identifier, z, x, y } = req.params
220225
const provider = chartProviders[identifier]
@@ -286,15 +291,15 @@ module.exports = (app: ChartProviderApp): Plugin => {
286291
listResources: (params: {
287292
[key: string]: number | string | object | null
288293
}) => {
289-
app.debug(`** listResources()`, params)
294+
app.debug(`** listResources() ${params}`)
290295
return Promise.resolve(
291296
_.mapValues(chartProviders, (provider) =>
292297
sanitizeProvider(provider, 2)
293298
)
294299
)
295300
},
296301
getResource: (id: string) => {
297-
app.debug(`** getResource()`, id)
302+
app.debug(`** getResource() ${id}`)
298303
const provider = chartProviders[id]
299304
if (provider) {
300305
return Promise.resolve(sanitizeProvider(provider, 2))
@@ -364,10 +369,10 @@ const sanitizeProvider = (provider: ChartProvider, version = 1) => {
364369
let v
365370
if (version === 1) {
366371
v = _.merge({}, provider.v1)
367-
v.tilemapUrl = v.tilemapUrl.replace('~basePath~', apiRoutePrefix[1])
372+
v.tilemapUrl = v.tilemapUrl.replace('~tilePath~', chartTilesPath)
368373
} else if (version === 2) {
369374
v = _.merge({}, provider.v2)
370-
v.url = v.url ? v.url.replace('~basePath~', apiRoutePrefix[2]) : ''
375+
v.url = v.url ? v.url.replace('~tilePath~', chartTilesPath) : ''
371376
}
372377
provider = _.omit(provider, [
373378
'_filePath',

src/types.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
type MapSourceType = 'tilelayer' | 'S-57' | 'WMS' | 'WMTS' | 'mapstyleJSON' | 'tileJSON'
1+
type MapSourceType =
2+
| 'tilelayer'
3+
| 'S-57'
4+
| 'WMS'
5+
| 'WMTS'
6+
| 'mapstyleJSON'
7+
| 'tileJSON'
28

39
export interface ChartProvider {
410
_fileFormat?: 'mbtiles' | 'directory'
@@ -13,11 +19,11 @@ export interface ChartProvider {
1319
scale: number
1420
v1?: {
1521
tilemapUrl: string
16-
chartLayers: string[]
22+
chartLayers?: string[]
1723
}
1824
v2?: {
1925
url: string
20-
layers: string[]
26+
layers?: string[]
2127
}
2228
bounds?: number[]
2329
minzoom?: number

test/expected-charts.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"minzoom": 3,
1515
"name": "MBTILES_19",
1616
"scale": 250000,
17-
"tilemapUrl": "/signalk/v1/api/resources/charts/test/{z}/{x}/{y}",
17+
"tilemapUrl": "/signalk/chart-tiles/test/{z}/{x}/{y}",
1818
"type": "tilelayer"
1919
},
2020
"tms-tiles": {
@@ -32,7 +32,7 @@
3232
"minzoom": 4,
3333
"name": "Översikt Svenska Sjökort",
3434
"scale": 4000000,
35-
"tilemapUrl": "/signalk/v1/api/resources/charts/tms-tiles/{z}/{x}/{y}",
35+
"tilemapUrl": "/signalk/chart-tiles/tms-tiles/{z}/{x}/{y}",
3636
"type": "tilelayer"
3737
},
3838
"unpacked-tiles": {
@@ -50,7 +50,7 @@
5050
"minzoom": 3,
5151
"name": "NOAA MBTiles test file",
5252
"scale": 250000,
53-
"tilemapUrl": "/signalk/v1/api/resources/charts/unpacked-tiles/{z}/{x}/{y}",
53+
"tilemapUrl": "/signalk/chart-tiles/unpacked-tiles/{z}/{x}/{y}",
5454
"type": "tilelayer"
5555
}
5656
}

0 commit comments

Comments
 (0)