mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 12:27:07 +00:00
Compare commits
2 Commits
bc59885a02
...
v3-branch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc2550a75 | ||
|
|
67f3f83c61 |
14
lib/www/client/source/package-lock.json
generated
14
lib/www/client/source/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^5.6.55",
|
"@mdi/font": "^7.2.96",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"d3": "^7.0.1",
|
"d3": "^7.0.1",
|
||||||
"jwt-decode": "^3.0.0",
|
"jwt-decode": "^3.0.0",
|
||||||
@@ -1763,9 +1763,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mdi/font": {
|
"node_modules/@mdi/font": {
|
||||||
"version": "5.9.55",
|
"version": "7.2.96",
|
||||||
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-5.9.55.tgz",
|
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.2.96.tgz",
|
||||||
"integrity": "sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg=="
|
"integrity": "sha512-e//lmkmpFUMZKhmCY9zdjRe4zNXfbOIJnn6xveHbaV2kSw5aJ5dLXUxcRt1Gxfi7ZYpFLUWlkG2MGSFAiqAu7w=="
|
||||||
},
|
},
|
||||||
"node_modules/@mrmlnc/readdir-enhanced": {
|
"node_modules/@mrmlnc/readdir-enhanced": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
@@ -16432,9 +16432,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mdi/font": {
|
"@mdi/font": {
|
||||||
"version": "5.9.55",
|
"version": "7.2.96",
|
||||||
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-5.9.55.tgz",
|
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.2.96.tgz",
|
||||||
"integrity": "sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg=="
|
"integrity": "sha512-e//lmkmpFUMZKhmCY9zdjRe4zNXfbOIJnn6xveHbaV2kSw5aJ5dLXUxcRt1Gxfi7ZYpFLUWlkG2MGSFAiqAu7w=="
|
||||||
},
|
},
|
||||||
"@mrmlnc/readdir-enhanced": {
|
"@mrmlnc/readdir-enhanced": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"build": "vue-cli-service build"
|
"build": "vue-cli-service build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^5.6.55",
|
"@mdi/font": "^7.2.96",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"d3": "^7.0.1",
|
"d3": "^7.0.1",
|
||||||
"jwt-decode": "^3.0.0",
|
"jwt-decode": "^3.0.0",
|
||||||
|
|||||||
@@ -119,7 +119,11 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
<template v-slot:item.srss="{item}">
|
<template v-slot:item.srss="{item}">
|
||||||
<v-icon small :title="srssInfo(item)">{{srssIcon(item)}}</v-icon>
|
<span style="white-space: nowrap;">
|
||||||
|
<v-icon small :title="srssInfo(item)">{{srssIcon(item)}}</v-icon>
|
||||||
|
/
|
||||||
|
<v-icon small :title="wxInfo(item)" v-if="item.meta.wx">{{wxIcon(item)}}</v-icon>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:item.sequence="{item, value}">
|
<template v-slot:item.sequence="{item, value}">
|
||||||
@@ -422,6 +426,123 @@ export default {
|
|||||||
plannerConfig: null,
|
plannerConfig: null,
|
||||||
shiftAll: false, // Shift all sequences checkbox
|
shiftAll: false, // Shift all sequences checkbox
|
||||||
|
|
||||||
|
// Weather API
|
||||||
|
wxData: null,
|
||||||
|
weathercode: {
|
||||||
|
0: {
|
||||||
|
description: "Clear sky",
|
||||||
|
icon: "mdi-weather-sunny"
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
description: "Mainly clear",
|
||||||
|
icon: "mdi-weather-sunny"
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
description: "Partly cloudy",
|
||||||
|
icon: "mdi-weather-partly-cloudy"
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
description: "Overcast",
|
||||||
|
icon: "mdi-weather-cloudy"
|
||||||
|
},
|
||||||
|
45: {
|
||||||
|
description: "Fog",
|
||||||
|
icon: "mde-weather-fog"
|
||||||
|
},
|
||||||
|
48: {
|
||||||
|
description: "Depositing rime fog",
|
||||||
|
icon: "mdi-weather-fog"
|
||||||
|
},
|
||||||
|
51: {
|
||||||
|
description: "Light drizzle",
|
||||||
|
icon: "mdi-weather-partly-rainy"
|
||||||
|
},
|
||||||
|
53: {
|
||||||
|
description: "Moderate drizzle",
|
||||||
|
icon: "mdi-weather-partly-rainy"
|
||||||
|
},
|
||||||
|
55: {
|
||||||
|
description: "Dense drizzle",
|
||||||
|
icon: "mdi-weather-rainy"
|
||||||
|
},
|
||||||
|
56: {
|
||||||
|
description: "Light freezing drizzle",
|
||||||
|
icon: "mdi-weather-partly-snowy-rainy"
|
||||||
|
},
|
||||||
|
57: {
|
||||||
|
description: "Freezing drizzle",
|
||||||
|
icon: "mdi-weather-partly-snowy-rainy"
|
||||||
|
},
|
||||||
|
61: {
|
||||||
|
description: "Light rain",
|
||||||
|
icon: "mdi-weather-rainy"
|
||||||
|
},
|
||||||
|
63: {
|
||||||
|
description: "Moderate rain",
|
||||||
|
icon: "mdi-weather-rainy"
|
||||||
|
},
|
||||||
|
65: {
|
||||||
|
description: "Heavy rain",
|
||||||
|
icon: "mdi-weather-pouring"
|
||||||
|
},
|
||||||
|
66: {
|
||||||
|
description: "Light freezing rain",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
67: {
|
||||||
|
description: "Freezing rain",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
71: {
|
||||||
|
description: "Light snow",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
73: {
|
||||||
|
description: "Moderate snow",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
75: {
|
||||||
|
description: "Heavy snow",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
77: {
|
||||||
|
description: "Snow grains",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
80: {
|
||||||
|
description: "Light rain showers",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
81: {
|
||||||
|
description: "Moderate rain showers",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
82: {
|
||||||
|
description: "Violent rain showers",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
85: {
|
||||||
|
description: "Light snow showers",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
86: {
|
||||||
|
description: "Snow showers",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
95: {
|
||||||
|
description: "Thunderstorm",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
96: {
|
||||||
|
description: "Hailstorm",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
99: {
|
||||||
|
description: "Heavy hailstorm",
|
||||||
|
icon: "mdi-loading"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Context menu stuff
|
// Context menu stuff
|
||||||
contextMenuShow: false,
|
contextMenuShow: false,
|
||||||
contextMenuX: 0,
|
contextMenuX: 0,
|
||||||
@@ -630,6 +751,113 @@ export default {
|
|||||||
return text.join("\n");
|
return text.join("\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
wxInfo (line) {
|
||||||
|
|
||||||
|
function atm(key) {
|
||||||
|
return line.meta?.wx?.atmospheric?.hourly[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function mar(key) {
|
||||||
|
return line.meta?.wx?.marine?.hourly[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = atm("weathercode");
|
||||||
|
|
||||||
|
const description = this.weathercode[code]?.description ?? `WMO code ${code}`;
|
||||||
|
const wind_speed = Math.round(atm("windspeed_10m"));
|
||||||
|
const wind_direction = String(Math.round(atm("winddirection_10m"))).padStart(3, "0");
|
||||||
|
const pressure = Math.round(atm("surface_pressure"));
|
||||||
|
const temperature = Math.round(atm("temperature_2m"));
|
||||||
|
const humidity = atm("relativehumidity_2m");
|
||||||
|
const precipitation = atm("precipitation");
|
||||||
|
const precipitation_probability = atm("precipitation_probability");
|
||||||
|
const precipitation_str = precipitation_probability
|
||||||
|
? `\nPrecipitation ${precipitation} mm (prob. ${precipitation_probability}%)`
|
||||||
|
: ""
|
||||||
|
|
||||||
|
const wave_height = mar("wave_height").toFixed(1);
|
||||||
|
const wave_direction = mar("wave_direction");
|
||||||
|
const wave_period = mar("wave_period");
|
||||||
|
|
||||||
|
return `${description}\n${temperature}° C\n${pressure} hPa\nWind ${wind_speed} kt ${wind_direction}°\nRelative humidity ${humidity}%${precipitation_str}\nWaves ${wave_height} m ${wave_direction}° @ ${wave_period} s`;
|
||||||
|
},
|
||||||
|
|
||||||
|
wxIcon (line) {
|
||||||
|
const code = line.meta?.wx?.atmospheric?.hourly?.weathercode;
|
||||||
|
|
||||||
|
return this.weathercode[code]?.icon ?? "mdi-help";
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
async wxQuery (line) {
|
||||||
|
function midpoint(line) {
|
||||||
|
// WARNING Fails if across the antimeridian
|
||||||
|
const longitude = (line.geometry.coordinates[0][0] + line.geometry.coordinates[1][0])/2;
|
||||||
|
const latitude = (line.geometry.coordinates[0][1] + line.geometry.coordinates[1][1])/2;
|
||||||
|
return [ longitude, latitude ];
|
||||||
|
}
|
||||||
|
|
||||||
|
function extract (fcst) {
|
||||||
|
const τ = (line.ts0.valueOf() + line.ts1.valueOf()) / 2000;
|
||||||
|
const [idx, ε] = fcst?.hourly?.time?.reduce( (acc, cur, idx) => {
|
||||||
|
const δ = Math.abs(cur - τ);
|
||||||
|
const retval = acc
|
||||||
|
? acc[1] < δ
|
||||||
|
? acc
|
||||||
|
: [ idx, δ ]
|
||||||
|
: [ idx, δ ];
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (idx) {
|
||||||
|
const hourly = {};
|
||||||
|
for (let key in fcst?.hourly) {
|
||||||
|
fcst.hourly[key] = fcst.hourly[key][idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fcst;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetch_atmospheric (opts) {
|
||||||
|
const { longitude, latitude, dt0, dt1 } = opts;
|
||||||
|
|
||||||
|
const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m,relativehumidity_2m,precipitation_probability,precipitation,weathercode,pressure_msl,surface_pressure,windspeed_10m,winddirection_10m&daily=uv_index_max&windspeed_unit=kn&timeformat=unixtime&timezone=GMT&start_date=${dt0}&end_date=${dt1}&format=json`;
|
||||||
|
const init = {};
|
||||||
|
const res = await fetch (url, init);
|
||||||
|
if (res?.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
return extract(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetch_marine (opts) {
|
||||||
|
const { longitude, latitude, dt0, dt1 } = opts;
|
||||||
|
const url = `https://marine-api.open-meteo.com/v1/marine?latitude=${latitude}&longitude=${longitude}&hourly=wave_height,wave_direction,wave_period&timeformat=unixtime&timezone=GMT&start_date=${dt0}&end_date=${dt1}&format=json`;
|
||||||
|
|
||||||
|
const init = {};
|
||||||
|
const res = await fetch (url, init);
|
||||||
|
if (res?.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
return extract(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line) {
|
||||||
|
const [ longitude, latitude ] = midpoint(line);
|
||||||
|
const dt0 = line.ts0.toISOString().substr(0, 10);
|
||||||
|
const dt1 = line.ts1.toISOString().substr(0, 10);
|
||||||
|
|
||||||
|
return {
|
||||||
|
atmospheric: await fetch_atmospheric({longitude, latitude, dt0, dt1}),
|
||||||
|
marine: await fetch_marine({longitude, latitude, dt0, dt1})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
lagAfter (item) {
|
lagAfter (item) {
|
||||||
const pos = this.items.indexOf(item)+1;
|
const pos = this.items.indexOf(item)+1;
|
||||||
if (pos != 0) {
|
if (pos != 0) {
|
||||||
@@ -723,6 +951,9 @@ export default {
|
|||||||
for (const item of this.items) {
|
for (const item of this.items) {
|
||||||
item.ts0 = new Date(item.ts0);
|
item.ts0 = new Date(item.ts0);
|
||||||
item.ts1 = new Date(item.ts1);
|
item.ts1 = new Date(item.ts1);
|
||||||
|
this.wxQuery(item).then( (wx) => {
|
||||||
|
item.meta = {...item.meta, wx};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user