@softwarity/ttaaii-provider
v1.0.10
Published
WMO TTAAII code completion provider - UI agnostic
Downloads
1,093
Maintainers
Readme
@softwarity/ttaaii-provider
A UI-agnostic TypeScript library for WMO TTAAII code completion, validation, and decoding.
This library provides the data and logic layer only - it can be used with any UI framework (Angular, React, Vue.js, Svelte, or vanilla JavaScript).
TTAAII is a 6-character code used in meteorological bulletins following the WMO-No. 386 specification.
Installation
npm install @softwarity/ttaaii-providerBasic Usage
import { TtaaiiProvider } from '@softwarity/ttaaii-provider';
const provider = new TtaaiiProvider();
// Get completions for current input
const result = provider.complete('SA');
console.log(result.field); // 'A1'
console.log(result.items); // [{ code: 'A', label: 'Countries starting with A' }, ...]
// For country codes, A1 gives first letter options, A2 gives countries
const a2Result = provider.complete('SAF');
console.log(a2Result.field); // 'A2'
console.log(a2Result.items); // [{ code: 'A', label: 'Faroe Islands' }, { code: 'R', label: 'France' }, ...]
// Decode a TTAAII code
const decoded = provider.decode('SAFR01');
console.log(decoded.dataType?.label); // 'Surface data'
console.log(decoded.dataSubtype?.label); // 'Aviation routine reports'
// Validate a code
const validation = provider.validate('SAFR01');
console.log(validation.valid); // true
console.log(validation.complete); // trueFramework Examples
Angular
import { Component, signal, computed } from '@angular/core';
import { TtaaiiProvider, CompletionResult } from '@softwarity/ttaaii-provider';
@Component({
selector: 'app-ttaaii-input',
template: `
<input [value]="input()" (input)="onInput($event)" [matAutocomplete]="auto" />
<mat-autocomplete #auto>
@for (item of completions().items; track item.code) {
<mat-option [value]="item.code">{{ item.code }} - {{ item.label }}</mat-option>
}
</mat-autocomplete>
`
})
export class TtaaiiInputComponent {
private provider = new TtaaiiProvider();
input = signal('');
completions = computed(() => this.provider.complete(this.input()));
onInput(event: Event) {
this.input.set((event.target as HTMLInputElement).value.toUpperCase());
}
}React
import { useState, useMemo } from 'react';
import { TtaaiiProvider } from '@softwarity/ttaaii-provider';
const provider = new TtaaiiProvider();
function TtaaiiInput() {
const [input, setInput] = useState('');
const completions = useMemo(() => provider.complete(input), [input]);
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value.toUpperCase())}
maxLength={6}
/>
<ul>
{completions.items.map((item) => (
<li key={item.code} onClick={() => setInput(input + item.code)}>
<strong>{item.code}</strong> - {item.label}
</li>
))}
</ul>
{completions.isComplete && <span>✓ Complete</span>}
</div>
);
}Vue.js
<script setup lang="ts">
import { ref, computed } from 'vue';
import { TtaaiiProvider } from '@softwarity/ttaaii-provider';
const provider = new TtaaiiProvider();
const input = ref('');
const completions = computed(() => provider.complete(input.value));
function onInput(event: Event) {
input.value = (event.target as HTMLInputElement).value.toUpperCase();
}
function selectItem(code: string) {
input.value += code;
}
</script>
<template>
<input :value="input" @input="onInput" maxlength="6" />
<ul>
<li v-for="item in completions.items" :key="item.code" @click="selectItem(item.code)">
<strong>{{ item.code }}</strong> - {{ item.label }}
</li>
</ul>
<span v-if="completions.isComplete">✓ Complete</span>
</template>Grouped Completions (Countries by Continent)
The WMO country list contains 200+ entries, making it difficult to navigate in a flat autocomplete. The groupBy: 'continent' option organizes countries into geographical sections for better UX:
// Without grouping: flat list of all countries starting with 'F'
const flat = provider.complete('SAF');
// flat.items = [
// { code: 'A', label: 'South Africa' },
// { code: 'G', label: 'French Guiana' },
// { code: 'I', label: 'Fiji' },
// { code: 'R', label: 'France' },
// ... (many more)
// ]
// With grouping: countries organized by continent
const grouped = provider.complete('SAF', { groupBy: 'continent' });
// grouped.groups = [
// { key: 'EU', label: 'Europe', items: [
// { code: 'L', label: 'Finland' },
// { code: 'N', label: 'Faroe Islands' },
// { code: 'R', label: 'France' },
// ]},
// { key: 'AF', label: 'Africa', items: [
// { code: 'A', label: 'South Africa' },
// ]},
// { key: 'OC', label: 'Oceania', items: [
// { code: 'I', label: 'Fiji' },
// ]},
// { key: 'SA', label: 'South America', items: [
// { code: 'G', label: 'French Guiana' },
// { code: 'K', label: 'Falkland Islands' },
// ]},
// ]Use result.groups for grouped display (e.g., <mat-optgroup> in Angular Material) or result.items for flat list.
Internationalization (i18n)
Pre-built Localized Tables
The library provides pre-built tables in English and French, available via CDN:
// Using dynamic import (recommended for lazy loading)
// Replace @x.x.x with the version you want (e.g., @1.0.8)
const tables = await fetch('https://cdn.jsdelivr.net/npm/@softwarity/[email protected]/tables.fr.json')
.then(r => r.json());
const provider = new TtaaiiProvider(tables);Available table files:
tables.en.json- English labelstables.fr.json- French labels
CDN URLs (replace @x.x.x with the desired version):
- jsDelivr:
https://cdn.jsdelivr.net/npm/@softwarity/[email protected]/tables.{lang}.json - unpkg:
https://unpkg.com/@softwarity/[email protected]/tables.{lang}.json
Note: Without a version (e.g.,
@softwarity/ttaaii-provider/tables.fr.json), the CDN will serve the latest version. For production, always pin to a specific version to ensure compatibility.
Bundled Import (static)
For bundlers that support JSON imports:
import tables from '@softwarity/ttaaii-provider/tables.fr.json';
import { TtaaiiProvider } from '@softwarity/ttaaii-provider';
const provider = new TtaaiiProvider(tables);Custom Tables
To create a custom translation or modify labels:
- Download a table file (e.g.,
tables.en.json) from CDN - Edit the
labelfields as needed - Use it in your application
import customTables from './my-tables.json';
const provider = new TtaaiiProvider(customTables);API
TtaaiiProvider
Constructor
new TtaaiiProvider(tables?: TtaaiiTables)tables- Optional custom tables (defaults to built-in WMO tables)
Methods
complete(input: string, options?: CompletionOptions): CompletionResult
Get completion suggestions for a partial TTAAII string.
input- Current input (0-6 characters)options.groupBy- Group results (e.g., 'continent')
Returns:
position- Current position (0-5)field- Current field ('T1', 'T2', 'A1', 'A2', 'ii')items- Completion itemsgroups- Grouped items (when groupBy is specified)isComplete- True if code is complete (6 characters)context- Parsed context
validate(input: string): ValidationResult
Validate a TTAAII string.
Returns:
valid- Whether the code is validcomplete- Whether the code is completeerrors- Validation errorscontext- Parsed context
decode(input: string): DecodedTtaaii
Decode a TTAAII string into human-readable format.
Returns:
dataType- T1 decoded (code, label, priority)dataSubtype- T2 decoded (code, label, codeForm)areaOrType1- A1 decoded (for country codes, contains combined A1A2 country info)areaOrTime2- A2 decoded (for non-country tables only, e.g., reference time)level- ii decoded
Note on Country Codes: For most T1 values (A, C, E, F, L, N, S, U, V, W), A1A2 forms a 2-character ISO country code (e.g., "FR" for France). In this case, areaOrType1 contains the combined country code and name. For other T1 values (BUFR, GRID, etc.), A1 and A2 are decoded separately.
getFieldSuggestions(field, context, options): { items, groups }
Get suggestions for a specific field independently.
TTAAII Format
T1 T2 A1 A2 ii
│ │ │ │ └── Level/sequence indicator (2 digits)
│ │ └──┴───── A1A2: Country code (FR, US, etc.) OR
│ │ A1: Area/type, A2: Time reference (for BUFR, GRID)
│ └─────────── Data subtype (depends on T1)
└────────────── Data type (A-Z)A1A2 Interpretation:
- Country codes (T1 = A, C, E, F, L, N, S, U, V, W): A1A2 forms a 2-character ISO country/territory code
- BUFR/CREX (T1 = I, J, K): A1 = data type, A2 = geographical area
- GRID/GRIB (T1 = D, G, H, O, P, Q, T, Y): A1 = geographical area, A2 = reference time
Common T1 Values
| Code | Label | Description | |------|-------|-------------| | A | Analyses | Weather analyses | | F | Forecasts | Weather forecasts | | S | Surface data | Surface observations | | U | Upper-air data | Upper-air observations | | W | Warnings | Weather warnings | | T | Satellite data | Satellite imagery | | I | BUFR | Binary data (BUFR format) | | K | CREX | Character data (CREX format) |
Development
# Install dependencies
npm install
# Run tests
npm test
# Build library
npm run build
# Run demo (Angular)
npm startLicense
MIT
