229 lines
16 KiB
JavaScript
229 lines
16 KiB
JavaScript
// tides.js — motore armonico delle maree (ES5 puro, compatibile Qt 5.6 QML)
|
|
// Formato porto: [nome, paese, lat, lon, cost, flag]
|
|
// cost = [Z0, [A,phi] per M2,S2,N2,K1,O1,P1,K2] (phi in gradi)
|
|
// flag = "" (porto noto) | "near:<porto>:<gradi>" | "synth"
|
|
|
|
var W = {
|
|
M2: 28.9841042, S2: 30.0, N2: 28.4397295,
|
|
K1: 15.0410686, O1: 13.9430356, P1: 14.9589314, K2: 30.0821373
|
|
};
|
|
var WK = ["M2", "S2", "N2", "K1", "O1", "P1", "K2"];
|
|
|
|
var PORTS = [
|
|
["Venezia", "Italia", 45.43, 12.34, [0.35, [0.22, 215], [0.10, 245], [0.05, 210], [0.19, 100], [0.12, 85], [0.06, 100], [0.03, 245]]],
|
|
["Chioggia", "Italia", 45.22, 12.28, [0.34, [0.21, 212], [0.10, 242], [0.05, 207], [0.18, 98], [0.11, 83], [0.06, 98], [0.03, 242]]],
|
|
["Trieste", "Italia", 45.65, 13.77, [0.34, [0.20, 220], [0.09, 250], [0.05, 215], [0.22, 105], [0.14, 90], [0.07, 105], [0.03, 250]]],
|
|
["Ancona", "Italia", 43.62, 13.52, [0.25, [0.12, 210], [0.05, 240], [0.03, 205], [0.10, 95], [0.06, 80], [0.03, 95], [0.02, 240]]],
|
|
["Genova", "Italia", 44.41, 8.93, [0.22, [0.08, 200], [0.04, 230], [0.02, 195], [0.10, 95], [0.07, 80], [0.03, 95], [0.01, 230]]],
|
|
["Livorno", "Italia", 43.55, 10.31, [0.24, [0.10, 205], [0.05, 235], [0.03, 200], [0.11, 95], [0.07, 80], [0.03, 95], [0.01, 235]]],
|
|
["Napoli", "Italia", 40.84, 14.27, [0.22, [0.10, 210], [0.05, 240], [0.03, 205], [0.11, 90], [0.07, 75], [0.03, 90], [0.01, 240]]],
|
|
["Palermo", "Italia", 38.12, 13.36, [0.21, [0.09, 205], [0.04, 235], [0.02, 200], [0.10, 90], [0.06, 75], [0.03, 90], [0.01, 235]]],
|
|
["Cagliari", "Italia", 39.21, 9.11, [0.20, [0.07, 200], [0.03, 230], [0.02, 195], [0.09, 88], [0.06, 73], [0.03, 88], [0.01, 230]]],
|
|
["Bari", "Italia", 41.13, 16.87, [0.25, [0.14, 200], [0.06, 230], [0.04, 195], [0.10, 92], [0.06, 77], [0.03, 92], [0.02, 230]]],
|
|
["Londra (London Bridge)", "Regno Unito", 51.51, -0.09, [3.50, [2.10, 150], [0.85, 180], [0.45, 145], [0.12, 200], [0.10, 190], [0.04, 200], [0.23, 180]]],
|
|
["New York (The Battery)", "USA", 40.70, -74.01, [0.80, [0.67, 180], [0.28, 205], [0.14, 175], [0.12, 60], [0.10, 55], [0.04, 60], [0.08, 205]]],
|
|
["San Francisco (Golden Gate)", "USA", 37.81, -122.47, [1.00, [0.58, 135], [0.26, 160], [0.12, 130], [0.37, 290], [0.24, 275], [0.11, 290], [0.07, 160]]],
|
|
["Tokyo (Bay)", "Giappone", 35.68, 139.77, [1.20, [0.45, 170], [0.20, 195], [0.09, 165], [0.18, 240], [0.12, 225], [0.05, 240], [0.05, 195]]],
|
|
["Sydney", "Australia", -33.87, 151.21, [1.00, [0.50, 100], [0.23, 125], [0.10, 95], [0.10, 300], [0.07, 285], [0.03, 300], [0.06, 125]]],
|
|
["Honolulu", "USA", 21.31, -157.86, [0.35, [0.20, 115], [0.09, 140], [0.05, 110], [0.18, 320], [0.12, 305], [0.05, 320], [0.02, 140]]],
|
|
// ---- Porti minori italiani (costanti di zona: Adriatico, Ionio, Tirreno, Sicilia, Sardegna) ----
|
|
["Grado", "Italia", 45.68, 13.39, [0.30, [0.19, 218], [0.09, 248], [0.045, 213], [0.20, 103], [0.13, 88], [0.065, 103], [0.03, 248]]],
|
|
["Ravenna", "Italia", 44.49, 12.28, [0.26, [0.16, 210], [0.08, 240], [0.04, 205], [0.17, 100], [0.11, 85], [0.055, 100], [0.025, 240]]],
|
|
["Rimini", "Italia", 44.07, 12.56, [0.24, [0.15, 205], [0.07, 235], [0.035, 200], [0.16, 98], [0.10, 83], [0.05, 98], [0.022, 235]]],
|
|
["San Benedetto del Tronto", "Italia", 42.95, 13.88, [0.18, [0.10, 195], [0.045, 225], [0.025, 190], [0.09, 100], [0.055, 85], [0.028, 100], [0.015, 225]]],
|
|
["Pescara", "Italia", 42.47, 14.21, [0.17, [0.095, 190], [0.042, 220], [0.023, 185], [0.09, 102], [0.055, 87], [0.028, 102], [0.014, 220]]],
|
|
["Vasto", "Italia", 42.11, 14.71, [0.15, [0.085, 185], [0.04, 215], [0.02, 180], [0.09, 105], [0.055, 90], [0.028, 105], [0.013, 215]]],
|
|
["Termoli", "Italia", 42.00, 14.99, [0.14, [0.08, 180], [0.038, 210], [0.019, 175], [0.10, 110], [0.062, 95], [0.03, 110], [0.012, 210]]],
|
|
["Vieste", "Italia", 41.88, 16.18, [0.14, [0.075, 175], [0.036, 205], [0.018, 170], [0.11, 115], [0.068, 100], [0.034, 115], [0.012, 205]]],
|
|
["Manfredonia", "Italia", 41.62, 15.92, [0.15, [0.085, 170], [0.04, 200], [0.02, 165], [0.11, 118], [0.07, 103], [0.034, 118], [0.013, 200]]],
|
|
["Molfetta", "Italia", 41.20, 16.60, [0.15, [0.09, 168], [0.042, 198], [0.021, 163], [0.11, 120], [0.07, 105], [0.034, 120], [0.014, 198]]],
|
|
["Brindisi", "Italia", 40.65, 17.97, [0.14, [0.08, 160], [0.038, 190], [0.019, 155], [0.115, 125], [0.072, 110], [0.036, 125], [0.012, 190]]],
|
|
["Otranto", "Italia", 40.15, 18.49, [0.12, [0.07, 150], [0.033, 180], [0.017, 145], [0.12, 130], [0.075, 115], [0.037, 130], [0.011, 180]]],
|
|
["Santa Maria di Leuca", "Italia", 39.80, 18.36, [0.11, [0.065, 145], [0.031, 175], [0.016, 140], [0.12, 133], [0.075, 118], [0.037, 133], [0.010, 175]]],
|
|
["Gallipoli", "Italia", 40.06, 17.98, [0.12, [0.068, 148], [0.032, 178], [0.017, 143], [0.12, 131], [0.075, 116], [0.037, 131], [0.010, 178]]],
|
|
["Porto Cesareo", "Italia", 40.27, 17.90, [0.12, [0.07, 152], [0.033, 182], [0.017, 147], [0.12, 129], [0.075, 114], [0.037, 129], [0.011, 182]]],
|
|
["Taranto", "Italia", 40.47, 17.23, [0.13, [0.075, 155], [0.035, 185], [0.018, 150], [0.12, 127], [0.075, 112], [0.037, 127], [0.011, 185]]],
|
|
["Crotone", "Italia", 39.08, 17.13, [0.10, [0.055, 140], [0.026, 170], [0.014, 135], [0.10, 140], [0.062, 125], [0.031, 140], [0.009, 170]]],
|
|
["Vibo Valentia Marina", "Italia", 38.71, 16.11, [0.15, [0.075, 195], [0.037, 225], [0.022, 190], [0.10, 97], [0.065, 82], [0.031, 97], [0.010, 225]]],
|
|
["Maratea", "Italia", 39.99, 15.72, [0.16, [0.08, 200], [0.04, 230], [0.024, 195], [0.10, 95], [0.065, 80], [0.031, 95], [0.011, 230]]],
|
|
["Messina", "Italia", 38.19, 15.56, [0.18, [0.12, 185], [0.06, 215], [0.03, 180], [0.09, 100], [0.06, 85], [0.028, 100], [0.018, 215]]],
|
|
["Reggio Calabria", "Italia", 38.11, 15.65, [0.18, [0.12, 188], [0.06, 218], [0.03, 183], [0.09, 98], [0.06, 83], [0.028, 98], [0.018, 218]]],
|
|
["Milazzo", "Italia", 38.23, 15.24, [0.16, [0.08, 198], [0.038, 228], [0.018, 193], [0.10, 92], [0.062, 77], [0.03, 92], [0.011, 228]]],
|
|
["Trapani", "Italia", 38.02, 12.54, [0.17, [0.085, 202], [0.04, 232], [0.019, 197], [0.095, 90], [0.06, 75], [0.029, 90], [0.011, 232]]],
|
|
["Marsala", "Italia", 37.80, 12.44, [0.16, [0.08, 200], [0.038, 230], [0.018, 195], [0.09, 91], [0.058, 76], [0.028, 91], [0.010, 230]]],
|
|
["Porto Empedocle", "Italia", 37.29, 13.53, [0.15, [0.075, 198], [0.036, 228], [0.017, 193], [0.09, 93], [0.057, 78], [0.028, 93], [0.010, 228]]],
|
|
["Siracusa", "Italia", 37.07, 15.29, [0.12, [0.06, 155], [0.028, 185], [0.015, 150], [0.10, 135], [0.062, 120], [0.031, 135], [0.009, 185]]],
|
|
["Olbia", "Italia", 40.92, 9.50, [0.14, [0.06, 192], [0.028, 222], [0.017, 187], [0.09, 90], [0.058, 75], [0.028, 90], [0.009, 222]]],
|
|
["Arbatax", "Italia", 39.93, 9.70, [0.14, [0.065, 195], [0.03, 225], [0.018, 190], [0.09, 89], [0.058, 74], [0.028, 89], [0.009, 225]]],
|
|
["Porto Torres", "Italia", 40.84, 8.40, [0.16, [0.085, 205], [0.04, 235], [0.023, 200], [0.085, 86], [0.055, 71], [0.026, 86], [0.011, 235]]],
|
|
["Alghero", "Italia", 40.56, 8.32, [0.16, [0.09, 208], [0.042, 238], [0.024, 203], [0.085, 85], [0.055, 70], [0.026, 85], [0.011, 238]]],
|
|
["Oristano", "Italia", 39.90, 8.54, [0.15, [0.085, 206], [0.04, 236], [0.023, 201], [0.085, 86], [0.055, 71], [0.026, 86], [0.011, 236]]],
|
|
["Civitavecchia", "Italia", 42.09, 11.79, [0.20, [0.09, 212], [0.045, 242], [0.027, 207], [0.10, 90], [0.065, 75], [0.03, 90], [0.012, 242]]],
|
|
["Anzio", "Italia", 41.45, 12.63, [0.18, [0.085, 208], [0.042, 238], [0.025, 203], [0.10, 92], [0.065, 77], [0.03, 92], [0.011, 238]]],
|
|
["Gaeta", "Italia", 41.22, 13.57, [0.18, [0.088, 206], [0.043, 236], [0.026, 201], [0.105, 93], [0.068, 78], [0.031, 93], [0.012, 236]]],
|
|
["La Spezia", "Italia", 44.10, 9.83, [0.20, [0.075, 198], [0.038, 228], [0.019, 193], [0.10, 96], [0.068, 81], [0.03, 96], [0.010, 228]]],
|
|
["Imperia", "Italia", 43.89, 8.03, [0.18, [0.07, 195], [0.035, 225], [0.018, 190], [0.095, 97], [0.065, 82], [0.029, 97], [0.009, 225]]],
|
|
["Sanremo", "Italia", 43.82, 7.78, [0.18, [0.072, 193], [0.036, 223], [0.018, 188], [0.095, 98], [0.065, 83], [0.029, 98], [0.010, 223]]],
|
|
["Portoferraio", "Italia", 42.81, 10.33, [0.18, [0.085, 203], [0.042, 233], [0.022, 198], [0.10, 96], [0.065, 81], [0.03, 96], [0.011, 233]]]
|
|
];
|
|
|
|
// Citta' con coordinate per ricerca offline (fallback senza rete)
|
|
var CITIES = [
|
|
["Milano", "Italia", 45.46, 9.19], ["Roma", "Italia", 41.90, 12.50], ["Torino", "Italia", 45.07, 7.69],
|
|
["Bologna", "Italia", 44.49, 11.34], ["Firenze", "Italia", 43.77, 11.26], ["Verona", "Italia", 45.44, 10.99],
|
|
["Padova", "Italia", 45.41, 11.88], ["Ravenna", "Italia", 44.42, 12.20], ["Pescara", "Italia", 42.46, 14.21],
|
|
["Reggio Calabria", "Italia", 38.11, 15.65], ["Catania", "Italia", 37.51, 15.09], ["Trapani", "Italia", 38.02, 12.54],
|
|
["Olbia", "Italia", 40.92, 9.50], ["Alghero", "Italia", 40.56, 8.32], ["Imperia", "Italia", 43.89, 8.03],
|
|
["La Spezia", "Italia", 44.10, 9.82], ["Viareggio", "Italia", 43.87, 10.25], ["Civitavecchia", "Italia", 42.09, 11.79],
|
|
["Salerno", "Italia", 40.68, 14.77], ["Taranto", "Italia", 40.46, 17.24], ["Brindisi", "Italia", 40.64, 17.95],
|
|
["Otranto", "Italia", 40.15, 18.49], ["Marsala", "Italia", 37.80, 12.44], ["Porto Torres", "Italia", 40.84, 8.40],
|
|
["Amsterdam", "Paesi Bassi", 52.37, 4.90], ["Rotterdam", "Paesi Bassi", 51.92, 4.48],
|
|
["Barcellona", "Spagna", 41.39, 2.17], ["Marsiglia", "Francia", 43.30, 5.37], ["Atene", "Grecia", 37.98, 23.73],
|
|
["Istanbul", "Turchia", 41.01, 28.98], ["Lisbona", "Portogallo", 38.72, -9.14], ["Dublino", "Irlanda", 53.35, -6.26],
|
|
["Edimburgo", "Regno Unito", 55.95, -3.19], ["Amburgo", "Germania", 53.55, 9.99], ["Copenaghen", "Danimarca", 55.68, 12.57],
|
|
["Oslo", "Norvegia", 59.91, 10.75], ["Stoccolma", "Svezia", 59.33, 18.07], ["Helsinki", "Finlandia", 60.17, 24.94],
|
|
["Miami", "USA", 25.76, -80.19], ["Los Angeles", "USA", 34.05, -118.24], ["Seattle", "USA", 47.61, -122.33],
|
|
["Vancouver", "Canada", 49.28, -123.12], ["Città del Messico", "Messico", 19.43, -99.13],
|
|
["Lima", "Perù", -12.05, -77.04], ["Buenos Aires", "Argentina", -34.60, -58.38],
|
|
["Città del Capo", "Sudafrica", -33.92, 18.42], ["Mumbai", "India", 19.08, 72.88],
|
|
["Singapore", "Singapore", 1.35, 103.82], ["Hong Kong", "Cina", 22.32, 114.17], ["Shanghai", "Cina", 31.23, 121.47],
|
|
["Seoul", "Corea del Sud", 37.57, 126.98], ["Manila", "Filippine", 14.60, 120.98], ["Jakarta", "Indonesia", -6.21, 106.85],
|
|
["Auckland", "Nuova Zelanda", -36.85, 174.76], ["Wellington", "Nuova Zelanda", -41.29, 174.78],
|
|
["Rio de Janeiro", "Brasile", -22.91, -43.17], ["Il Cairo", "Egitto", 30.04, 31.24], ["Dubai", "EAU", 25.20, 55.27],
|
|
["Gibilterra", "Regno Unito", 36.14, -5.35], ["Malta (La Valletta)", "Malta", 35.90, 14.51]
|
|
];
|
|
|
|
var EPOCH = Date.UTC(2000, 0, 1) / 3600000; // ore dal 1970 all'epoca armonica
|
|
|
|
// Altezza della marea (metri) in un dato istante
|
|
function tide(port, date) {
|
|
var t = date.getTime() / 3600000 - EPOCH;
|
|
var C = port[4];
|
|
var h = C[0];
|
|
for (var i = 0; i < 7; i++) {
|
|
var a = C[i + 1][0], p = C[i + 1][1];
|
|
h += a * Math.cos((W[WK[i]] * t + p) * Math.PI / 180);
|
|
}
|
|
return h;
|
|
}
|
|
|
|
// Estremi (alta/bassa) da adesso nei prossimi giorni
|
|
function extrema(port, now, days) {
|
|
var step = 2 * 60 * 1000;
|
|
var out = [];
|
|
var end = now.getTime() + days * 86400000;
|
|
var prev = tide(port, new Date(now.getTime() - step));
|
|
var cur = tide(port, now);
|
|
var rising = cur > prev;
|
|
for (var t = now.getTime() + step; t <= end; t += step) {
|
|
var nx = tide(port, new Date(t));
|
|
if (rising && nx < cur) {
|
|
var bt = t - step, hh = cur;
|
|
for (var dt = bt - 10 * 60000; dt <= t + 10 * 60000; dt += 30000) {
|
|
var hh2 = tide(port, new Date(dt));
|
|
if (hh2 > hh) { hh = hh2; bt = dt; }
|
|
}
|
|
out.push({ t: bt, h: hh, kind: "high" });
|
|
} else if (!rising && nx > cur) {
|
|
var bt2 = t - step, hh3 = cur;
|
|
for (var dt2 = bt2 - 10 * 60000; dt2 <= t + 10 * 60000; dt2 += 30000) {
|
|
var hh4 = tide(port, new Date(dt2));
|
|
if (hh4 < hh3) { hh3 = hh4; bt2 = dt2; }
|
|
}
|
|
out.push({ t: bt2, h: hh3, kind: "low" });
|
|
}
|
|
prev = cur; cur = nx; rising = nx > prev;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// Distanza approssimata in km tra un porto e un punto
|
|
function portDist(p, lat, lon) {
|
|
var dlat = (p[2] - lat) * 111;
|
|
var dlon = (p[3] - lon) * 111 * Math.cos(lat * Math.PI / 180);
|
|
return Math.sqrt(dlat * dlat + dlon * dlon);
|
|
}
|
|
|
|
// Porto noto piu' vicino a lat/lon
|
|
function nearestPort(lat, lon) {
|
|
var best = null, bd = 1e9;
|
|
for (var i = 0; i < PORTS.length; i++) {
|
|
var d = portDist(PORTS[i], lat, lon);
|
|
if (d < bd) { bd = d; best = PORTS[i]; }
|
|
}
|
|
return { port: best, km: bd };
|
|
}
|
|
|
|
// Costanti stimate da lat/lon (modello sintetico, per il mockup)
|
|
function synthConst(lat, lon) {
|
|
var a = Math.abs(lat);
|
|
var g = Math.exp(-0.5 * Math.pow((a - 48) / 18, 2));
|
|
var M2 = 0.08 + 1.6 * g;
|
|
var S2 = 0.38 * M2, N2 = 0.19 * M2, K2 = 0.27 * S2;
|
|
var dg = Math.exp(-0.5 * Math.pow(a / 25, 2));
|
|
var K1 = 0.10 + 0.22 * dg, O1 = 0.70 * K1, P1 = 0.33 * K1;
|
|
var pM = ((lon * 2.8 + 100) % 360 + 360) % 360;
|
|
return [0.3, [M2, pM], [S2, (pM + 30) % 360], [N2, (pM - 25 + 360) % 360], [K1, 80], [O1, 65], [P1, 80], [K2, (pM + 30) % 360]];
|
|
}
|
|
|
|
// Citta' nel dataset locale (ricerca offline)
|
|
function findCityLocal(q) {
|
|
q = q.toLowerCase();
|
|
for (var i = 0; i < CITIES.length; i++) {
|
|
if (CITIES[i][0].toLowerCase().indexOf(q) !== -1) return CITIES[i];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Fase lunare: {icon, idx (0-7), age (giorni)}
|
|
function moonPhase(date) {
|
|
var syn = 29.53058867;
|
|
var known = Date.UTC(2000, 0, 6, 18, 14) / 86400000;
|
|
var age = ((date.getTime() / 86400000 - known) % syn + syn) % syn;
|
|
var icons = ["\uD83C\uDF11", "\uD83C\uDF12", "\uD83C\uDF13", "\uD83C\uDF14", "\uD83C\uDF15", "\uD83C\uDF16", "\uD83C\uDF17", "\uD83C\uDF18"];
|
|
return { icon: icons[Math.round(age / syn * 8) % 8], idx: Math.round(age / syn * 8) % 8, age: age };
|
|
}
|
|
|
|
// Alba/tramonto approssimati (ora solare, ore decimali)
|
|
function sunTimes(lat, date) {
|
|
var N = Math.floor((date.getTime() - Date.UTC(date.getFullYear(), 0, 1)) / 86400000);
|
|
var dec = 23.45 * Math.sin((360 / 365) * (284 + N) * Math.PI / 180);
|
|
var latR = lat * Math.PI / 180, decR = dec * Math.PI / 180;
|
|
var cosH = (-Math.sin(latR) * Math.sin(decR)) / (Math.cos(latR) * Math.cos(decR));
|
|
cosH = Math.max(-1, Math.min(1, cosH));
|
|
var H = Math.acos(cosH) * 180 / Math.PI / 15;
|
|
return { rise: 12 - H, set: 12 + H };
|
|
}
|
|
|
|
// Formato altezza secondo l'unita' scelta
|
|
function fmtH(h, unit) {
|
|
return unit === "ft" ? (h * 3.28084).toFixed(1) + " ft" : h.toFixed(2) + " m";
|
|
}
|
|
|
|
// Formato ora HH:MM
|
|
function fmtHM(t) {
|
|
var d = new Date(t);
|
|
var hh = d.getHours(), mm = d.getMinutes();
|
|
return (hh < 10 ? "0" : "") + hh + ":" + (mm < 10 ? "0" : "") + mm;
|
|
}
|
|
|
|
// Scala Douglas (stato del mare) dall'altezza d'onda significativa in metri
|
|
// 0 Calmo, 1 Quasi calmo, 2 Poco mosso, 3 Mosso, 4 Molto mosso, 5 Agitato,
|
|
// 6 Molto agitato, 7 Grosso, 8 Molto grosso, 9 Tempestoso
|
|
function douglas(h) {
|
|
if (h <= 0) return 0;
|
|
var soglie = [0.1, 0.5, 1.25, 2.5, 4, 6, 9, 14];
|
|
var g = 1;
|
|
for (var i = 0; i < soglie.length; i++) {
|
|
if (h >= soglie[i]) g++;
|
|
}
|
|
return g;
|
|
}
|
|
|
|
// Formato data breve
|
|
function fmtDate(t, dn) {
|
|
var d = new Date(t);
|
|
var days = dn || ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"];
|
|
return days[d.getDay()] + " " + (d.getDate() < 10 ? "0" : "") + d.getDate() + "/" + ((d.getMonth() + 1) < 10 ? "0" : "") + (d.getMonth() + 1);
|
|
}
|