@rhizomatics/signalk-einklabel-plugin 0.5.1 → 0.6.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 CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.6.0
2
+ - Added ability to select images for inclusion in SVG templated based on SignalK value
3
+ - New image selection used to add lunar phase to tide clock example
4
+ - Requires a source for `environment.moonPhase.name`, for example the `dervived-data` plugin
1
5
  # 0.5.1
2
6
  - Work around `resvg-wasm` font limitations by overriding generic font family with matching font name prior to rendering
3
7
  # 0.5.0
package/README.md CHANGED
@@ -10,6 +10,28 @@ Since they are designed to be used in large quantity in small shops, they are ch
10
10
 
11
11
  Unlike some eInk projects, this plugin doesn't require any physical modification to the labels, or loading any new firmware. It can send an image to a supported shelf label fresh out of the box.
12
12
 
13
+ ## Pre-requisites
14
+
15
+ Most of this is about making SignalK work with Bluetooth Low Energy, which is good thing to have anyway, since vendors like Victron, Switchbot, Ruuvi and others have BLE enabled hardware that's useful to have on a boat. [Direct BLE support](https://github.com/SignalK/signalk-server/issues/2411) in SignalK is being planned in 2026.
16
+
17
+ 1. A SignalK server, preferably running Linux (MacOS does weird things with bluetooth)
18
+ 2. A Bluetooth adapter, that can handle BLE (Bluetooth Low Energy), which is Bluetooth v4.0 or higher
19
+ - Bluetooth adapters for Linux can be tricky, TP-Link UB400 and Asus USB-BT500 are two well-known and available ones
20
+ - Some Raspberry Pi models come with suitable Bluetooth it built-in
21
+ - Don't worry about the very latest Bluetooth versions, 4.0 is basic, 5.0 is nice
22
+ - Home Assistant is massively more popular than SignalK, and often also run on Raspberry Pi and similar, so good source of advice
23
+ 3. `bluez` package installed in Linux
24
+ - No need to do this if you have a Raspberry Pi with recent Raspian version, since bluez comes built in.
25
+ - If you're not running a Raspberry Pi, then ensure that the `dbus` package is installed
26
+ 4. One or more supported Electronic Shelf Labels
27
+ - The label used for testing this is the [ZhunyCo 3.7 BRWY](https://www.aliexpress.com/item/1005010050104435.html)
28
+ 5. Correct time zone set on server if local time is to be shown on display
29
+ - Use `raspi-config` on a Raspberry Pi, or `timedatectl` on a Linux server
30
+ - If not set, everything will work, but you may see the wrong zone or not have daylight savings applied
31
+
32
+ Once you have all of that, it may be worth also installing [signalk-victron-ble](https://github.com/stefanor/signalk-victron-ble) or [bt-sensors-plugin](https://github.com/naugehyde/bt-sensors-plugin-sk) to pull in data from other sensors and equipment.
33
+
34
+
13
35
  ## Installation
14
36
 
15
37
  Look for **eInk Label Instrument** in the [SignalK AppStore]() on your
@@ -31,6 +53,9 @@ npm install @rhizomatics/signalk-einklabel-plugin
31
53
 
32
54
  The tide clock needs the [signalk-tides](https://github.com/openwatersio/signalk-tides) plugin to be installed and publishing tides to the Resources API. The [tide.svg](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tide.svg) can be customized to run with other APIs or take data only from SignalK data paths.
33
55
 
56
+ To show the lunar phase, the `environment.moonPhase.name` path is required, which can
57
+ be easily achieved by installing and configuring the `derived-data` plugin.
58
+
34
59
  ## Templating
35
60
 
36
61
  Templates are simply SVG files, to which expressions can be added to use SignalK data, with options to make it easier to read, like rounding or simplifying dates and times. The template can have sample data in the placeholder, so is easy to layout and visualize.
@@ -79,6 +104,18 @@ Additionally, `round=n` can be used to round to limited decimal places.
79
104
 
80
105
  These can all be combined as in `source=resources,resource=tides,path=extremes[2].level,category=depth,round=2`
81
106
 
107
+ ### Non-Textual Fields (Images)
108
+
109
+ The same `<desc>` mechanism works on an `<image>` element instead of a `<text>` element, for a value that's better shown as a picture than as text - a moon phase icon, a wind direction arrow, a weather condition glyph, and so on. Rather than substituting text, the resolved value picks one of a directory of `.svg` files to embed, by an extra required `assets=` key naming that directory (resolved relative to the template file itself, so a bundled template and a user override both work the same way). For example, the tide clock's moon phase icon uses:
110
+
111
+ ```
112
+ path=environment.moonPhase.name,assets=../resources/svg/lunar_phases
113
+ ```
114
+
115
+ The resolved value (e.g. `"Waning Gibbous"`, as published by the [derived-data](https://www.npmjs.com/package/signalk-derived-data) plugin) is normalized to match a filename - lower-cased, punctuation and spaces collapsed to underscores - so `"Waning Gibbous"` picks `waning_gibbous.svg` out of that directory. If the underlying path has no value at all (e.g. the `derived-data` plugin isn't installed), or the value doesn't normalize to any file in the directory, the `<image>` element is simply omitted from that render - no broken image, no placeholder, nothing shown.
116
+
117
+ This is a general mechanism, not specific to moon phases - any `source`/`context`/`path`/`format` combination valid for a `<text>` binding works here too (a `source=resources` value, an explicit `category=`, etc.), the only difference is the required `assets=` directory and the "no match -> no image" behaviour instead of substituted text. To add your own, put a directory of `<value>.svg` files somewhere relative to your template, add an `<image>` element in your SVG editor at the size/position you want, and give it a `<desc>` the same way you would a text field.
118
+
82
119
  ### Fonts
83
120
 
84
121
  Three font types are loaded by default, use the generic font family, or exact font name, in the SVG editor and choose size and weight (bold, semi-bold etc). Some labels will make a decent attempt to gray scale. Use the simple pure red, yellow, white, black to match the label's limited colour choice (some labels only offer black and white, or black/white/red). If a font can't be matched it will default to (sans-serif) Roboto.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Turns a resolved binding value into a filename stem, e.g. `"Waning Gibbous"` -> `"waning_gibbous"` -
3
+ * so a `signalk`/`resources` value using SignalK's own display casing (title case, spaces) can match a
4
+ * file named the conventional snake_case way. Returns `undefined` for anything that can't sensibly name
5
+ * a file (non-string, empty, or all-punctuation), which callers treat as "no image".
6
+ */
7
+ export declare function normalizeAssetKey(value: unknown): string | undefined;
8
+ /**
9
+ * Resolves an `assets=` binding to an actual `.svg` file, looked up as `<key>.svg` inside `assetsDir`
10
+ * resolved relative to the template's own directory (mirroring how a bundled template's relative asset
11
+ * references keep working whether the template itself is the bundled copy or a user override elsewhere) -
12
+ * `undefined` if no matching file exists, which callers treat as "no image" rather than an error, since an
13
+ * unmapped value (e.g. a phase name the asset set doesn't cover) is an expected, not exceptional, case.
14
+ */
15
+ export declare function resolveAssetPath(templatePath: string, assetsDir: string, key: string): string | undefined;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeAssetKey = normalizeAssetKey;
4
+ exports.resolveAssetPath = resolveAssetPath;
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ /**
8
+ * Turns a resolved binding value into a filename stem, e.g. `"Waning Gibbous"` -> `"waning_gibbous"` -
9
+ * so a `signalk`/`resources` value using SignalK's own display casing (title case, spaces) can match a
10
+ * file named the conventional snake_case way. Returns `undefined` for anything that can't sensibly name
11
+ * a file (non-string, empty, or all-punctuation), which callers treat as "no image".
12
+ */
13
+ function normalizeAssetKey(value) {
14
+ if (typeof value !== 'string')
15
+ return undefined;
16
+ const normalized = value
17
+ .trim()
18
+ .toLowerCase()
19
+ .replace(/[^a-z0-9]+/g, '_')
20
+ .replace(/^_+|_+$/g, '');
21
+ return normalized || undefined;
22
+ }
23
+ /**
24
+ * Resolves an `assets=` binding to an actual `.svg` file, looked up as `<key>.svg` inside `assetsDir`
25
+ * resolved relative to the template's own directory (mirroring how a bundled template's relative asset
26
+ * references keep working whether the template itself is the bundled copy or a user override elsewhere) -
27
+ * `undefined` if no matching file exists, which callers treat as "no image" rather than an error, since an
28
+ * unmapped value (e.g. a phase name the asset set doesn't cover) is an expected, not exceptional, case.
29
+ */
30
+ function resolveAssetPath(templatePath, assetsDir, key) {
31
+ const candidate = (0, path_1.join)((0, path_1.dirname)(templatePath), assetsDir, `${key}.svg`);
32
+ return (0, fs_1.existsSync)(candidate) ? candidate : undefined;
33
+ }
@@ -17,6 +17,8 @@ export interface Binding {
17
17
  /** Explicit unit-preferences category (e.g. `depth`, `speed`, `temperature`) for a numeric value with no path metadata of its own, e.g. a `source=resources` value - see `../unitCategories.ts`. */
18
18
  category?: string;
19
19
  round?: number;
20
+ /** Only meaningful on an `<image>` element's binding (see `SvgRenderer`) - a directory, resolved relative to the template file, of `<value>.svg` files to pick from by the resolved value (see `../assets.ts`'s `normalizeAssetKey`). */
21
+ assets?: string;
20
22
  }
21
23
  /**
22
24
  * Parses a `<desc>` element's text content into a `Binding`, e.g.
@@ -29,9 +31,11 @@ export interface Binding {
29
31
  */
30
32
  export declare function parseBinding(desc: string): Binding;
31
33
  /**
32
- * Parses every `<text>` element's `<desc>` binding out of raw SVG source - lets a caller discover what
33
- * data a template needs before fetching anything, with no separate config declaring it (see
34
- * `assembleRawContext` in repaintScheduler.ts).
34
+ * Parses every `<text>` and `<image>` element's `<desc>` binding out of raw SVG source - lets a caller
35
+ * discover what data a template needs before fetching anything, with no separate config declaring it
36
+ * (see `assembleRawContext` in repaintScheduler.ts). `<image>` bindings (see `SvgRenderer`) resolve to a
37
+ * picked asset file rather than substituted text, but still need their underlying value fetched the same
38
+ * way as a `<text>` binding.
35
39
  */
36
40
  export declare function findBindings(svgSource: string): Binding[];
37
41
  /** Resolves a parsed `Binding` against the render context assembled by `assembleRawContext`. */
@@ -7,7 +7,7 @@ exports.renderBinding = renderBinding;
7
7
  const xmldom_1 = require("@xmldom/xmldom");
8
8
  const formatters_1 = require("./formatters");
9
9
  const SOURCES = ['signalk', 'resources', 'einklabel'];
10
- const KNOWN_KEYS = new Set(['source', 'context', 'resource', 'path', 'format', 'category', 'round']);
10
+ const KNOWN_KEYS = new Set(['source', 'context', 'resource', 'path', 'format', 'category', 'round', 'assets']);
11
11
  /**
12
12
  * Parses a `<desc>` element's text content into a `Binding`, e.g.
13
13
  * `source=resources,resource=tides,path=extremes[0].level,category=depth,round=2` or, using the
@@ -56,21 +56,26 @@ function parseBinding(desc) {
56
56
  format: fields.format,
57
57
  category: fields.category,
58
58
  round: fields.round !== undefined ? Number(fields.round) : undefined,
59
+ assets: fields.assets,
59
60
  };
60
61
  }
61
62
  /**
62
- * Parses every `<text>` element's `<desc>` binding out of raw SVG source - lets a caller discover what
63
- * data a template needs before fetching anything, with no separate config declaring it (see
64
- * `assembleRawContext` in repaintScheduler.ts).
63
+ * Parses every `<text>` and `<image>` element's `<desc>` binding out of raw SVG source - lets a caller
64
+ * discover what data a template needs before fetching anything, with no separate config declaring it
65
+ * (see `assembleRawContext` in repaintScheduler.ts). `<image>` bindings (see `SvgRenderer`) resolve to a
66
+ * picked asset file rather than substituted text, but still need their underlying value fetched the same
67
+ * way as a `<text>` binding.
65
68
  */
66
69
  function findBindings(svgSource) {
67
70
  const doc = new xmldom_1.DOMParser().parseFromString(svgSource, 'image/svg+xml');
68
- const elements = doc.getElementsByTagName('text');
69
71
  const bindings = [];
70
- for (let i = 0; i < elements.length; i++) {
71
- const desc = elements.item(i)?.getElementsByTagName('desc').item(0);
72
- if (desc?.textContent) {
73
- bindings.push(parseBinding(desc.textContent));
72
+ for (const tagName of ['text', 'image']) {
73
+ const elements = doc.getElementsByTagName(tagName);
74
+ for (let i = 0; i < elements.length; i++) {
75
+ const desc = elements.item(i)?.getElementsByTagName('desc').item(0);
76
+ if (desc?.textContent) {
77
+ bindings.push(parseBinding(desc.textContent));
78
+ }
74
79
  }
75
80
  }
76
81
  return bindings;
@@ -16,6 +16,14 @@ import { Bitmap, Renderer, TemplateContext } from './types';
16
16
  * `getElementsByTagName` is a live NodeList, so that previously truncated the
17
17
  * whole tree and rendered blank.
18
18
  *
19
+ * Non-textual inclusion: an `<image>` element with a `<desc>` child works the same way, except the
20
+ * resolved value picks one of a directory of `.svg` files (an `assets=` key, required, names that
21
+ * directory - see `./assets.ts`) rather than substituting text, and the element is dropped entirely
22
+ * (rather than showing an error placeholder) whenever the value is unavailable or doesn't map to a
23
+ * file - e.g. a device without the `derived-data` plugin installed simply shows no moon-phase icon.
24
+ * The matched file's contents are inlined as a `data:image/svg+xml;base64,...` URI so no filesystem
25
+ * access happens at resvg-wasm's render step.
26
+ *
19
27
  * resvg-wasm cannot see the host's installed fonts (`loadSystemFonts`/`fontFiles`
20
28
  * are silently no-ops under plain Node) - it only renders text if given font
21
29
  * bytes directly via `fontBuffers`, read from disk by us. Without at least one
@@ -5,6 +5,7 @@ const promises_1 = require("fs/promises");
5
5
  const xmldom_1 = require("@xmldom/xmldom");
6
6
  const resvg_wasm_1 = require("@resvg/resvg-wasm");
7
7
  const binding_1 = require("./binding");
8
+ const assets_1 = require("./assets");
8
9
  const fonts_1 = require("./fonts");
9
10
  let wasmReady;
10
11
  function ensureWasmInitialized() {
@@ -51,6 +52,14 @@ function expandGenericFontFamilies(svgSource) {
51
52
  * `getElementsByTagName` is a live NodeList, so that previously truncated the
52
53
  * whole tree and rendered blank.
53
54
  *
55
+ * Non-textual inclusion: an `<image>` element with a `<desc>` child works the same way, except the
56
+ * resolved value picks one of a directory of `.svg` files (an `assets=` key, required, names that
57
+ * directory - see `./assets.ts`) rather than substituting text, and the element is dropped entirely
58
+ * (rather than showing an error placeholder) whenever the value is unavailable or doesn't map to a
59
+ * file - e.g. a device without the `derived-data` plugin installed simply shows no moon-phase icon.
60
+ * The matched file's contents are inlined as a `data:image/svg+xml;base64,...` URI so no filesystem
61
+ * access happens at resvg-wasm's render step.
62
+ *
54
63
  * resvg-wasm cannot see the host's installed fonts (`loadSystemFonts`/`fontFiles`
55
64
  * are silently no-ops under plain Node) - it only renders text if given font
56
65
  * bytes directly via `fontBuffers`, read from disk by us. Without at least one
@@ -94,6 +103,44 @@ class SvgRenderer {
94
103
  element.textContent = 'ERROR';
95
104
  }
96
105
  }
106
+ // Collected up front, rather than iterated live like the `<text>` loop above - some of these get
107
+ // removed outright (see below), and removing from a live NodeList while iterating it by index would
108
+ // skip whatever shifts into the removed slot.
109
+ const imageElements = [];
110
+ const rawImageElements = doc.getElementsByTagName('image');
111
+ for (let i = 0; i < rawImageElements.length; i++) {
112
+ const element = rawImageElements.item(i);
113
+ if (element)
114
+ imageElements.push(element);
115
+ }
116
+ for (const element of imageElements) {
117
+ const descElement = element.getElementsByTagName('desc').item(0);
118
+ if (!descElement)
119
+ continue;
120
+ // Same one-field-at-a-time isolation as the `<text>` loop - and the same "no value" outcome
121
+ // (here, dropping the element rather than blanking to 'ERROR' text) for a value that isn't
122
+ // available at all as for one that doesn't map to any asset file, per the binding's contract.
123
+ try {
124
+ const binding = (0, binding_1.parseBinding)(descElement.textContent ?? '');
125
+ if (!binding.assets) {
126
+ throw new Error('an <image> binding requires an "assets" key naming the directory to pick a file from');
127
+ }
128
+ const key = (0, assets_1.normalizeAssetKey)((0, binding_1.resolveBinding)(binding, context));
129
+ const assetPath = key && (0, assets_1.resolveAssetPath)(svgTemplatePath, binding.assets, key);
130
+ if (!assetPath) {
131
+ element.parentNode?.removeChild(element);
132
+ continue;
133
+ }
134
+ const assetSource = await (0, promises_1.readFile)(assetPath, 'utf-8');
135
+ const dataUri = `data:image/svg+xml;base64,${Buffer.from(assetSource).toString('base64')}`;
136
+ const hrefAttr = element.getAttribute('href') !== null ? 'href' : 'xlink:href';
137
+ element.setAttribute(hrefAttr, dataUri);
138
+ }
139
+ catch (err) {
140
+ console.error(`image "${descElement.textContent}" failed to render: ${err.message}`);
141
+ element.parentNode?.removeChild(element);
142
+ }
143
+ }
97
144
  const svgOutput = new xmldom_1.XMLSerializer().serializeToString(doc);
98
145
  const resvg = new resvg_wasm_1.Resvg(svgOutput, {
99
146
  fitTo: { mode: 'width', value: width },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhizomatics/signalk-einklabel-plugin",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
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",
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "dist",
12
12
  "templates",
13
+ "resources",
13
14
  "docs",
14
15
  "CHANGELOG.md",
15
16
  "README.md"
@@ -0,0 +1,51 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="first_quarter.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2">
37
+ <path
38
+ d="M 749.07161 200.5305 A 168.70027 168.70027 0 1 1 411.67107,200.5305 A 168.70027 168.70027 0 1 1 749.07161 200.5305 z"
39
+ transform="translate(-380.37134,-0.5305023)"
40
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
41
+ id="path6166" />
42
+ </g>
43
+ <g
44
+ id="layer3"
45
+ style="fill:#ffff00">
46
+ <path
47
+ d="M 199.86125,368.70045 C 292.83638,368.70045 368.20758,293.17071 368.20758,199.99995 C 368.20758,106.82922 292.83638,31.29945 199.86125,31.29945 C 199.12886,143.76656 199.72222,256.23321 199.86125,368.70045 z "
48
+ style="fill:#ffff00;fill-opacity:1;stroke:none"
49
+ id="path7307" />
50
+ </g>
51
+ </svg>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="full_moon.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2"
37
+ style="fill:#ffff00">
38
+ <path
39
+ d="M 749.07161 200.5305 A 168.70027 168.70027 0 1 1 411.67107,200.5305 A 168.70027 168.70027 0 1 1 749.07161 200.5305 z"
40
+ transform="translate(-380.37134,-0.5305023)"
41
+ style="fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
42
+ id="path6166" />
43
+ </g>
44
+ <g
45
+ id="layer3" />
46
+ </svg>
@@ -0,0 +1,43 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="new_moon.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2">
37
+ <path
38
+ d="M 749.07161 200.5305 A 168.70027 168.70027 0 1 1 411.67107,200.5305 A 168.70027 168.70027 0 1 1 749.07161 200.5305 z"
39
+ transform="translate(-380.37134,-0.5305023)"
40
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
41
+ id="path6166" />
42
+ </g>
43
+ </svg>
@@ -0,0 +1,52 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="third_quarter.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2">
37
+ <path
38
+ d="M 749.07161,200.5305 C 749.07161,293.70109 673.54193,369.23077 580.37134,369.23077 C 487.20075,369.23077 411.67107,293.70109 411.67107,200.5305 C 411.67107,107.35991 487.20075,31.83023 580.37134,31.83023 C 673.54193,31.83023 749.07161,107.35991 749.07161,200.5305 z "
39
+ transform="translate(-380.37134,-0.5305023)"
40
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
41
+ id="path6166" />
42
+ </g>
43
+ <g
44
+ transform="matrix(-1,0,0,1,399.50816,0)"
45
+ id="layer3"
46
+ style="fill:#ffff00">
47
+ <path
48
+ d="M 199.86125,368.70045 C 292.83638,368.70045 368.20758,293.17071 368.20758,199.99995 C 368.20758,106.82922 292.83638,31.29945 199.86125,31.29945 C 199.12886,143.76656 199.72222,256.23321 199.86125,368.70045 z "
49
+ style="fill:#ffff00;fill-opacity:1;stroke:none"
50
+ id="path7307" />
51
+ </g>
52
+ </svg>
@@ -0,0 +1,51 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="waning_crescent.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2">
37
+ <path
38
+ d="M 749.07161,200.5305 C 749.07161,293.70109 673.54193,369.23077 580.37134,369.23077 C 487.20075,369.23077 411.67107,293.70109 411.67107,200.5305 C 411.67107,107.35991 487.20075,31.83023 580.37134,31.83023 C 673.54193,31.83023 749.07161,107.35991 749.07161,200.5305 z "
39
+ transform="translate(-380.37134,-0.5305023)"
40
+ style="fill:#00000f;fill-opacity:0.94117647;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
41
+ id="path6166" />
42
+ </g>
43
+ <g
44
+ id="layer3"
45
+ style="fill:#ffff00">
46
+ <path
47
+ d="M 199.97697,368.70045 C 106.80628,368.70045 31.27656,293.17073 31.27656,200 C 31.27656,106.8293 106.80628,31.299555 199.97697,31.299555 C 153.39106,31.299555 115.62676,106.8293 115.62676,200 C 115.62676,293.17073 153.39106,368.70045 199.97697,368.70045 z "
48
+ style="fill:#ffff00;fill-opacity:1;stroke:none"
49
+ id="path7307" />
50
+ </g>
51
+ </svg>
@@ -0,0 +1,51 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="waning_gibbous.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2"
37
+ style="fill:#ffff00">
38
+ <path
39
+ d="M 749.07161 200.5305 A 168.70027 168.70027 0 1 1 411.67107,200.5305 A 168.70027 168.70027 0 1 1 749.07161 200.5305 z"
40
+ transform="translate(-380.37134,-0.5305023)"
41
+ style="fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
42
+ id="path6166" />
43
+ </g>
44
+ <g
45
+ id="layer3">
46
+ <path
47
+ d="M 200.0278,368.70045 C 293.19849,368.70045 368.72822,293.17073 368.72822,200 C 368.72822,106.8293 293.19849,31.299555 200.0278,31.299555 C 246.61371,31.299555 284.37801,106.8293 284.37801,200 C 284.37801,293.17073 246.61371,368.70045 200.0278,368.70045 z "
48
+ style="fill:#00000f;fill-opacity:0.94117647;stroke:none"
49
+ id="path7307" />
50
+ </g>
51
+ </svg>
@@ -0,0 +1,51 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="waxing_crescent.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2">
37
+ <path
38
+ d="M 749.07161 200.5305 A 168.70027 168.70027 0 1 1 411.67107,200.5305 A 168.70027 168.70027 0 1 1 749.07161 200.5305 z"
39
+ transform="translate(-380.37134,-0.5305023)"
40
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
41
+ id="path6166" />
42
+ </g>
43
+ <g
44
+ id="layer3"
45
+ style="fill:#ffff00">
46
+ <path
47
+ d="M 199.862,368.70045 C 293.03269,368.70045 368.56242,293.17073 368.56242,200 C 368.56242,106.8293 293.03269,31.299555 199.862,31.299555 C 246.44791,31.299555 284.21221,106.8293 284.21221,200 C 284.21221,293.17073 246.44791,368.70045 199.862,368.70045 z "
48
+ style="fill:#ffff00;fill-opacity:1;stroke:none"
49
+ id="path7307" />
50
+ </g>
51
+ </svg>
@@ -0,0 +1,51 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ version="1.0"
6
+ width="400"
7
+ height="400"
8
+ id="svg2"
9
+ sodipodi:docname="waxing_gibbous.svg"
10
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ xmlns:svg="http://www.w3.org/2000/svg">
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#000000"
19
+ borderopacity="0.25"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="0.59"
25
+ inkscape:cx="200.84746"
26
+ inkscape:cy="200"
27
+ inkscape:window-width="1312"
28
+ inkscape:window-height="449"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="33"
31
+ inkscape:window-maximized="0"
32
+ inkscape:current-layer="svg2" />
33
+ <defs
34
+ id="defs4" />
35
+ <g
36
+ id="layer2"
37
+ style="fill:#ffff00">
38
+ <path
39
+ d="M 749.07161 200.5305 A 168.70027 168.70027 0 1 1 411.67107,200.5305 A 168.70027 168.70027 0 1 1 749.07161 200.5305 z"
40
+ transform="translate(-380.37134,-0.5305023)"
41
+ style="fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:3.54330707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
42
+ id="path6166" />
43
+ </g>
44
+ <g
45
+ id="layer3">
46
+ <path
47
+ d="M 199.97697,368.70045 C 106.80628,368.70045 31.27656,293.17073 31.27656,200 C 31.27656,106.8293 106.80628,31.299555 199.97697,31.299555 C 153.39106,31.299555 115.62676,106.8293 115.62676,200 C 115.62676,293.17073 153.39106,368.70045 199.97697,368.70045 z "
48
+ style="fill:#00000f;fill-opacity:0.94117647;stroke:none"
49
+ id="path7307" />
50
+ </g>
51
+ </svg>
@@ -7,13 +7,13 @@
7
7
  id="svg1"
8
8
  sodipodi:docname="tide.svg"
9
9
  inkscape:version="1.3.2 (091e20e, 2023-11-25)"
10
+ xml:space="preserve"
10
11
  xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
12
  xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
+ xmlns:xlink="http://www.w3.org/1999/xlink"
12
14
  xmlns="http://www.w3.org/2000/svg"
13
- xmlns:svg="http://www.w3.org/2000/svg">
14
- <defs
15
- id="defs1" />
16
- <sodipodi:namedview
15
+ xmlns:svg="http://www.w3.org/2000/svg"><defs
16
+ id="defs1" /><sodipodi:namedview
17
17
  id="namedview1"
18
18
  pagecolor="#ffffff"
19
19
  bordercolor="#000000"
@@ -23,16 +23,16 @@
23
23
  inkscape:pagecheckerboard="0"
24
24
  inkscape:deskcolor="#d1d1d1"
25
25
  inkscape:zoom="1.3906433"
26
- inkscape:cx="176.537"
27
- inkscape:cy="132.67241"
26
+ inkscape:cx="249.88435"
27
+ inkscape:cy="101.03238"
28
28
  inkscape:window-width="1488"
29
29
  inkscape:window-height="674"
30
30
  inkscape:window-x="24"
31
31
  inkscape:window-y="33"
32
32
  inkscape:window-maximized="0"
33
33
  inkscape:current-layer="svg1"
34
- showgrid="true">
35
- <inkscape:grid
34
+ showgrid="true"
35
+ showguides="true"><inkscape:grid
36
36
  id="grid1"
37
37
  units="px"
38
38
  originx="0"
@@ -47,24 +47,20 @@
47
47
  dotted="false"
48
48
  gridanglex="30"
49
49
  gridanglez="30"
50
- visible="true" />
51
- </sodipodi:namedview>
52
- <rect
50
+ visible="true" /></sodipodi:namedview><rect
53
51
  x="1.0169492"
54
52
  y="2.0798673e-08"
55
53
  width="416"
56
54
  height="240"
57
55
  fill="#ffffff"
58
- id="rect1" />
59
- <rect
56
+ id="rect1" /><rect
60
57
  style="fill:#ffff00;stroke:#000000;stroke-width:0.614045"
61
58
  id="rect2"
62
59
  width="416.94916"
63
60
  height="18.410578"
64
61
  x="-24.928873"
65
62
  y="-242.40596"
66
- transform="matrix(1,0,-0.1068988,-0.99426991,0,0)" />
67
- <text
63
+ transform="matrix(1,0,-0.1068988,-0.99426991,0,0)" /><text
68
64
  id="station.name"
69
65
  x="11.235578"
70
66
  y="26.851856"
@@ -73,8 +69,7 @@
73
69
  fill="yellow"
74
70
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;font-family:serif;-inkscape-font-specification:'Roboto Serif 20pt';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffff00;stroke:#000000;stroke-width:1.2278;stroke-opacity:1"
75
71
  transform="scale(0.89274878,1.1201359)">Tidal Station Name<desc
76
- id="desc1">source=resources,resource=tides,path=station.name</desc></text>
77
- <text
72
+ id="desc1">source=resources,resource=tides,path=station.name</desc></text><text
78
73
  id="source"
79
74
  x="347.95166"
80
75
  y="226.1548"
@@ -88,8 +83,7 @@
88
83
  id="tspan20"
89
84
  x="347.95166"
90
85
  y="226.1548"
91
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3332995px;font-family:sans-serif;-inkscape-font-specification:'Roboto',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:3.80045">SOURCE</tspan></text>
92
- <text
86
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3332995px;font-family:sans-serif;-inkscape-font-specification:'Roboto',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:3.80045">SOURCE</tspan></text><text
93
87
  id="last_repaint"
94
88
  x="4.3477793"
95
89
  y="201.9895"
@@ -100,8 +94,7 @@
100
94
  transform="scale(0.85412558,1.170788)"><desc
101
95
  id="desc12">source=einklabel,path=repainted,format=local_datetime_short</desc><tspan
102
96
  sodipodi:role="line"
103
- id="tspan3">27 Jun 26 18:05</tspan></text>
104
- <text
97
+ id="tspan3">27 Jun 26 18:05</tspan></text><text
105
98
  id="extremes.0"
106
99
  x="8.6851931"
107
100
  y="57.123634"
@@ -109,8 +102,7 @@
109
102
  font-family="monospace"
110
103
  fill="black"
111
104
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:22.6667px;font-family:sans-serif;-inkscape-font-specification:'Roboto, Normal',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000"><desc
112
- id="desc13">source=resources,resource=tides,path=extremes[0].label</desc>High or Low</text>
113
- <text
105
+ id="desc13">source=resources,resource=tides,path=extremes[0].label</desc>High or Low</text><text
114
106
  id="extremes.1"
115
107
  x="7.6000967"
116
108
  y="117.85719"
@@ -118,8 +110,7 @@
118
110
  font-family="monospace"
119
111
  fill="black"
120
112
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:22.6667px;font-family:sans-serif;-inkscape-font-specification:'Roboto, Normal',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000"><desc
121
- id="desc14">source=resources,resource=tides,path=extremes[1].label</desc>High or Low</text>
122
- <text
113
+ id="desc14">source=resources,resource=tides,path=extremes[1].label</desc>High or Low</text><text
123
114
  id="extremes.2"
124
115
  x="7.2470098"
125
116
  y="176.12112"
@@ -127,20 +118,18 @@
127
118
  font-family="monospace"
128
119
  fill="red"
129
120
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:22.6667px;font-family:sans-serif;-inkscape-font-specification:'Roboto, Normal',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000"><desc
130
- id="desc15">source=resources,resource=tides,path=extremes[2].label</desc>High or Low</text>
131
- <text
121
+ id="desc15">source=resources,resource=tides,path=extremes[2].label</desc>High or Low</text><text
132
122
  id="environment.time.timezoneRegion"
133
- x="220.79686"
123
+ x="239.09819"
134
124
  y="204.67754"
135
125
  font-size="10.373px"
136
126
  font-family="sans-serif"
137
127
  fill="#ffff00"
138
128
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:sans-serif;-inkscape-font-specification:'Roboto, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;direction:rtl;fill:#000000;stroke-width:0.864419"
139
129
  transform="scale(0.86441854,1.156847)">UTC+01:00<desc
140
- id="desc11">source=einklabel,path=local_zone</desc></text>
141
- <text
130
+ id="desc11">source=einklabel,path=local_zone</desc></text><text
142
131
  id="lat"
143
- x="289.84515"
132
+ x="282.65424"
144
133
  y="24.967674"
145
134
  font-size="12px"
146
135
  font-family="sans-serif"
@@ -149,12 +138,11 @@
149
138
  id="desc18">source=resources,resource=tides,path=station.datums.LAT,category=depth,round=1</desc><tspan
150
139
  sodipodi:role="line"
151
140
  id="tspan17"
152
- x="289.84515"
153
- y="24.967674">0.1m</tspan></text>
154
- <text
141
+ x="282.65424"
142
+ y="24.967674">0.0m</tspan></text><text
155
143
  id="hat"
156
- x="363.64786"
157
- y="25.766863"
144
+ x="360.77148"
145
+ y="24.967674"
158
146
  font-size="12px"
159
147
  font-family="sans-serif"
160
148
  fill="#ffff00"
@@ -162,26 +150,24 @@
162
150
  id="desc19">source=resources,resource=tides,path=station.datums.HAT,category=depth,round=1</desc><tspan
163
151
  sodipodi:role="line"
164
152
  id="tspan18"
165
- x="363.64786"
166
- y="25.766863">6.0m</tspan></text>
167
- <text
153
+ x="360.77148"
154
+ y="24.967674">9.9m</tspan></text><text
168
155
  id="extremes.2.level"
169
- x="334.61356"
170
- y="159.19211"
156
+ x="322.52386"
157
+ y="158.08005"
171
158
  font-size="14"
172
159
  font-family="monospace"
173
160
  fill="red"
174
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.78893"
161
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'\'Roboto Mono, Bold\', monospace';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.78893"
175
162
  transform="scale(0.7732368,1.2932649)"
176
163
  inkscape:transform-center-x="11.578953"
177
164
  inkscape:transform-center-y="-96.464172"><tspan
178
165
  sodipodi:role="line"
179
166
  id="tspan2-0"
180
- x="334.61356"
181
- y="159.19211"
182
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;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
183
- id="desc6">source=resources,resource=tides,path=extremes[2].level,category=depth</desc></text>
184
- <text
167
+ x="322.52386"
168
+ y="158.08005"
169
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'\'Roboto Mono, Bold\', monospace';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
170
+ id="desc6">source=resources,resource=tides,path=extremes[2].level,category=depth</desc></text><text
185
171
  id="extremes.2.time"
186
172
  x="194.47879"
187
173
  y="146.1584"
@@ -190,25 +176,23 @@
190
176
  fill="red"
191
177
  style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.51352"
192
178
  transform="scale(0.70226239,1.4239692)">15:53<desc
193
- id="desc10">source=resources,resource=tides,path=extremes[2].time,format=local_time</desc></text>
194
- <text
179
+ id="desc10">source=resources,resource=tides,path=extremes[2].time,format=local_time</desc></text><text
195
180
  id="extremes.1.level"
196
- x="341.23608"
197
- y="110.25762"
181
+ x="328.79895"
182
+ y="109.17664"
198
183
  font-size="14"
199
184
  font-family="monospace"
200
185
  fill="red"
201
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.701"
186
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'\'Roboto Mono, Bold\', monospace';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.701"
202
187
  transform="scale(0.75163533,1.3304324)"
203
188
  inkscape:transform-center-y="-140.38618"
204
189
  inkscape:transform-center-x="-0.00038058817"><tspan
205
190
  sodipodi:role="line"
206
191
  id="tspan2-1"
207
- x="341.23608"
208
- y="110.25762"
209
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;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
210
- id="desc5">source=resources,resource=tides,path=extremes[1].level,category=depth</desc></text>
211
- <text
192
+ x="328.79895"
193
+ y="109.17664"
194
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'\'Roboto Mono, Bold\', monospace';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
195
+ id="desc5">source=resources,resource=tides,path=extremes[1].level,category=depth</desc></text><text
212
196
  id="extremes.1.time"
213
197
  x="187.55594"
214
198
  y="108.65867"
@@ -217,8 +201,7 @@
217
201
  fill="red"
218
202
  style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.48364"
219
203
  transform="scale(0.7345432,1.3613903)">09:36<desc
220
- id="desc8">source=resources,resource=tides,path=extremes[1].time,format=local_time</desc></text>
221
- <text
204
+ id="desc8">source=resources,resource=tides,path=extremes[1].time,format=local_time</desc></text><text
222
205
  id="extremes.0.time"
223
206
  x="184.40854"
224
207
  y="64.242691"
@@ -227,8 +210,7 @@
227
210
  fill="red"
228
211
  style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.55397"
229
212
  transform="scale(0.74654856,1.3394976)">03:27<desc
230
- id="desc3">source=resources,resource=tides,path=extremes[0].time,format=local_time</desc></text>
231
- <text
213
+ id="desc3">source=resources,resource=tides,path=extremes[0].time,format=local_time</desc></text><text
232
214
  id="extremes.0.time-8"
233
215
  x="10.326324"
234
216
  y="74.630478"
@@ -237,8 +219,7 @@
237
219
  fill="red"
238
220
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:22.6667px;font-family:sans-serif;-inkscape-font-specification:'Roboto, Normal',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.32646"
239
221
  transform="scale(0.82428216,1.2131768)"><desc
240
- id="desc2">source=resources,resource=tides,path=extremes[0].time,format=day_mon</desc>27 Jun</text>
241
- <text
222
+ id="desc2">source=resources,resource=tides,path=extremes[0].time,format=day_mon</desc>27 Jun</text><text
242
223
  id="extremes.0.time-8-5"
243
224
  x="9.9649849"
244
225
  y="123.34451"
@@ -247,8 +228,7 @@
247
228
  fill="red"
248
229
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:22.6667px;font-family:sans-serif;-inkscape-font-specification:'Roboto, Normal',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.32646"
249
230
  transform="scale(0.82428216,1.2131768)"><desc
250
- id="desc7">source=resources,resource=tides,path=extremes[1].time,format=day_mon</desc>27 Jun</text>
251
- <text
231
+ id="desc7">source=resources,resource=tides,path=extremes[1].time,format=day_mon</desc>27 Jun</text><text
252
232
  id="extremes.0.time-8-9"
253
233
  x="12.934491"
254
234
  y="156.77794"
@@ -257,30 +237,36 @@
257
237
  fill="red"
258
238
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:22.6667px;font-family:sans-serif;-inkscape-font-specification:'Roboto, Normal',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#666666;stroke-width:1.4652"
259
239
  transform="scale(0.74623156,1.3400666)"><desc
260
- id="desc9">source=resources,resource=tides,path=extremes[2].time,format=day_mon</desc>27 Jun</text>
261
- <text
240
+ id="desc9">source=resources,resource=tides,path=extremes[2].time,format=day_mon</desc>27 Jun</text><text
262
241
  id="extremes.0.level"
263
- x="334.47769"
264
- y="65.931427"
242
+ x="322.34219"
243
+ y="64.82357"
265
244
  font-size="14"
266
245
  font-family="monospace"
267
246
  fill="red"
268
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.71992"
247
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'\'Roboto Mono, Bold\', monospace';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.71992"
269
248
  transform="scale(0.7703174,1.2981662)"><tspan
270
249
  sodipodi:role="line"
271
250
  id="tspan2"
272
- x="334.47769"
273
- y="65.931427"
274
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'Roboto Mono, Bold',monospace;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
275
- id="desc4">source=resources,resource=tides,path=extremes[0].level,category=depth</desc></text>
276
- <text
251
+ x="322.34219"
252
+ y="64.82357"
253
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:37.3333px;font-family:monospace;-inkscape-font-specification:'\'Roboto Mono, Bold\', monospace';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
254
+ id="desc4">source=resources,resource=tides,path=extremes[0].level,category=depth</desc></text><text
277
255
  xml:space="preserve"
278
256
  style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.3333px;font-family:sans-serif;-inkscape-font-specification:'\'Roboto, Normal\', sans-serif';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1a1a1a;stroke:#fefe54;stroke-width:0.612283;stroke-opacity:1"
279
- x="340.84946"
280
- y="25.168207"
257
+ x="337.254"
258
+ y="25.327612"
281
259
  id="text19"><tspan
282
260
  sodipodi:role="line"
283
261
  id="tspan19"
284
- x="340.84946"
285
- y="25.168207">-&gt;</tspan></text>
286
- </svg>
262
+ x="337.254"
263
+ y="25.327612">-&gt;</tspan></text><image
264
+ preserveAspectRatio="none"
265
+ inkscape:svg-dpi="96"
266
+ width="71.375114"
267
+ height="70.656021"
268
+ xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjwh&#10;LS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8&#10;c3ZnCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJo&#10;dHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4wIgogICB3aWR0aD0iNDAw&#10;IgogICBoZWlnaHQ9IjQwMCIKICAgaWQ9InN2ZzIiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAv&#10;PgogIDxnCiAgICAgaWQ9ImxheWVyMSI+CiAgICA8cmVjdAogICAgICAgd2lkdGg9IjQwMCIKICAg&#10;ICAgIGhlaWdodD0iNDAwIgogICAgICAgeD0iMCIKICAgICAgIHk9Ii0xLjU0NTQ0MzVlLTAxNCIK&#10;ICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwYmI7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7c3Ry&#10;b2tlLXdpZHRoOjMuNTQzMzA3MDc7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5&#10;Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGlkPSJyZWN0MzIzOSIgLz4KICA8L2c+CiAg&#10;PGcKICAgICBpZD0ibGF5ZXIyIj4KICAgIDxwYXRoCiAgICAgICBkPSJNIDc0OS4wNzE2MSAyMDAu&#10;NTMwNSBBIDE2OC43MDAyNyAxNjguNzAwMjcgMCAxIDEgIDQxMS42NzEwNywyMDAuNTMwNSBBIDE2&#10;OC43MDAyNyAxNjguNzAwMjcgMCAxIDEgIDc0OS4wNzE2MSAyMDAuNTMwNSB6IgogICAgICAgdHJh&#10;bnNmb3JtPSJ0cmFuc2xhdGUoLTM4MC4zNzEzNCwtMC41MzA1MDIzKSIKICAgICAgIHN0eWxlPSJm&#10;aWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjMuNTQz&#10;MzA3MDc7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9w&#10;YWNpdHk6MSIKICAgICAgIGlkPSJwYXRoNjE2NiIgLz4KICA8L2c+CiAgPGcKICAgICBpZD0ibGF5&#10;ZXIzIj4KICAgIDxwYXRoCiAgICAgICBkPSJNIDIwMC4wMjc4LDM2OC43MDA0NSBDIDI5My4xOTg0&#10;OSwzNjguNzAwNDUgMzY4LjcyODIyLDI5My4xNzA3MyAzNjguNzI4MjIsMjAwIEMgMzY4LjcyODIy&#10;LDEwNi44MjkzIDI5My4xOTg0OSwzMS4yOTk1NTUgMjAwLjAyNzgsMzEuMjk5NTU1IEMgMjQ2LjYx&#10;MzcxLDMxLjI5OTU1NSAyODQuMzc4MDEsMTA2LjgyOTMgMjg0LjM3ODAxLDIwMCBDIDI4NC4zNzgw&#10;MSwyOTMuMTcwNzMgMjQ2LjYxMzcxLDM2OC43MDA0NSAyMDAuMDI3OCwzNjguNzAwNDUgeiAiCiAg&#10;ICAgICBzdHlsZT0iZmlsbDojMDAwMDBmO2ZpbGwtb3BhY2l0eTowLjk0MTE3NjQ3O3N0cm9rZTpu&#10;b25lIgogICAgICAgaWQ9InBhdGg3MzA3IiAvPgogIDwvZz4KPC9zdmc+Cg==&#10;"
269
+ id="moon_phase"
270
+ x="340.3974"
271
+ y="90.153488"><desc
272
+ id="desc22">path=environment.moonPhase.name,assets=../resources/svg/lunar_phases</desc></image></svg>