signalk-livedepth-plugin 0.2.1 → 0.3.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/CHANGELOG.md +67 -0
- package/README.md +67 -41
- package/dist/collector.d.ts +1 -1
- package/dist/collector.d.ts.map +1 -1
- package/dist/config.d.ts +3 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -3
- package/dist/config.js.map +1 -1
- package/dist/plotterext/manifest.d.ts +24 -0
- package/dist/plotterext/manifest.d.ts.map +1 -0
- package/dist/plotterext/manifest.js +78 -0
- package/dist/plotterext/manifest.js.map +1 -0
- package/dist/plotterext/provider.d.ts +7 -0
- package/dist/plotterext/provider.d.ts.map +1 -0
- package/dist/plotterext/provider.js +61 -0
- package/dist/plotterext/provider.js.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +19 -5
- package/dist/plugin.js.map +1 -1
- package/dist/streams/live.d.ts +1 -1
- package/dist/streams/live.d.ts.map +1 -1
- package/dist/streams/live.js +1 -1
- package/dist/streams/live.js.map +1 -1
- package/dist/tiles/api.d.ts +21 -11
- package/dist/tiles/api.d.ts.map +1 -1
- package/dist/tiles/api.js +36 -27
- package/dist/tiles/api.js.map +1 -1
- package/dist/tiles/core/accumulator.d.ts +37 -0
- package/dist/tiles/core/accumulator.d.ts.map +1 -0
- package/dist/tiles/core/accumulator.js +75 -0
- package/dist/tiles/core/accumulator.js.map +1 -0
- package/dist/tiles/core/correction.d.ts +32 -0
- package/dist/tiles/core/correction.d.ts.map +1 -0
- package/dist/tiles/core/correction.js +43 -0
- package/dist/tiles/core/correction.js.map +1 -0
- package/dist/tiles/core/grid.d.ts +64 -0
- package/dist/tiles/core/grid.d.ts.map +1 -0
- package/dist/tiles/core/grid.js +127 -0
- package/dist/tiles/core/grid.js.map +1 -0
- package/dist/tiles/core/index.d.ts +7 -0
- package/dist/tiles/core/index.d.ts.map +1 -0
- package/dist/tiles/core/index.js +7 -0
- package/dist/tiles/core/index.js.map +1 -0
- package/dist/tiles/core/store.d.ts +37 -0
- package/dist/tiles/core/store.d.ts.map +1 -0
- package/dist/tiles/core/store.js +86 -0
- package/dist/tiles/core/store.js.map +1 -0
- package/dist/tiles/core/tile-source.d.ts +41 -0
- package/dist/tiles/core/tile-source.d.ts.map +1 -0
- package/dist/tiles/core/tile-source.js +86 -0
- package/dist/tiles/core/tile-source.js.map +1 -0
- package/dist/tiles/core/types.d.ts +11 -0
- package/dist/tiles/core/types.d.ts.map +1 -0
- package/dist/tiles/core/types.js +2 -0
- package/dist/tiles/core/types.js.map +1 -0
- package/dist/tiles/index.d.ts +1 -1
- package/dist/tiles/index.d.ts.map +1 -1
- package/dist/tiles/index.js +1 -1
- package/dist/tiles/index.js.map +1 -1
- package/dist/tiles/seed.d.ts +1 -1
- package/dist/tiles/seed.d.ts.map +1 -1
- package/dist/tiles/seed.js +1 -1
- package/dist/tiles/seed.js.map +1 -1
- package/dist/tiles/store.d.ts +1 -1
- package/dist/tiles/store.d.ts.map +1 -1
- package/dist/tiles/store.js +1 -1
- package/dist/tiles/store.js.map +1 -1
- package/dist/tiles/writer.d.ts +1 -1
- package/dist/tiles/writer.d.ts.map +1 -1
- package/dist/tiles/writer.js +1 -1
- package/dist/tiles/writer.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -6
- package/public/config.html +14 -0
- package/public/index.html +15 -0
- package/public/js/config.js +901 -0
- package/public/js/config.js.map +7 -0
- package/public/js/render-worker.js +706 -0
- package/public/js/render-worker.js.map +7 -0
- package/public/js/renderer.js +1273 -0
- package/public/js/renderer.js.map +7 -0
- package/public/renderer.html +14 -0
- package/public/styles.css +110 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here, newest first. The
|
|
4
|
+
format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
5
|
+
versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - 2026-10-15
|
|
8
|
+
|
|
9
|
+
The LiveDepth chart layer, and the merge that brings everything into one
|
|
10
|
+
package. The chart layer needs a host that implements `overlays.image` —
|
|
11
|
+
Freeboard-SK **3.4.0-beta.0 or later**.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- The **LiveDepth chart layer**: an image map overlay offered to
|
|
16
|
+
plotter-extensions chartplotters (Freeboard-SK 3.4.0-beta.0 or later), drawn
|
|
17
|
+
by a headless background runtime that shades the depth surface, draws isobaths
|
|
18
|
+
and
|
|
19
|
+
labels the shoals. It appears in the chartplotter's Chart list with show/hide
|
|
20
|
+
and opacity controls, and its settings open from the layer's row or the
|
|
21
|
+
LiveDepth toolbar button.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- The shared tile algorithm (`signalk-livedepth-tiles`) is folded into this
|
|
26
|
+
package under `src/tiles/core/`; that published package is no longer a
|
|
27
|
+
dependency.
|
|
28
|
+
- Bathymetry data is served from the extension's own top-level path
|
|
29
|
+
(`/plotterext/signalk-livedepth-plugin/bathymetry/…`) so a read-only user's
|
|
30
|
+
chartplotter can reach it; the one-time history rebuild stays an admin route.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- The config schema no longer emits a `default: undefined`, which the server
|
|
35
|
+
dropped when storing the schema as JSON.
|
|
36
|
+
- Switching the depth source now takes effect: the live Signal K subscription
|
|
37
|
+
starts and stops with the source.
|
|
38
|
+
- The settings panel greys the history-only fields while the depth source is the
|
|
39
|
+
plugin's tiles, and shows depth values in the user's preferred unit.
|
|
40
|
+
|
|
41
|
+
## [0.2.1] - 2026-10-14
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Serve tiles as raw bytes, not JSON — express was JSON-encoding the `Uint8Array`
|
|
46
|
+
`encodeTile` returns, so a client decoded the JSON text as sparse records.
|
|
47
|
+
- Read the vessel length overall from `design.length`, so transducer correction
|
|
48
|
+
no longer reports it missing on servers that have it.
|
|
49
|
+
|
|
50
|
+
## [0.2.0] - 2026-10-14
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- Transducer position correction.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- Trimmed the configuration to the depth path, the transducer's position, the
|
|
59
|
+
correction mode and the one-time history rebuild.
|
|
60
|
+
|
|
61
|
+
## [0.1.0] - 2026-10-13
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- First release: build a gridded bathymetry tile store from the vessel's own
|
|
66
|
+
position and depth, serve it to a chart client, and seed it from the server's
|
|
67
|
+
History API.
|
package/README.md
CHANGED
|
@@ -1,54 +1,80 @@
|
|
|
1
|
-
#
|
|
1
|
+
# LiveDepth
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
**LiveDepth builds and visualises a depth map from your own soundings, in real
|
|
4
|
+
time, as your vessel moves.** Everywhere you go, your depth sounder's readings
|
|
5
|
+
are turned into bathymetric data you can see on the chart — so the ground you've
|
|
6
|
+
already covered is there to look at the next time you're in the area.
|
|
6
7
|
|
|
7
8
|
## What it does
|
|
8
9
|
|
|
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
|
-
- **Depth
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
10
|
+
- **Records as you go.** Each depth reading is placed where your vessel was when
|
|
11
|
+
it was taken, building a picture of the seabed along your track. No surveying
|
|
12
|
+
needed — just your normal boating.
|
|
13
|
+
- **Draws it on the chart.** A compatible chartplotter (Freeboard-SK) shows the
|
|
14
|
+
depth as a shaded surface with depth-contour lines, so you can see the shape of
|
|
15
|
+
the ground and where it shallows.
|
|
16
|
+
- **Never hides a shoal.** Where readings overlap, the shallowest one wins, so
|
|
17
|
+
zooming out can't bury a shallow patch under the deeper water around it.
|
|
18
|
+
- **Live or accumulated.** Show your own recent track, or the depth map the
|
|
19
|
+
server has built up over time — extended live as you move.
|
|
20
|
+
- **Rebuild from history.** Already have a track recorded on the server? A
|
|
21
|
+
one-time rebuild fills the map in from it.
|
|
22
|
+
|
|
23
|
+
## What you need
|
|
24
|
+
|
|
25
|
+
- A Signal K server with your depth sounder and GPS feeding it.
|
|
26
|
+
- A chartplotter that supports plotter extensions — **Freeboard-SK
|
|
27
|
+
3.4.0-beta.0 or later**, or another host that supports the `overlays.image`
|
|
28
|
+
capability.
|
|
29
|
+
- The plugin enabled on the server (install it from the App Store, then switch it
|
|
30
|
+
on in the server's plugin settings).
|
|
31
|
+
|
|
32
|
+
## Using it
|
|
33
|
+
|
|
34
|
+
Once enabled, **Live depth** appears in the chartplotter's Chart list, under
|
|
35
|
+
_Plugin layers_, with its own show/hide and opacity controls — saved just like a
|
|
36
|
+
chart's. Open **Layer settings** on its row (or the LiveDepth toolbar button) to
|
|
37
|
+
choose what it shows:
|
|
38
|
+
|
|
39
|
+
- **Show the layer** — on or off.
|
|
40
|
+
- **Depth source** — your own recent track, or the accumulated depth map.
|
|
41
|
+
- **Shade limit** — the depth the shading runs to; deeper water is left to the
|
|
42
|
+
chart.
|
|
43
|
+
- **Isobath interval** — how far apart the depth-contour lines are.
|
|
44
|
+
- **Grid cell**, **Merge band** — how readings are grouped into the dots on the
|
|
45
|
+
chart.
|
|
46
|
+
- **Max depth**, **Safety depth** — which readings are drawn, and which shallow
|
|
47
|
+
ones are marked individually.
|
|
48
|
+
|
|
49
|
+
Depth values are shown in your preferred units.
|
|
50
|
+
|
|
51
|
+
## Server settings
|
|
52
|
+
|
|
53
|
+
- **Path** — which depth measurement to record (below surface, transducer or
|
|
54
|
+
keel).
|
|
55
|
+
- **Depth transducer** — where the sounder sits on the hull, so each reading is
|
|
56
|
+
placed at it rather than at the position the server reports.
|
|
57
|
+
- **Transducer position correction** — shift readings from the vessel's
|
|
58
|
+
reference point to the transducer (needs the server's own GNSS correction on).
|
|
59
|
+
- **Rebuild from history** — a one-time rebuild of the depth map from the track
|
|
60
|
+
the server already has.
|
|
61
|
+
|
|
62
|
+
## How it works
|
|
63
|
+
|
|
64
|
+
The plugin runs on your Signal K server and turns each sounding into a
|
|
65
|
+
Web-Mercator depth map, keeping the shallowest reading for every spot. A
|
|
66
|
+
chartplotter that supports plotter extensions draws it as a map layer, rendering
|
|
67
|
+
in the browser. The plugin offers the layer to the chartplotter automatically —
|
|
68
|
+
there's nothing to configure on the chart side.
|
|
43
69
|
|
|
44
70
|
## Development
|
|
45
71
|
|
|
46
72
|
```bash
|
|
47
73
|
npm install
|
|
48
|
-
npm run build
|
|
49
|
-
npm test
|
|
74
|
+
npm run build # server code (dist/) + the chart-layer pages (public/)
|
|
75
|
+
npm test # vitest
|
|
50
76
|
npm run format
|
|
51
77
|
npm run lint
|
|
52
78
|
```
|
|
53
79
|
|
|
54
|
-
Node `>=22.5
|
|
80
|
+
Node `>=22.5`.
|
package/dist/collector.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Writable } from "stream";
|
|
2
2
|
import { ServerAPI } from "@signalk/server-api";
|
|
3
|
-
import { TransducerOffset } from "
|
|
3
|
+
import { TransducerOffset } from "./tiles/core/index.js";
|
|
4
4
|
import { Config } from "./config.js";
|
|
5
5
|
export interface CollectorOptions {
|
|
6
6
|
app: ServerAPI;
|
package/dist/collector.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,QAAQ,CAAC;IACjB,wEAAwE;IACxE,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,WAAW,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAC9B,GAAG,EACH,MAAM,EACN,MAAM,EACN,MAAM,EACN,MAAM,GACP,EAAE,gBAAgB,0CAkBlB"}
|
package/dist/config.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare function schema(app: ServerAPI): {
|
|
|
29
29
|
description: string;
|
|
30
30
|
enum: readonly ["belowSurface", "belowTransducer", "belowKeel"];
|
|
31
31
|
enumNames: string[];
|
|
32
|
-
default
|
|
32
|
+
default?: "belowKeel" | "belowSurface" | "belowTransducer" | undefined;
|
|
33
33
|
};
|
|
34
34
|
sounder: {
|
|
35
35
|
type: string;
|
|
@@ -41,13 +41,13 @@ export declare function schema(app: ServerAPI): {
|
|
|
41
41
|
type: string;
|
|
42
42
|
title: string;
|
|
43
43
|
minimum: number;
|
|
44
|
-
default
|
|
44
|
+
default?: {} | null | undefined;
|
|
45
45
|
};
|
|
46
46
|
x: {
|
|
47
47
|
type: string;
|
|
48
48
|
title: string;
|
|
49
49
|
description: string;
|
|
50
|
-
default
|
|
50
|
+
default?: {} | null | undefined;
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
53
|
};
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,MAAM,GAAG;IACnB;0DACsD;IACtD,IAAI,EAAE,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;IAClC;;oEAEgE;IAChE,OAAO,EAAE;QACP,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;KACX,CAAC;IACF;kEAC8D;IAC9D,oBAAoB,EAAE,KAAK,GAAG,SAAS,CAAC;IACxC,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,OAAO,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,UAAU,YACrB,cAAc,EACd,iBAAiB,EACjB,WAAW,CACH,CAAC;AAEX,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,MAAM,GAAG;IACnB;0DACsD;IACtD,IAAI,EAAE,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;IAClC;;oEAEgE;IAChE,OAAO,EAAE;QACP,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;KACX,CAAC;IACF;kEAC8D;IAC9D,oBAAoB,EAAE,KAAK,GAAG,SAAS,CAAC;IACxC,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,OAAO,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,UAAU,YACrB,cAAc,EACd,iBAAiB,EACjB,WAAW,CACH,CAAC;AAEX,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAiD7B,IAAI;YACJ,KAAK;YACL,WAAW;YAEX,IAAI;YACJ,SAAS;YACT,OAAO;;;YAGP,IAAI;YACJ,KAAK;YACL,WAAW;YAEX,UAAU;gBACR,GAAG;oBACD,IAAI;oBACJ,OAAO;oBACP,KAAK;oBACL,WAAW;;gBAGb,UAAU;oBACR,IAAI;oBACJ,OAAO;oBACP,KAAK;oBACL,WAAW;;;;;EAOtB"}
|
package/dist/config.js
CHANGED
|
@@ -5,6 +5,11 @@ export const DepthPaths = [
|
|
|
5
5
|
];
|
|
6
6
|
export function schema(app) {
|
|
7
7
|
const defaultPath = DepthPaths.find((path) => app.getSelfPath(`environment.depth.${path}.value`));
|
|
8
|
+
// A `default` is attached only when present: `default: undefined` survives in
|
|
9
|
+
// the object but is dropped when the server stores the schema as JSON, which
|
|
10
|
+
// its plugin schema check flags as data loss.
|
|
11
|
+
const fromBow = app.getSelfPath("sensors.depth.fromBow.value");
|
|
12
|
+
const fromCenter = app.getSelfPath("sensors.depth.fromCenter.value");
|
|
8
13
|
return {
|
|
9
14
|
type: "object",
|
|
10
15
|
description: "Build a gridded bathymetry tile store from the vessel's own position and depth, and serve it to a chart client's LiveDepth layer.",
|
|
@@ -19,7 +24,7 @@ export function schema(app) {
|
|
|
19
24
|
"Depth below transducer",
|
|
20
25
|
"Depth below keel",
|
|
21
26
|
],
|
|
22
|
-
default: defaultPath,
|
|
27
|
+
...(defaultPath === undefined ? {} : { default: defaultPath }),
|
|
23
28
|
},
|
|
24
29
|
sounder: {
|
|
25
30
|
type: "object",
|
|
@@ -31,13 +36,13 @@ export function schema(app) {
|
|
|
31
36
|
type: "number",
|
|
32
37
|
title: "Distance of the transducer from the bow (meters)",
|
|
33
38
|
minimum: 0,
|
|
34
|
-
default:
|
|
39
|
+
...(fromBow === undefined ? {} : { default: fromBow }),
|
|
35
40
|
},
|
|
36
41
|
x: {
|
|
37
42
|
type: "number",
|
|
38
43
|
title: "Distance of the transducer from the center (meters)",
|
|
39
44
|
description: "+ve to starboard, -ve to port",
|
|
40
|
-
default:
|
|
45
|
+
...(fromCenter === undefined ? {} : { default: fromCenter }),
|
|
41
46
|
},
|
|
42
47
|
},
|
|
43
48
|
},
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,cAAc;IACd,iBAAiB;IACjB,WAAW;CACH,CAAC;AAEX,MAAM,UAAU,MAAM,CAAC,GAAc;IACnC,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC3C,GAAG,CAAC,WAAW,CAAC,qBAAqB,IAAI,QAAQ,CAAC,CACnD,CAAC;IACF,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EACT,mIAAmI;QACrI,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,OAAO;gBACd,WAAW,EACT,sIAAsI;gBACxI,IAAI,EAAE,UAAU;gBAChB,SAAS,EAAE;oBACT,qBAAqB;oBACrB,wBAAwB;oBACxB,kBAAkB;iBACnB;gBACD,OAAO,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,cAAc;IACd,iBAAiB;IACjB,WAAW;CACH,CAAC;AAEX,MAAM,UAAU,MAAM,CAAC,GAAc;IACnC,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC3C,GAAG,CAAC,WAAW,CAAC,qBAAqB,IAAI,QAAQ,CAAC,CACnD,CAAC;IACF,8EAA8E;IAC9E,6EAA6E;IAC7E,8CAA8C;IAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC,6BAA6B,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,CAAC,gCAAgC,CAAC,CAAC;IACrE,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EACT,mIAAmI;QACrI,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,OAAO;gBACd,WAAW,EACT,sIAAsI;gBACxI,IAAI,EAAE,UAAU;gBAChB,SAAS,EAAE;oBACT,qBAAqB;oBACrB,wBAAwB;oBACxB,kBAAkB;iBACnB;gBACD,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;aAC/D;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,kBAAkB;gBACzB,WAAW,EACT,sHAAsH;gBACxH,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;gBACpB,UAAU,EAAE;oBACV,CAAC,EAAE;wBACD,IAAI,EAAE,QAAQ;wBACd,KAAK,EAAE,kDAAkD;wBACzD,OAAO,EAAE,CAAC;wBACV,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;qBACvD;oBACD,CAAC,EAAE;wBACD,IAAI,EAAE,QAAQ;wBACd,KAAK,EAAE,qDAAqD;wBAC5D,WAAW,EAAE,+BAA+B;wBAC5C,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;qBAC7D;iBACF;aACF;YACD,oBAAoB,EAAE;gBACpB,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,gCAAgC;gBACvC,WAAW,EACT,2UAA2U;gBAC7U,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC;gBACxB,SAAS,EAAE,CAAC,KAAK,EAAE,wCAAwC,CAAC;gBAC5D,OAAO,EAAE,KAAK;aACf;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,sBAAsB;gBAC7B,WAAW,EACT,iQAAiQ;gBACnQ,UAAU,EAAE;oBACV,GAAG,EAAE;wBACH,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,KAAK;wBACd,KAAK,EAAE,+CAA+C;wBACtD,WAAW,EACT,wGAAwG;qBAC3G;oBACD,UAAU,EAAE;wBACV,IAAI,EAAE,QAAQ;wBACd,OAAO,EAAE,GAAG;wBACZ,KAAK,EAAE,8BAA8B;wBACrC,WAAW,EACT,kGAAkG;qBACrG;iBACF;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The providing plugin's id; also the plotterExtensions resource key. */
|
|
2
|
+
export declare const PLUGIN_ID = "signalk-livedepth-plugin";
|
|
3
|
+
/** Top-level, non-admin route the extension's pages and data are served from. */
|
|
4
|
+
export declare const ASSET_BASE = "/plotterext/signalk-livedepth-plugin";
|
|
5
|
+
/** The plotterExtensions manifest (see docs/api/plotter-extensions-api.md). */
|
|
6
|
+
export interface PlotterExtManifest {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
version: string;
|
|
10
|
+
apiVersion: string;
|
|
11
|
+
requires: string[];
|
|
12
|
+
optional: string[];
|
|
13
|
+
buttons: unknown[];
|
|
14
|
+
panels: unknown[];
|
|
15
|
+
background: unknown[];
|
|
16
|
+
overlays: unknown[];
|
|
17
|
+
}
|
|
18
|
+
/** Build the extension manifest offered to a host chartplotter.
|
|
19
|
+
*
|
|
20
|
+
* One `image` overlay, drawn by a headless background runtime, placed above
|
|
21
|
+
* the charts and below the vessel/resources. A `mapToolbar` button opens the
|
|
22
|
+
* settings panel, which shares state with the runtime over the bus. */
|
|
23
|
+
export declare function buildManifest(version?: string): PlotterExtManifest;
|
|
24
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/plotterext/manifest.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,eAAO,MAAM,SAAS,6BAA6B,CAAC;AACpD,iFAAiF;AACjF,eAAO,MAAM,UAAU,yCAA6B,CAAC;AAErD,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAeD;;;;uEAIuE;AACvE,wBAAgB,aAAa,CAAC,OAAO,SAAkB,GAAG,kBAAkB,CAsD3E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/** The providing plugin's id; also the plotterExtensions resource key. */
|
|
5
|
+
export const PLUGIN_ID = "signalk-livedepth-plugin";
|
|
6
|
+
/** Top-level, non-admin route the extension's pages and data are served from. */
|
|
7
|
+
export const ASSET_BASE = `/plotterext/${PLUGIN_ID}`;
|
|
8
|
+
/** The plugin's own version, read from its package.json. */
|
|
9
|
+
function pluginVersion() {
|
|
10
|
+
try {
|
|
11
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const pkg = JSON.parse(readFileSync(join(here, "..", "..", "package.json"), "utf8"));
|
|
13
|
+
return pkg.version ?? "0.0.0";
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return "0.0.0";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Build the extension manifest offered to a host chartplotter.
|
|
20
|
+
*
|
|
21
|
+
* One `image` overlay, drawn by a headless background runtime, placed above
|
|
22
|
+
* the charts and below the vessel/resources. A `mapToolbar` button opens the
|
|
23
|
+
* settings panel, which shares state with the runtime over the bus. */
|
|
24
|
+
export function buildManifest(version = pluginVersion()) {
|
|
25
|
+
return {
|
|
26
|
+
name: "LiveDepth",
|
|
27
|
+
description: "Draw your recorded depth as a live depth map on the chart, shaded with depth contours.",
|
|
28
|
+
version,
|
|
29
|
+
apiVersion: "1",
|
|
30
|
+
requires: ["background.iframe", "overlays.image", "signalk.stream"],
|
|
31
|
+
optional: ["buttons", "panels.iframe", "units", "map", "nightMode"],
|
|
32
|
+
buttons: [
|
|
33
|
+
{
|
|
34
|
+
id: "open-livedepth-settings",
|
|
35
|
+
title: "LiveDepth",
|
|
36
|
+
slot: "mapToolbar",
|
|
37
|
+
icon: "water",
|
|
38
|
+
action: { type: "togglePanel", panel: "livedepth-settings" },
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
panels: [
|
|
42
|
+
{
|
|
43
|
+
id: "livedepth-settings",
|
|
44
|
+
title: "LiveDepth",
|
|
45
|
+
type: "iframe",
|
|
46
|
+
url: `${ASSET_BASE}/config.html`,
|
|
47
|
+
lifecycle: "keepAlive",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
background: [
|
|
51
|
+
{
|
|
52
|
+
id: "renderer",
|
|
53
|
+
type: "iframe",
|
|
54
|
+
url: `${ASSET_BASE}/renderer.html`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
overlays: [
|
|
58
|
+
{
|
|
59
|
+
id: "depth",
|
|
60
|
+
title: "Live depth",
|
|
61
|
+
type: "image",
|
|
62
|
+
runtime: "renderer",
|
|
63
|
+
band: "aboveCharts",
|
|
64
|
+
visible: true,
|
|
65
|
+
opacity: 0.85,
|
|
66
|
+
minZoom: 4,
|
|
67
|
+
// The Chart list's "Layer settings" button opens this panel, so the
|
|
68
|
+
// layer's settings sit with the layer, not only behind the toolbar
|
|
69
|
+
// button.
|
|
70
|
+
configPanel: "livedepth-settings",
|
|
71
|
+
// Isobaths and labels want the pixels; the host still clamps to its own
|
|
72
|
+
// memory budget.
|
|
73
|
+
preferredPixelRatio: 2,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/plotterext/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,SAAS,GAAG,0BAA0B,CAAC;AACpD,iFAAiF;AACjF,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,SAAS,EAAE,CAAC;AAgBrD,4DAA4D;AAC5D,SAAS,aAAa;IACpB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CACrC,CAAC;QAC1B,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;;uEAIuE;AACvE,MAAM,UAAU,aAAa,CAAC,OAAO,GAAG,aAAa,EAAE;IACrD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,WAAW,EACT,wFAAwF;QAC1F,OAAO;QACP,UAAU,EAAE,GAAG;QACf,QAAQ,EAAE,CAAC,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB,CAAC;QACnE,QAAQ,EAAE,CAAC,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC;QACnE,OAAO,EAAE;YACP;gBACE,EAAE,EAAE,yBAAyB;gBAC7B,KAAK,EAAE,WAAW;gBAClB,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,oBAAoB,EAAE;aAC7D;SACF;QACD,MAAM,EAAE;YACN;gBACE,EAAE,EAAE,oBAAoB;gBACxB,KAAK,EAAE,WAAW;gBAClB,IAAI,EAAE,QAAQ;gBACd,GAAG,EAAE,GAAG,UAAU,cAAc;gBAChC,SAAS,EAAE,WAAW;aACvB;SACF;QACD,UAAU,EAAE;YACV;gBACE,EAAE,EAAE,UAAU;gBACd,IAAI,EAAE,QAAQ;gBACd,GAAG,EAAE,GAAG,UAAU,gBAAgB;aACnC;SACF;QACD,QAAQ,EAAE;YACR;gBACE,EAAE,EAAE,OAAO;gBACX,KAAK,EAAE,YAAY;gBACnB,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,UAAU;gBACnB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC;gBACV,oEAAoE;gBACpE,mEAAmE;gBACnE,UAAU;gBACV,WAAW,EAAE,oBAAoB;gBACjC,wEAAwE;gBACxE,iBAAiB;gBACjB,mBAAmB,EAAE,CAAC;aACvB;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ServerAPI } from "@signalk/server-api";
|
|
2
|
+
/** Offer the plotter extension: register the read-only `plotterExtensions`
|
|
3
|
+
* resource and mount the extension's pages. Safe to call on every start — the
|
|
4
|
+
* provider is registered and the assets mounted once; `isRunning` reflects the
|
|
5
|
+
* plugin's current state so the extension leaves when the plugin is disabled. */
|
|
6
|
+
export declare function registerPlotterExtension(app: ServerAPI, isRunning: () => boolean): void;
|
|
7
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/plotterext/provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAYrD;;;iFAGiF;AACjF,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,MAAM,OAAO,GACvB,IAAI,CAgCN"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { ASSET_BASE, PLUGIN_ID, buildManifest } from "./manifest.js";
|
|
5
|
+
let registered = false;
|
|
6
|
+
let mounted = false;
|
|
7
|
+
/** Offer the plotter extension: register the read-only `plotterExtensions`
|
|
8
|
+
* resource and mount the extension's pages. Safe to call on every start — the
|
|
9
|
+
* provider is registered and the assets mounted once; `isRunning` reflects the
|
|
10
|
+
* plugin's current state so the extension leaves when the plugin is disabled. */
|
|
11
|
+
export function registerPlotterExtension(app, isRunning) {
|
|
12
|
+
const a = app;
|
|
13
|
+
if (!registered) {
|
|
14
|
+
if (typeof a.registerResourceProvider === "function") {
|
|
15
|
+
a.registerResourceProvider({
|
|
16
|
+
type: "plotterExtensions",
|
|
17
|
+
methods: {
|
|
18
|
+
listResources: async () => isRunning() ? { [PLUGIN_ID]: buildManifest() } : {},
|
|
19
|
+
getResource: async (id) => {
|
|
20
|
+
if (!isRunning() || id !== PLUGIN_ID) {
|
|
21
|
+
throw new Error(`No such plotterExtensions resource: ${id}`);
|
|
22
|
+
}
|
|
23
|
+
return buildManifest();
|
|
24
|
+
},
|
|
25
|
+
setResource: async () => {
|
|
26
|
+
throw new Error(`${PLUGIN_ID} is a read-only provider`);
|
|
27
|
+
},
|
|
28
|
+
deleteResource: async () => {
|
|
29
|
+
throw new Error(`${PLUGIN_ID} is a read-only provider`);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
registered = true;
|
|
34
|
+
a.debug(`${PLUGIN_ID}: plotter extension offered`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
a.debug(`${PLUGIN_ID}: server has no resource provider registry`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
mountAssets(a);
|
|
41
|
+
}
|
|
42
|
+
/** Serve the extension's pages as a top-level static route. Express is provided
|
|
43
|
+
* by the server, so requiring it adds no runtime dependency of our own. */
|
|
44
|
+
function mountAssets(app) {
|
|
45
|
+
if (mounted || typeof app.use !== "function") {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let serveStatic;
|
|
49
|
+
try {
|
|
50
|
+
serveStatic = createRequire(import.meta.url)("express").static;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
app.debug(`${PLUGIN_ID}: express unavailable; cannot serve ${ASSET_BASE}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
57
|
+
app.use(ASSET_BASE, serveStatic(join(here, "..", "..", "public")));
|
|
58
|
+
mounted = true;
|
|
59
|
+
app.debug(`${PLUGIN_ID}: extension assets served at ${ASSET_BASE}`);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/plotterext/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAQrE,IAAI,UAAU,GAAG,KAAK,CAAC;AACvB,IAAI,OAAO,GAAG,KAAK,CAAC;AAEpB;;;iFAGiF;AACjF,MAAM,UAAU,wBAAwB,CACtC,GAAc,EACd,SAAwB;IAExB,MAAM,CAAC,GAAG,GAAmB,CAAC;IAE9B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,IAAI,OAAO,CAAC,CAAC,wBAAwB,KAAK,UAAU,EAAE,CAAC;YACrD,CAAC,CAAC,wBAAwB,CAAC;gBACzB,IAAI,EAAE,mBAAmB;gBACzB,OAAO,EAAE;oBACP,aAAa,EAAE,KAAK,IAAI,EAAE,CACxB,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE;oBACrD,WAAW,EAAE,KAAK,EAAE,EAAU,EAAE,EAAE;wBAChC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;4BACrC,MAAM,IAAI,KAAK,CAAC,uCAAuC,EAAE,EAAE,CAAC,CAAC;wBAC/D,CAAC;wBACD,OAAO,aAAa,EAAE,CAAC;oBACzB,CAAC;oBACD,WAAW,EAAE,KAAK,IAAI,EAAE;wBACtB,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,0BAA0B,CAAC,CAAC;oBAC1D,CAAC;oBACD,cAAc,EAAE,KAAK,IAAI,EAAE;wBACzB,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,0BAA0B,CAAC,CAAC;oBAC1D,CAAC;iBACF;aACF,CAAC,CAAC;YACH,UAAU,GAAG,IAAI,CAAC;YAClB,CAAC,CAAC,KAAK,CAAC,GAAG,SAAS,6BAA6B,CAAC,CAAC;QACrD,CAAC;aAAM,CAAC;YACN,CAAC,CAAC,KAAK,CAAC,GAAG,SAAS,4CAA4C,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,WAAW,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED;2EAC2E;AAC3E,SAAS,WAAW,CAAC,GAAiB;IACpC,IAAI,OAAO,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QAC7C,OAAO;IACT,CAAC;IACD,IAAI,WAAmD,CAAC;IACxD,IAAI,CAAC;QACH,WAAW,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,MAE5C,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,KAAK,CAAC,GAAG,SAAS,uCAAuC,UAAU,EAAE,CAAC,CAAC;QAC3E,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,GAAG,IAAI,CAAC;IACf,GAAG,CAAC,KAAK,CAAC,GAAG,SAAS,gCAAgC,UAAU,EAAE,CAAC,CAAC;AACtE,CAAC"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAgB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAgB,MAAM,qBAAqB,CAAC;AAgBtE;;;;;;;mEAOmE;AACnE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAWpE;AA+CD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAqH3D"}
|
package/dist/plugin.js
CHANGED
|
@@ -3,7 +3,8 @@ import { schema } from "./config.js";
|
|
|
3
3
|
import { createTileDB, createTileStore } from "./tiles/store.js";
|
|
4
4
|
import { createTileWriter } from "./tiles/writer.js";
|
|
5
5
|
import { seedFromHistory } from "./tiles/seed.js";
|
|
6
|
-
import { registerBathymetryRoutes, } from "./tiles/api.js";
|
|
6
|
+
import { registerBathymetryRoutes, registerSeedRoutes, } from "./tiles/api.js";
|
|
7
|
+
import { registerPlotterExtension } from "./plotterext/provider.js";
|
|
7
8
|
import { createCollector } from "./collector.js";
|
|
8
9
|
/** The vessel's length overall, for locating the reference point the server
|
|
9
10
|
* reports (the CCRP is midships).
|
|
@@ -67,6 +68,8 @@ export default function createPlugin(app) {
|
|
|
67
68
|
let config;
|
|
68
69
|
let transducer;
|
|
69
70
|
let seedStatus = { running: false };
|
|
71
|
+
let running = false;
|
|
72
|
+
let routesRegistered = false;
|
|
70
73
|
const runSeed = async (resolution) => {
|
|
71
74
|
if (seedStatus.running)
|
|
72
75
|
return;
|
|
@@ -116,6 +119,17 @@ export default function createPlugin(app) {
|
|
|
116
119
|
signal: abortController.signal,
|
|
117
120
|
});
|
|
118
121
|
app.setPluginStatus("Collecting depth into the bathymetry tile store");
|
|
122
|
+
running = true;
|
|
123
|
+
// Offer the chart layer to a host chartplotter, and serve the store the
|
|
124
|
+
// layer reads from a top-level, non-admin route.
|
|
125
|
+
registerPlotterExtension(app, () => running);
|
|
126
|
+
if (!routesRegistered) {
|
|
127
|
+
registerBathymetryRoutes(app, () => tileStore, () => ({
|
|
128
|
+
depthPath: config ? `environment.depth.${config.path}` : undefined,
|
|
129
|
+
transducer,
|
|
130
|
+
}));
|
|
131
|
+
routesRegistered = true;
|
|
132
|
+
}
|
|
119
133
|
if (startConfig.seed?.run) {
|
|
120
134
|
// One-time rebuild. Clear the trigger when it finishes (or fails) so it
|
|
121
135
|
// doesn't fire again on the next start; an interrupted run resumes from
|
|
@@ -127,6 +141,7 @@ export default function createPlugin(app) {
|
|
|
127
141
|
}
|
|
128
142
|
},
|
|
129
143
|
stop() {
|
|
144
|
+
running = false;
|
|
130
145
|
abortController?.abort("Stopping signalk-livedepth-plugin");
|
|
131
146
|
tileStore?.close();
|
|
132
147
|
tileStore = undefined;
|
|
@@ -135,10 +150,9 @@ export default function createPlugin(app) {
|
|
|
135
150
|
return schema(app);
|
|
136
151
|
},
|
|
137
152
|
registerWithRouter(router) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}), seed);
|
|
153
|
+
// The one-time seed trigger stays admin-only, on the plugin's own router;
|
|
154
|
+
// the store the extension reads is served from the top-level route above.
|
|
155
|
+
registerSeedRoutes(router, seed);
|
|
142
156
|
},
|
|
143
157
|
};
|
|
144
158
|
}
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAU,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,wBAAwB,
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAU,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GAGnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;;mEAOmE;AACnE,MAAM,UAAU,iBAAiB,CAAC,GAAc;IAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC,eAAe,CACoB,CAAC;IACjE,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC;IAC5D,MAAM,OAAO,GACX,OAAO,KAAK,KAAK,QAAQ;QACvB,CAAC,CAAC,KAAK;QACP,CAAC,CAAE,KAA0C,EAAE,OAAO,CAAC;IAC3D,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;QAC3E,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,0EAA0E;AAC1E,SAAS,kBAAkB,CAAC,GAAc;IACxC,mDAAmD;IACnD,OAAO,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,IAAI,KAAK,CAAC;AACvD,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,uBAAuB,CAC9B,GAAc,EACd,MAAc;IAEd,IAAI,MAAM,CAAC,oBAAoB,KAAK,SAAS,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,kBAAkB,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;QACtC,GAAG,CAAC,cAAc,CAChB,0EAA0E;YACxE,uEAAuE;YACvE,uEAAuE;YACvE,2BAA2B,CAC9B,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,GAAG,CAAC,cAAc,CAChB,0DAA0D;YACxD,uEAAuE;YACvE,qCAAqC,CACxC,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC;QAC9C,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,GAAc;IACjD,IAAI,eAAe,GAAgC,SAAS,CAAC;IAC7D,2EAA2E;IAC3E,6CAA6C;IAC7C,IAAI,SAAgC,CAAC;IACrC,IAAI,MAA0B,CAAC;IAC/B,IAAI,UAAwC,CAAC;IAC7C,IAAI,UAAU,GAAe,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAChD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,MAAM,OAAO,GAAG,KAAK,EAAE,UAAmB,EAAiB,EAAE;QAC3D,IAAI,UAAU,CAAC,OAAO;YAAE,OAAO;QAC/B,MAAM,KAAK,GAAG,SAAS,CAAC;QACxB,MAAM,GAAG,GAAG,MAAM,CAAC;QACnB,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG;YAAE,OAAO;QAE3B,UAAU,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC/B,GAAG,CAAC,eAAe,CAAC,2CAA2C,CAAC,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC;gBACrC,GAAG;gBACH,KAAK;gBACL,MAAM,EAAE,GAAG;gBACX,UAAU;gBACV,MAAM,EAAE,UAAU;gBAClB,MAAM,EAAE,eAAe,EAAE,MAAM;gBAC/B,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;oBAChB,UAAU,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;oBAC/C,GAAG,CAAC,eAAe,CACjB,gCAAgC,CAAC,CAAC,MAAM,gBAAgB,CAAC,CAAC,MAAM,UAAU,CAC3E,CAAC;gBACJ,CAAC;aACF,CAAC,CAAC;YACH,UAAU,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;YAC1C,GAAG,CAAC,eAAe,CACjB,gCAAgC,QAAQ,CAAC,MAAM,SAAS,CACzD,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,UAAU,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,cAAc,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,GAAe,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;IAEpE,OAAO;QACL,EAAE,EAAE,0BAA0B;QAC9B,IAAI,EAAE,WAAW;QACjB,WAAW,EACT,8HAA8H;QAEhI,KAAK,CAAC,KAAK,CAAC,WAAmB,EAAE,OAAiC;YAChE,MAAM,GAAG,WAAW,CAAC;YACrB,GAAG,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;YAE/C,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;YACxC,SAAS,GAAG,eAAe,CACzB,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,yBAAyB,CAAC,CAAC,CACpE,CAAC;YACF,UAAU,GAAG,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAElD,eAAe,CAAC;gBACd,GAAG;gBACH,MAAM;gBACN,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC;gBACnC,MAAM,EAAE,UAAU;gBAClB,MAAM,EAAE,eAAe,CAAC,MAAM;aAC/B,CAAC,CAAC;YACH,GAAG,CAAC,eAAe,CAAC,iDAAiD,CAAC,CAAC;YAEvE,OAAO,GAAG,IAAI,CAAC;YACf,wEAAwE;YACxE,iDAAiD;YACjD,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACtB,wBAAwB,CACtB,GAAG,EACH,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,CAAC;oBACL,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,qBAAqB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;oBAClE,UAAU;iBACX,CAAC,CACH,CAAC;gBACF,gBAAgB,GAAG,IAAI,CAAC;YAC1B,CAAC;YAED,IAAI,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;gBAC1B,wEAAwE;gBACxE,wEAAwE;gBACxE,0CAA0C;gBAC1C,KAAK,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAClD,OAAO,CAAC;oBACN,GAAG,WAAW;oBACd,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE;iBAC1C,CAAC,CACH,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI;YACF,OAAO,GAAG,KAAK,CAAC;YAChB,eAAe,EAAE,KAAK,CAAC,mCAAmC,CAAC,CAAC;YAC5D,SAAS,EAAE,KAAK,EAAE,CAAC;YACnB,SAAS,GAAG,SAAS,CAAC;QACxB,CAAC;QAED,MAAM;YACJ,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QAED,kBAAkB,CAAC,MAAoB;YACrC,0EAA0E;YAC1E,0EAA0E;YAC1E,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/streams/live.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ServerAPI } from "@signalk/server-api";
|
|
2
2
|
import { PassThrough } from "stream";
|
|
3
|
-
import { TransducerOffset } from "
|
|
3
|
+
import { TransducerOffset } from "../tiles/core/index.js";
|
|
4
4
|
import { Config } from "../config.js";
|
|
5
5
|
/**
|
|
6
6
|
* Subscribe to the vessel's own depth and pair each sounding with the last
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/streams/live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAuB,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/streams/live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAuB,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAOtC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,gBAAgB,eAyE1B"}
|
package/dist/streams/live.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PassThrough } from "stream";
|
|
2
|
-
import { correctToTransducer } from "
|
|
2
|
+
import { correctToTransducer } from "../tiles/core/index.js";
|
|
3
3
|
import { Temporal } from "@js-temporal/polyfill";
|
|
4
4
|
/** Maximum age of last position fix for a depth to be saved */
|
|
5
5
|
const ttl = 2000;
|
package/dist/streams/live.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/streams/live.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAoB,MAAM,
|
|
1
|
+
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/streams/live.ts"],"names":[],"mappings":"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"}
|