diff --git a/metar.html b/metar.html
index 8a4ee16..9d1b7d6 100644
--- a/metar.html
+++ b/metar.html
@@ -260,7 +260,7 @@
'CB': 'Cumulonimbus'
};
const specialRemarks = {
- 'FROIN': 'Frost on indicator',
+ 'FROIN': 'Frontal passage in vicinity',
'CONTRAILS': 'Contrails observed',
'VIRGA': 'Virga (precipitation not reaching the ground)',
'HALO': 'Halo phenomenon observed'
@@ -282,6 +282,11 @@
const location = parts[i+1];
decoded.push(` - VIA ${location}: relayed via ${location}`);
i++; // Consume location
+ } else if (part === 'VIS' && i + 2 < parts.length) {
+ const direction = parts[i+1];
+ const distance = parts[i+2];
+ decoded.push(` - VIS ${direction} ${distance}: Sector visibility to the ${direction} of ${distance} statute miles`);
+ i += 2;
} else if (part === 'VIRGA' && i + 1 < parts.length && parts[i+1] === 'ALQDS') {
decoded.push(` - VIRGA ALQDS: Virga in all quadrants`);
i++; // Consume ALQDS
@@ -289,6 +294,16 @@
const direction = directions[parts[i+1]];
decoded.push(` - VIRGA ${parts[i+1]}: Virga to the ${direction.toLowerCase()}`);
i++; // Consume direction part
+ } else if (part === 'TCU' && i + 1 < parts.length && parts[i+1] === 'ASOCTD') {
+ let remark = ' - TCU ASOCTD: Towering cumulus associated';
+ let consumed = 1;
+ if (i + 2 < parts.length && directions[parts[i+2]]) {
+ const direction = directions[parts[i+2]];
+ remark += ` to the ${direction.toLowerCase()}`;
+ consumed = 2;
+ }
+ decoded.push(remark);
+ i += consumed;
} else if (part === 'TCU' && i + 1 < parts.length && parts[i+1] === 'ALQDS') {
decoded.push(` - TCU ALQDS: Towering cumulus in all quadrants`);
i++; // Consume ALQDS
@@ -319,6 +334,10 @@
if (i + 3 < parts.length && parts[i+2] === '/' && parts[i+3] === 'HALO') {
remark = ` - ${part} ASOCTD / HALO: ${cloudName} associated with Halo phenomenon`;
consumed = 3;
+ } else if (i + 2 < parts.length && directions[parts[i+2]]) {
+ const direction = directions[parts[i+2]];
+ remark += ` to the ${direction.toLowerCase()}`;
+ consumed = 2;
}
decoded.push(remark);
i += consumed;
@@ -326,6 +345,16 @@
const cloudName = cloudTypes[part];
decoded.push(` - ${part} ALQDS: ${cloudName} in all quadrants`);
i++; // Consume ALQDS
+ } else if (cloudTypes[part] && i + 1 < parts.length && directions[parts[i+1]]) {
+ const cloudName = cloudTypes[part];
+ let remark = ` - ${part} ${parts[i+1]}: ${cloudName} to the ${directions[parts[i+1]].toLowerCase()}`;
+ let consumed = 1;
+ if (i + 2 < parts.length && parts[i+2] === 'MOV' && i + 3 < parts.length && directions[parts[i+3]]) {
+ remark += `, moving ${directions[parts[i+3]].toLowerCase()}`;
+ consumed = 3;
+ }
+ decoded.push(remark);
+ i += consumed;
} else if (specialRemarks[part]) {
decoded.push(` - ${part}: ${specialRemarks[part]}`);
} else if (cloudTypes[part] && i + 1 < parts.length && parts[i + 1] === 'TR') {
@@ -335,7 +364,28 @@
} else if (weatherRegex.test(part)) {
decoded.push(` - ${decodeWeather(part, metarString)}`);
} else if (part.match(/^(AC|CI|CC)/)) {
- decoded.push(` - ${part}: Cloud types and coverage details`);
+ // This is a cloud-like remark, e.g. ACC for Altocumulus Castellanus
+ // It can have modifiers like other clouds.
+ const cloudName = cloudTypes[part] || part; // part will be ACC
+ let remark = ` - ${cloudName}: Cloud types and coverage details`;
+ let consumed = 0;
+
+ if (i + 1 < parts.length && parts[i+1] === 'TR') {
+ remark = ` - ${cloudName} TR: ${cloudName} clouds are translucent (thin)`;
+ consumed = 1;
+ } else if (i + 1 < parts.length && parts[i+1] === 'ASOCTD') {
+ remark = ` - ${cloudName} ASOCTD: ${cloudName} associated`;
+ consumed = 1;
+ if (i + 2 < parts.length && directions[parts[i+2]]) {
+ remark += ` to the ${directions[parts[i+2]].toLowerCase()}`;
+ consumed = 2;
+ }
+ } else if (i + 1 < parts.length && directions[parts[i+1]]) {
+ remark = ` - ${cloudName} ${parts[i+1]}: ${cloudName} to the ${directions[parts[i+1]].toLowerCase()}`;
+ consumed = 1;
+ }
+ decoded.push(remark);
+ i += consumed;
} else if (part === '/') {
// separator, ignore
} else {