@rhizomatics/signalk-einklabel-plugin 0.4.2 → 0.4.3

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 CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.4.3
2
+ - Fixed datetime values when running from plugin were blank while CLI was fine
3
+ - Added an example Resources API output from signalk-tides plugin
4
+ - Relaid out the example tide clock, adding the tidal range (LAT to HAT) and the source of tide data
5
+ - Alternative source of time zone info, `source=einklabel,path=local_zone`
1
6
  # 0.4.2
2
7
  - Fix High/Low display for tide clock template
3
8
  - Fix last repaint time for display being hour out
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ** BETA - basic functionality, limited vendor/product support **
4
4
 
5
- A SignalK plugin to display data from SignalK paths, APIs and plugins on Electronic Shelf Labels over a Bluetooth Low Energy (BLE) connection.
5
+ A SignalK plugin to display data from SignalK paths, APIs and plugins on Electronic Shelf Labels over a Bluetooth Low Energy (BLE) connection using simple SVG templates.
6
6
 
7
7
  Electronic Shelf Labels (ESLs) are [eInk](https://en.wikipedia.org/wiki/E_Ink) devices that consume very little battery energy, presuming they are not constantly updated - the battery is used only when the display changes, and a periodic BLE check for incoming changes. Perfect for info that changes only once or twice a day, like tidal information.
8
8
 
@@ -36,7 +36,10 @@ The source can be overridden to use the SignalK server's Resources API instead.
36
36
 
37
37
  #### Plugin Data
38
38
 
39
- `source=einklabel` reads data injected by the plugin itself, rather than from SignalK. Currently the only such value is `path=repainted`, the timestamp of the current repaint - for example `source=einklabel,path=repainted,format=local_datetime_short` to show when the label was last updated.
39
+ `source=einklabel` reads data injected by the plugin itself, rather than from SignalK. Available paths:
40
+
41
+ * `path=repainted` - the timestamp of the current repaint - for example `source=einklabel,path=repainted,format=local_datetime_short` to show when the label was last updated.
42
+ * `path=local_zone` - a short zone name (e.g. `BST`) for the same timezone used for `local_time`/`day_mon`/`local_datetime_short` (see above) - a fallback for `environment.time.timezoneRegion,format=utc_offset` on installs that never publish that path, since it needs no SignalK metadata of its own. Falls back to a plain UTC offset like `GMT+1` where the host's locale has no real abbreviation for the zone.
40
43
 
41
44
  #### Customizing Output
42
45
 
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assembleLiveContext = assembleLiveContext;
4
+ const formatters_1 = require("../render/formatters");
4
5
  const httpJson_1 = require("../httpJson");
5
6
  const unitCategories_1 = require("../unitCategories");
6
7
  const pathMeta_1 = require("../pathMeta");
@@ -71,6 +72,6 @@ async function assembleLiveContext(signalkUrl, bindings) {
71
72
  const categories = await (0, unitCategories_1.fetchCategoryDisplayUnits)(signalkUrl, categoryNames);
72
73
  // Matches `considerRepaint` in repaintScheduler.ts - the CLI has no real device repaint to time, so
73
74
  // a `source=einklabel,path=repainted` binding just resolves to "now", same as a live render would.
74
- const meta = { repainted: new Date().toISOString() };
75
+ const meta = { repainted: new Date().toISOString(), local_zone: (0, formatters_1.resolveLocalZoneAbbreviation)({ signalk }) };
75
76
  return { signalk, resources, pathMeta, categories, meta };
76
77
  }
@@ -20,7 +20,7 @@ export interface Binding {
20
20
  }
21
21
  /**
22
22
  * Parses a `<desc>` element's text content into a `Binding`, e.g.
23
- * `source=resources,resource=tides,path=extremes.[0].level,category=depth,round=2` or, using the
23
+ * `source=resources,resource=tides,path=extremes[0].level,category=depth,round=2` or, using the
24
24
  * defaults (`source=signalk,context=self`), plain `path=navigation.speedOverGround` (auto-converts via
25
25
  * that path's own metadata - see `renderBinding`). A bare path with no `key=value` pairs at all, e.g.
26
26
  * `environment.forecast.description`, is shorthand for `path=environment.forecast.description`
@@ -10,7 +10,7 @@ const SOURCES = ['signalk', 'resources', 'einklabel'];
10
10
  const KNOWN_KEYS = new Set(['source', 'context', 'resource', 'path', 'format', 'category', 'round']);
11
11
  /**
12
12
  * Parses a `<desc>` element's text content into a `Binding`, e.g.
13
- * `source=resources,resource=tides,path=extremes.[0].level,category=depth,round=2` or, using the
13
+ * `source=resources,resource=tides,path=extremes[0].level,category=depth,round=2` or, using the
14
14
  * defaults (`source=signalk,context=self`), plain `path=navigation.speedOverGround` (auto-converts via
15
15
  * that path's own metadata - see `renderBinding`). A bare path with no `key=value` pairs at all, e.g.
16
16
  * `environment.forecast.description`, is shorthand for `path=environment.forecast.description`
@@ -14,5 +14,15 @@ export interface DisplayUnits {
14
14
  }
15
15
  /** Converts a base-SI value (always what SignalK paths deliver) to its metadata's preferred display unit and formats it with the unit's symbol, e.g. 3.42 -> "11.2ft". */
16
16
  export declare function formatDisplayUnits(value: number, displayUnits: DisplayUnits, round: number | undefined): string;
17
+ /**
18
+ * Short zone name (e.g. "BST", or "GMT+1" where the host's own `Intl` locale has no real abbreviation
19
+ * for the zone) for the same zone `selfTimezone` resolves - an alternative for
20
+ * `environment.time.timezoneRegion,format=utc_offset` on installs that never publish that path (see
21
+ * `selfTimezone`'s own comment). Uses the host's default locale rather than pinning one, so it shows
22
+ * whatever `Intl` would show the host's own user - "GMT+1" is an acceptable fallback where that locale
23
+ * doesn't have a 3-letter abbreviation to offer. Resolved as a `source=einklabel,path=local_zone`
24
+ * binding (see `considerRepaint`).
25
+ */
26
+ export declare function resolveLocalZoneAbbreviation(context: TemplateContext): string;
17
27
  /** Applies a named `format=` formatter to a resolved binding value. */
18
28
  export declare function applyFormat(name: string, value: unknown, context: TemplateContext, round: number | undefined): string;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatDisplayUnits = formatDisplayUnits;
4
+ exports.resolveLocalZoneAbbreviation = resolveLocalZoneAbbreviation;
4
5
  exports.applyFormat = applyFormat;
5
6
  const luxon_1 = require("luxon");
6
7
  const mathjs_1 = require("mathjs");
@@ -26,30 +27,53 @@ function selfTimezone(context) {
26
27
  const environment = self?.environment;
27
28
  return environment?.time?.timezoneRegion || Intl.DateTimeFormat().resolvedOptions().timeZone;
28
29
  }
30
+ /**
31
+ * Short zone name (e.g. "BST", or "GMT+1" where the host's own `Intl` locale has no real abbreviation
32
+ * for the zone) for the same zone `selfTimezone` resolves - an alternative for
33
+ * `environment.time.timezoneRegion,format=utc_offset` on installs that never publish that path (see
34
+ * `selfTimezone`'s own comment). Uses the host's default locale rather than pinning one, so it shows
35
+ * whatever `Intl` would show the host's own user - "GMT+1" is an acceptable fallback where that locale
36
+ * doesn't have a 3-letter abbreviation to offer. Resolved as a `source=einklabel,path=local_zone`
37
+ * binding (see `considerRepaint`).
38
+ */
39
+ function resolveLocalZoneAbbreviation(context) {
40
+ const part = new Intl.DateTimeFormat(undefined, { timeZone: selfTimezone(context), timeZoneName: 'short' })
41
+ .formatToParts(new Date())
42
+ .find((p) => p.type === 'timeZoneName');
43
+ return part?.value ?? '';
44
+ }
45
+ /**
46
+ * A `resources`-sourced timestamp can arrive as either an ISO string (always true over HTTP, since
47
+ * `JSON.stringify` converts a `Date` to its ISO string automatically) or a live `Date` instance (an
48
+ * in-process `app.resourcesApi` call skips that serialisation step entirely, so a provider that hasn't
49
+ * itself called `.toISOString()` yet hands back the raw object) - accept both so the live plugin and
50
+ * the CLI render identically regardless of which path a binding's data took.
51
+ */
52
+ function parseTimestamp(value) {
53
+ if (value instanceof Date)
54
+ return luxon_1.DateTime.fromJSDate(value, { zone: 'utc' });
55
+ if (typeof value === 'string')
56
+ return luxon_1.DateTime.fromISO(value, { zone: 'utc' });
57
+ return undefined;
58
+ }
29
59
  /**
30
60
  * Shows the explicit IANA zone name rather than an abbreviation (e.g. "BST") - UK tide tables are
31
61
  * officially published in GMT, so the basis for the displayed time must be unambiguous rather than
32
62
  * just locally styled.
33
63
  */
34
64
  function formatLocalTime(value, context) {
35
- if (typeof value !== 'string')
36
- return '';
37
- const dt = luxon_1.DateTime.fromISO(value, { zone: 'utc' }).setZone(selfTimezone(context));
38
- return dt.isValid ? dt.toFormat('HH:mm') : '';
65
+ const dt = parseTimestamp(value)?.setZone(selfTimezone(context));
66
+ return dt?.isValid ? dt.toFormat('HH:mm') : '';
39
67
  }
40
68
  /** e.g. "27 Jun" - day of month (no leading zero) and abbreviated month name, in the local vessel's timezone. */
41
69
  function formatDayMonth(value, context) {
42
- if (typeof value !== 'string')
43
- return '';
44
- const dt = luxon_1.DateTime.fromISO(value, { zone: 'utc' }).setZone(selfTimezone(context));
45
- return dt.isValid ? dt.toFormat('d MMM') : '';
70
+ const dt = parseTimestamp(value)?.setZone(selfTimezone(context));
71
+ return dt?.isValid ? dt.toFormat('d MMM') : '';
46
72
  }
47
73
  /** e.g. "21 Jun 26 18:05" - day of month (no leading zero), abbreviated month, 2-digit year, and 24h time, in the local vessel's timezone. */
48
74
  function formatLocalDatetimeShort(value, context) {
49
- if (typeof value !== 'string')
50
- return '';
51
- const dt = luxon_1.DateTime.fromISO(value, { zone: 'utc' }).setZone(selfTimezone(context));
52
- return dt.isValid ? dt.toFormat('d MMM yy HH:mm') : '';
75
+ const dt = parseTimestamp(value)?.setZone(selfTimezone(context));
76
+ return dt?.isValid ? dt.toFormat('d MMM yy HH:mm') : '';
53
77
  }
54
78
  /**
55
79
  * IANA region names are ambiguous about DST (e.g. "Europe/London" is UTC+00:00 in winter, UTC+01:00
@@ -5,7 +5,7 @@ import { Bitmap, Renderer, TemplateContext } from './types';
5
5
  * Binding model: a `<text>` element with a `<desc>` child has that child's
6
6
  * content parsed as a flat `key=value,key=value` binding against the render
7
7
  * context and substituted in as the element's text, e.g.
8
- * `<desc>source=resources,resource=tides,path=extremes.[0].time,format=local_time</desc>`
8
+ * `<desc>source=resources,resource=tides,path=extremes[0].time,format=local_time</desc>`
9
9
  * (see `./binding.ts` for the grammar and `./formatters.ts` for the `format=` registry).
10
10
  * The `<text>` element's own visible content is left untouched in the source
11
11
  * file - it's just a placeholder so the template looks sane while laying it
@@ -19,7 +19,7 @@ function ensureWasmInitialized() {
19
19
  * Binding model: a `<text>` element with a `<desc>` child has that child's
20
20
  * content parsed as a flat `key=value,key=value` binding against the render
21
21
  * context and substituted in as the element's text, e.g.
22
- * `<desc>source=resources,resource=tides,path=extremes.[0].time,format=local_time</desc>`
22
+ * `<desc>source=resources,resource=tides,path=extremes[0].time,format=local_time</desc>`
23
23
  * (see `./binding.ts` for the grammar and `./formatters.ts` for the `format=` registry).
24
24
  * The `<text>` element's own visible content is left untouched in the source
25
25
  * file - it's just a placeholder so the template looks sane while laying it
@@ -9,6 +9,7 @@ const bleDiscovery_1 = require("./devices/bleDiscovery");
9
9
  const registry_1 = require("./devices/registry");
10
10
  const svgRenderer_1 = require("./render/svgRenderer");
11
11
  const binding_1 = require("./render/binding");
12
+ const formatters_1 = require("./render/formatters");
12
13
  const unitCategories_1 = require("./unitCategories");
13
14
  const pathMeta_1 = require("./pathMeta");
14
15
  const resolveApiUrl_1 = require("./resolveApiUrl");
@@ -136,7 +137,10 @@ async function considerRepaint(app, config, device, state, getApiUrl) {
136
137
  app.debug(`"${device.friendlyName}": data unchanged, skipping repaint`);
137
138
  return;
138
139
  }
139
- const renderContext = { ...rawContext, meta: { repainted: new Date().toISOString() } };
140
+ const renderContext = {
141
+ ...rawContext,
142
+ meta: { repainted: new Date().toISOString(), local_zone: (0, formatters_1.resolveLocalZoneAbbreviation)(rawContext) },
143
+ };
140
144
  const renderer = new svgRenderer_1.SvgRenderer();
141
145
  const bitmap = await renderer.render(templatePath, renderContext, metadata.width, metadata.height - metadata.voffset);
142
146
  const connectTimeoutMs = config.paintConnectTimeoutSeconds * 1000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhizomatics/signalk-einklabel-plugin",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "SignalK plugin that renders selected SignalK data to eInk Electronic Shelf Labels",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,15 +22,33 @@
22
22
  inkscape:pageopacity="0.0"
23
23
  inkscape:pagecheckerboard="0"
24
24
  inkscape:deskcolor="#d1d1d1"
25
- inkscape:zoom="0.98333333"
26
- inkscape:cx="250.16949"
27
- inkscape:cy="246.61017"
25
+ inkscape:zoom="1.3906433"
26
+ inkscape:cx="202.4243"
27
+ inkscape:cy="151.36879"
28
28
  inkscape:window-width="1488"
29
29
  inkscape:window-height="674"
30
30
  inkscape:window-x="0"
31
31
  inkscape:window-y="33"
32
32
  inkscape:window-maximized="0"
33
- inkscape:current-layer="svg1" />
33
+ inkscape:current-layer="svg1"
34
+ showgrid="true">
35
+ <inkscape:grid
36
+ id="grid1"
37
+ units="px"
38
+ originx="0"
39
+ originy="0"
40
+ spacingx="1"
41
+ spacingy="1"
42
+ empcolor="#0099e5"
43
+ empopacity="0.30196078"
44
+ color="#0099e5"
45
+ opacity="0.14901961"
46
+ empspacing="5"
47
+ dotted="false"
48
+ gridanglex="30"
49
+ gridanglez="30"
50
+ visible="true" />
51
+ </sodipodi:namedview>
34
52
  <rect
35
53
  x="1.0169492"
36
54
  y="2.0798673e-08"
@@ -58,18 +76,16 @@
58
76
  id="desc1">source=resources,resource=tides,path=station.name</desc></text>
59
77
  <text
60
78
  id="station.name-8"
61
- x="325.49939"
62
- y="195.87753"
79
+ x="440.67529"
80
+ y="196.47466"
63
81
  font-size="18"
64
82
  font-family="serif"
65
83
  fill="black"
66
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;stroke-width:2.39571"
67
- transform="scale(0.83037506,1.2042751)"><tspan
84
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;stroke-width:2.39571"
85
+ transform="scale(0.83037506,1.2042751)"><desc
86
+ id="desc21">source=resources,resource=tides,path=station.source.name</desc><tspan
68
87
  sodipodi:role="line"
69
- id="tspan1"
70
- x="325.49939"
71
- y="195.87753"
72
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.99999995px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:2.39571;fill:#ffffff">signalk-einklabel-plugin</tspan></text>
88
+ id="tspan20">SOURCE</tspan></text>
73
89
  <text
74
90
  id="last_repaint"
75
91
  x="4.3477793"
@@ -90,7 +106,7 @@
90
106
  font-family="monospace"
91
107
  fill="black"
92
108
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.6667px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000"><desc
93
- id="desc13">source=resources,resource=tides,path=extremes.[0].label</desc>High Water</text>
109
+ id="desc13">source=resources,resource=tides,path=extremes[0].label</desc>High</text>
94
110
  <text
95
111
  id="extremes.1"
96
112
  x="13.35283"
@@ -99,7 +115,7 @@
99
115
  font-family="monospace"
100
116
  fill="black"
101
117
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.6667px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000"><desc
102
- id="desc14">source=resources,resource=tides,path=extremes.[1].label</desc>Low Water</text>
118
+ id="desc14">source=resources,resource=tides,path=extremes[1].label</desc>Low</text>
103
119
  <text
104
120
  id="extremes.2"
105
121
  x="7.9661016"
@@ -108,123 +124,158 @@
108
124
  font-family="monospace"
109
125
  fill="red"
110
126
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.6667px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000"><desc
111
- id="desc15">source=resources,resource=tides,path=extremes.[2].label</desc>High Water</text>
127
+ id="desc15">source=resources,resource=tides,path=extremes[2].label</desc>High</text>
112
128
  <text
113
129
  id="environment.time.timezoneRegion"
114
- x="338.47461"
115
- y="17.457626"
130
+ x="440.41293"
131
+ y="11.361118"
132
+ font-size="10.373px"
133
+ font-family="sans-serif"
134
+ fill="#ffff00"
135
+ style="direction:rtl;fill:#000000;stroke-width:0.864419"
136
+ transform="scale(0.86441854,1.156847)">UTC+01:00<desc
137
+ id="desc11">source=einklabel,path=local_zone</desc></text>
138
+ <text
139
+ id="lat"
140
+ x="175.5096"
141
+ y="30.720407"
142
+ font-size="12px"
143
+ font-family="sans-serif"
144
+ fill="#ffff00"
145
+ style="fill:#ffff00;-inkscape-font-specification:'Sans, Normal';font-family:Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:18.6667px;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke:#a6a6a6;stroke-opacity:1"><desc
146
+ id="desc18">source=resources,resource=tides,path=station.datums.LAT,category=depth</desc><tspan
147
+ sodipodi:role="line"
148
+ id="tspan17">0.1m</tspan></text>
149
+ <text
150
+ id="hat"
151
+ x="264.41321"
152
+ y="30.800503"
116
153
  font-size="12px"
117
154
  font-family="sans-serif"
118
155
  fill="#ffff00"
119
- style="fill:#000000">UTC+01:00<desc
120
- id="desc11">path=environment.time.timezoneRegion,format=utc_offset</desc></text>
156
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.6667px;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffff00;stroke:#acacaa;stroke-opacity:1"><desc
157
+ id="desc19">source=resources,resource=tides,path=station.datums.HAT,category=depth</desc><tspan
158
+ sodipodi:role="line"
159
+ id="tspan18"
160
+ x="264.41321"
161
+ y="30.800503">6.0m</tspan></text>
121
162
  <text
122
163
  id="extremes.2.level"
123
- x="173.64978"
124
- y="199.27094"
164
+ x="335.54352"
165
+ y="168.08856"
125
166
  font-size="14"
126
167
  font-family="monospace"
127
168
  fill="red"
128
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.44101"
129
- transform="scale(0.95993086,1.0417417)"
130
- inkscape:transform-center-x="11.578901"
131
- inkscape:transform-center-y="-62.5909"><tspan
169
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.78893"
170
+ transform="scale(0.7732368,1.2932649)"
171
+ inkscape:transform-center-x="11.578953"
172
+ inkscape:transform-center-y="-96.464172"><tspan
132
173
  sodipodi:role="line"
133
174
  id="tspan2-0"
134
- x="173.64978"
135
- y="199.27094"
136
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.44101">3.3m</tspan><desc
137
- id="desc6">source=resources,resource=tides,path=extremes.[2].level,category=depth</desc></text>
175
+ x="335.54352"
176
+ y="168.08856"
177
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.78893">3.3m</tspan><desc
178
+ id="desc6">source=resources,resource=tides,path=extremes[2].level,category=depth</desc></text>
138
179
  <text
139
180
  id="extremes.2.time"
140
- x="31.471279"
141
- y="166.20743"
181
+ x="164.78383"
182
+ y="154.23825"
142
183
  font-size="14"
143
184
  font-family="monospace"
144
185
  fill="red"
145
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.31448"
146
- transform="scale(0.77910527,1.2835236)">15:53<desc
147
- id="desc10">source=resources,resource=tides,path=extremes.[2].time,format=local_time</desc></text>
186
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333281px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.51352"
187
+ transform="scale(0.7022624,1.4239692)">15:53<desc
188
+ id="desc10">source=resources,resource=tides,path=extremes[2].time,format=local_time</desc></text>
148
189
  <text
149
190
  id="extremes.1.level"
150
- x="182.71886"
151
- y="135.3521"
191
+ x="345.0629"
192
+ y="118.90553"
152
193
  font-size="14"
153
194
  font-family="monospace"
154
195
  fill="red"
155
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.40832"
156
- transform="scale(0.90784041,1.1015152)"
157
- inkscape:transform-center-y="-96.23206"
158
- inkscape:transform-center-x="-0.00043342573"><tspan
196
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.701"
197
+ transform="scale(0.75163533,1.3304324)"
198
+ inkscape:transform-center-y="-140.38618"
199
+ inkscape:transform-center-x="-0.00038058817"><tspan
159
200
  sodipodi:role="line"
160
201
  id="tspan2-1"
161
- x="182.71886"
162
- y="135.3521"
163
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.40832">1.6m</tspan><desc
164
- id="desc5">source=resources,resource=tides,path=extremes.[1].level,category=depth</desc></text>
202
+ x="345.0629"
203
+ y="118.90553"
204
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.701">1.6m</tspan><desc
205
+ id="desc5">source=resources,resource=tides,path=extremes[1].level,category=depth</desc></text>
165
206
  <text
166
207
  id="extremes.1.time"
167
- x="33.328102"
168
- y="118.54723"
208
+ x="161.1239"
209
+ y="115.52532"
169
210
  font-size="14"
170
211
  font-family="monospace"
171
212
  fill="red"
172
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.31609"
173
- transform="scale(0.77081545,1.2973274)">09:36<desc
174
- id="desc8">source=resources,resource=tides,path=extremes.[1].time,format=local_time</desc></text>
213
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333356px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.48364"
214
+ transform="scale(0.7345432,1.3613903)">09:36<desc
215
+ id="desc8">source=resources,resource=tides,path=extremes[1].time,format=local_time</desc></text>
175
216
  <text
176
217
  id="extremes.0.time"
177
- x="29.798925"
178
- y="79.890121"
218
+ x="156.47511"
219
+ y="73.368919"
179
220
  font-size="14"
180
221
  font-family="monospace"
181
222
  fill="red"
182
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.32646"
183
- transform="scale(0.82428216,1.2131768)">03:27<desc
184
- id="desc3">source=resources,resource=tides,path=extremes.[0].time,format=local_time</desc></text>
223
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333325px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.55397"
224
+ transform="scale(0.74654856,1.3394976)">03:27<desc
225
+ id="desc3">source=resources,resource=tides,path=extremes[0].time,format=local_time</desc></text>
185
226
  <text
186
227
  id="extremes.0.time-8"
187
- x="378.11157"
188
- y="79.890121"
228
+ x="10.326324"
229
+ y="77.594147"
189
230
  font-size="14"
190
231
  font-family="monospace"
191
232
  fill="red"
192
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.32646"
233
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:26.6667px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.32646"
193
234
  transform="scale(0.82428216,1.2131768)"><desc
194
- id="desc2">source=resources,resource=tides,path=extremes.[0].time,format=day_mon</desc>27 Jun</text>
235
+ id="desc2">source=resources,resource=tides,path=extremes[0].time,format=day_mon</desc>27 Jun</text>
195
236
  <text
196
237
  id="extremes.0.time-8-5"
197
- x="378.11157"
198
- y="125.12273"
238
+ x="10.83737"
239
+ y="126.90092"
199
240
  font-size="14"
200
241
  font-family="monospace"
201
242
  fill="red"
202
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.32646"
243
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:26.6667px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.32646"
203
244
  transform="scale(0.82428216,1.2131768)"><desc
204
- id="desc7">source=resources,resource=tides,path=extremes.[1].time,format=day_mon</desc>27 Jun</text>
245
+ id="desc7">source=resources,resource=tides,path=extremes[1].time,format=day_mon</desc>27 Jun</text>
205
246
  <text
206
247
  id="extremes.0.time-8-9"
207
- x="378.11157"
208
- y="174.46791"
248
+ x="12.934491"
249
+ y="161.07082"
209
250
  font-size="14"
210
251
  font-family="monospace"
211
252
  fill="red"
212
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.32646"
213
- transform="scale(0.82428216,1.2131768)"><desc
214
- id="desc9">source=resources,resource=tides,path=extremes.[2].time,format=day_mon</desc>27 Jun</text>
253
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:26.6667px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.4652"
254
+ transform="scale(0.74623156,1.3400666)"><desc
255
+ id="desc9">source=resources,resource=tides,path=extremes[2].time,format=day_mon</desc>27 Jun</text>
215
256
  <text
216
257
  id="extremes.0.level"
217
- x="173.7571"
218
- y="88.829666"
258
+ x="336.3447"
259
+ y="76.456078"
219
260
  font-size="14"
220
261
  font-family="monospace"
221
262
  fill="red"
222
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.38653"
223
- transform="scale(0.95553786,1.046531)"><tspan
263
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.71992"
264
+ transform="scale(0.7703174,1.2981662)"><tspan
224
265
  sodipodi:role="line"
225
266
  id="tspan2"
226
- x="173.7571"
227
- y="88.829666"
228
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.38653">3.4m</tspan><desc
229
- id="desc4">source=resources,resource=tides,path=extremes.[0].level,category=depth</desc></text>
267
+ x="336.3447"
268
+ y="76.456078"
269
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.33333333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.71992">3.4m</tspan><desc
270
+ id="desc4">source=resources,resource=tides,path=extremes[0].level,category=depth</desc></text>
271
+ <text
272
+ xml:space="preserve"
273
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.6667px;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffff00;stroke:#acacaa;stroke-width:0.612283;stroke-opacity:1"
274
+ x="232.26662"
275
+ y="30.201849"
276
+ id="text19"><tspan
277
+ sodipodi:role="line"
278
+ id="tspan19"
279
+ x="232.26662"
280
+ y="30.201849">-&gt;</tspan></text>
230
281
  </svg>