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 {