diff --git a/README.md b/README.md index 651c416..f72fe20 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ import { GRIB } from 'vgrib2' const gribFile = fs.readFileSync('./path/to/file.grib2') const grib = GRIB.parse(gribFile) +// Lookup grid data point from the first packet +const point = GRIB.lookupDataPoint(grib[0], -39.75, 146.15) // No Table Lookup (useful for adding new tables) const gribNoLookup = GRIB.parseNoLookup(gribFile) ``` diff --git a/package-lock.json b/package-lock.json index d0aa854..49cee52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,10 @@ "version": "0.1.13", "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", "buffer": "^6.0.3", - "ieee754": "^1.2.1" + "jpeg2000": "^1.1.0" }, "devDependencies": { - "@types/buffer-from": "^1.1.0", "tsdx": "^0.14.1", "tslib": "^2.3.0", "typescript": "^3.9.10" @@ -2327,15 +2325,6 @@ "@babel/types": "^7.3.0" } }, - "node_modules/@types/buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-BLFpLBcN+RPKUsFxqRkMiwqTOOdi+TrKr5OpLJ9qCnUdSxS6S80+QRX/mIhfR66u0Ykc4QTkReaejOM2ILh+9Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -7372,6 +7361,11 @@ "node": ">= 8.3" } }, + "node_modules/jpeg2000": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jpeg2000/-/jpeg2000-1.1.0.tgz", + "integrity": "sha512-3zV+uTrwA1Z6erXENIWUnS140JjL1iRWbGiLfUi7rETYDV9NWmMl+gx5DHz+58pT8LiGXuXwiM4RpWGyotomxA==" + }, "node_modules/jpjs": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/jpjs/-/jpjs-1.2.1.tgz", @@ -7677,7 +7671,7 @@ "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, "node_modules/lodash.merge": { @@ -10891,43 +10885,6 @@ "punycode": "^2.1.0" } }, - "node_modules/ts-jest": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.5.1.tgz", - "integrity": "sha512-kHEUlZMK8fn8vkxDjwbHlxXRB9dHYpyzqKIGDNxbzs+Rz+ssNDSDNusEK8Fk/sDd4xE6iKoQLfFkFVaskmTJyw==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "micromatch": "4.x", - "mkdirp": "0.x", - "semver": "6.x", - "yargs-parser": "18.x" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": ">= 8" - }, - "peerDependencies": { - "jest": ">=25 <26", - "typescript": ">=3.4 <4.0" - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/tsconfig-paths": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", @@ -11018,6 +10975,43 @@ "node": ">=10" } }, + "node_modules/tsdx/node_modules/ts-jest": { + "version": "25.5.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.5.1.tgz", + "integrity": "sha512-kHEUlZMK8fn8vkxDjwbHlxXRB9dHYpyzqKIGDNxbzs+Rz+ssNDSDNusEK8Fk/sDd4xE6iKoQLfFkFVaskmTJyw==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "micromatch": "4.x", + "mkdirp": "0.x", + "semver": "6.x", + "yargs-parser": "18.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": ">= 8" + }, + "peerDependencies": { + "jest": ">=25 <26", + "typescript": ">=3.4 <4.0" + } + }, + "node_modules/tsdx/node_modules/ts-jest/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/tsdx/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -13399,15 +13393,6 @@ "@babel/types": "^7.3.0" } }, - "@types/buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-BLFpLBcN+RPKUsFxqRkMiwqTOOdi+TrKr5OpLJ9qCnUdSxS6S80+QRX/mIhfR66u0Ykc4QTkReaejOM2ILh+9Q==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -17295,6 +17280,11 @@ "supports-color": "^7.0.0" } }, + "jpeg2000": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jpeg2000/-/jpeg2000-1.1.0.tgz", + "integrity": "sha512-3zV+uTrwA1Z6erXENIWUnS140JjL1iRWbGiLfUi7rETYDV9NWmMl+gx5DHz+58pT8LiGXuXwiM4RpWGyotomxA==" + }, "jpjs": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/jpjs/-/jpjs-1.2.1.tgz", @@ -17544,7 +17534,7 @@ "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, "lodash.merge": { @@ -20120,32 +20110,6 @@ "punycode": "^2.1.0" } }, - "ts-jest": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.5.1.tgz", - "integrity": "sha512-kHEUlZMK8fn8vkxDjwbHlxXRB9dHYpyzqKIGDNxbzs+Rz+ssNDSDNusEK8Fk/sDd4xE6iKoQLfFkFVaskmTJyw==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "micromatch": "4.x", - "mkdirp": "0.x", - "semver": "6.x", - "yargs-parser": "18.x" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "tsconfig-paths": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", @@ -20229,6 +20193,32 @@ "typescript": "^3.7.3" }, "dependencies": { + "ts-jest": { + "version": "25.5.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.5.1.tgz", + "integrity": "sha512-kHEUlZMK8fn8vkxDjwbHlxXRB9dHYpyzqKIGDNxbzs+Rz+ssNDSDNusEK8Fk/sDd4xE6iKoQLfFkFVaskmTJyw==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "micromatch": "4.x", + "mkdirp": "0.x", + "semver": "6.x", + "yargs-parser": "18.x" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", diff --git a/package.json b/package.json index 5d4003e..d4c7ee6 100644 --- a/package.json +++ b/package.json @@ -27,16 +27,22 @@ ], "name": "vgrib2", "author": "Alessandro Vecchi", + "contributors": [ + "Dmitry Shirokov @runk" + ], "module": "dist/vgrib2.esm.js", "devDependencies": { - "@types/buffer-from": "^1.1.0", "tsdx": "^0.14.1", "tslib": "^2.3.0", "typescript": "^3.9.10" }, "dependencies": { - "base64-js": "^1.3.1", "buffer": "^6.0.3", - "ieee754": "^1.2.1" + "jpeg2000": "^1.1.0" + }, + "homepage": "https://github.com/veech/vgrib2", + "repository": { + "type": "git", + "url": "https://github.com/veech/vgrib2.git" } } diff --git a/src/data/index.ts b/src/data/index.ts index 1badd99..969ab18 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -2,7 +2,9 @@ import { Buffer } from 'buffer/' import { DataRepresentationSectionValues } from '../sections/section-5' -import { simpleUnpacking } from './simple-unpacking' +import { simpleUnpacking } from './simple' +import { jpeg2000Unpacking } from './jpeg2000' +import { BitMapSectionValues } from '../sections' /** * Converts data Buffer according to data representation section @@ -10,14 +12,15 @@ import { simpleUnpacking } from './simple-unpacking' * @param data Data to be converted * @returns Converted data */ -export const convertData = (drs: DataRepresentationSectionValues, data: Buffer) => { +export const convertData = (drs: DataRepresentationSectionValues, bms:BitMapSectionValues, data: Buffer) => { const { dataRepresentationTemplate } = drs.contents switch (dataRepresentationTemplate) { case 0: return simpleUnpacking(drs, data) - + case 40: + return jpeg2000Unpacking(drs, bms, data) default: throw new Error(`Template 7.${dataRepresentationTemplate} not defined`) } -} +} \ No newline at end of file diff --git a/src/data/jpeg2000.ts b/src/data/jpeg2000.ts new file mode 100644 index 0000000..55ced35 --- /dev/null +++ b/src/data/jpeg2000.ts @@ -0,0 +1,57 @@ +import { Buffer } from 'buffer/' +import { DataRepresentationSectionValues } from '../sections/section-5' +import { JpxImage } from 'jpeg2000' +import { BitMapSectionValues } from '../sections' + +/** + * Data Template 7.0 + * + * [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp7-40.shtml) + */ +export const jpeg2000Unpacking = (drs: DataRepresentationSectionValues, bms: BitMapSectionValues, data: Buffer) => { + const jpx = new JpxImage() + jpx.parse(data) + + if (jpx.componentsCount !== 1) throw new Error('JPEG Decoder: Only single component is supported') + if (jpx.tiles.length !== 1) throw new Error('JPEG Decoder: Only single tile is supported') + if (jpx.tiles[0].height !== 1) throw new Error('JPEG Decoder: Only single row (1xN) is supported') + + const { dataRepresentation } = drs.contents + + const D = dataRepresentation.decimalScaleFactor + const R = dataRepresentation.referenceValue + const E = dataRepresentation.binaryScaleFactor + + const DD = Math.pow(10, D) + const EE = Math.pow(2, E) + + const result: (number | null)[] = [] + + // A bit map applies to this product + // [See more](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table6-0.shtml) + if (bms.contents.bitMapIndicator === 0) { + if (!bms.contents.bitMap) { + throw new Error('Bit map is not defined') + } + + let k = 0; + for (const byte of bms.contents.bitMap) { + // Apply bit map to the data. + // Length of data values is often smaller than the bit map itself. Bitmap is used to + // indicate which data values are present, 1 bit meaning is present, 0 bit meaning is missing. + // [Read more](https://confluence.ecmwf.int/display/UDOC/What+is+the+GRIB+bitmap+-+ecCodes+GRIB+FAQ) + for (let i = 0; i < 8; i++) { + if (byte & (1 << i)) { + result.push((R + jpx.tiles[0].items[k++] * EE) / DD) + } else { + result.push(null) + } + } + } + } else { + // Do not use `.map` on Uint8Array, as it clamps the values to 0-255 + for (const byte of jpx.tiles[0].items) result.push((R + byte * EE) / DD) + } + + return result +} diff --git a/src/data/simple-unpacking.ts b/src/data/simple.ts similarity index 100% rename from src/data/simple-unpacking.ts rename to src/data/simple.ts diff --git a/src/index.ts b/src/index.ts index fd8ddc6..27666ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,6 +41,26 @@ const parseNoLookup = (data: ArrayBuffer): Array => { return packets } -export const GRIB = { parse, parseNoLookup } +/** + * Lookup data point from GRIB packet based on lat/lon coordinates. + * + * @param packet GRIB packet + * @param lat Latitude + * @param lon Longitude + */ +const lookupDataPoint = (packet: GRIBPacket, lat: number, lon: number): number | null => { + const { la1, lo1, la2, lo2, dx, dy, ny } = packet.gridDefinition + if (lat < la1 || lat > la2 || lon < lo1 || lon > lo2) return null + + // lon (W-E) is x + // lat (N-S) is y + const x = Math.abs(Math.round((lo1 - lon) / dx)) + const y = Math.abs(Math.round((la1 - lat) / dy)) + + const idx = y * ny + x + return packet.data[idx] +} + +export const GRIB = { parse, parseNoLookup, lookupDataPoint } export * from './types/grib' diff --git a/src/section.ts b/src/section.ts index c8d5163..1f1c8cd 100644 --- a/src/section.ts +++ b/src/section.ts @@ -84,7 +84,7 @@ const parseSection = (section: Buffer | null) => { export const lookupSections = (parsedSections: SectionValues) => { const [ins, ids, lus, gds, pds, drs, bms, ds, es] = parsedSections - return [lookupSection0(ins), lookupSection1(ids), lus, lookupSection3(gds), lookupSection4(pds, ins, ids), lookupSection5(drs), bms, lookupSection7(ds, drs), es] as Sections + return [lookupSection0(ins), lookupSection1(ids), lus, lookupSection3(gds), lookupSection4(pds, ins, ids), lookupSection5(drs), bms, lookupSection7(ds, drs, bms), es] as Sections } /** diff --git a/src/sections/section-2.ts b/src/sections/section-2.ts index 196c6b2..b45a815 100644 --- a/src/sections/section-2.ts +++ b/src/sections/section-2.ts @@ -8,6 +8,15 @@ export type LocalUseSection = ReturnType * * [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect2.shtml) */ -export const parseSection2 = (_section: Buffer) => { - throw new Error('Section 2 is not supported') +export const parseSection2 = (section: Buffer) => { + return { + /** Number of GRIB section */ + sectionNumber: section.readUInt8(4), + /** Name of Grib section */ + sectionName: 'Local Use Section', + /** Length of GRIB section */ + length: section.readUInt32BE(0), + /** Section 2 Contents */ + contents: { data: section.slice(5) } + } } diff --git a/src/sections/section-3.ts b/src/sections/section-3.ts index 1cfd47f..126734b 100644 --- a/src/sections/section-3.ts +++ b/src/sections/section-3.ts @@ -24,8 +24,14 @@ export const parseSection3 = (section: Buffer) => { length: section.readUInt32BE(0), /** Section 3 Contents */ contents: { + // Source of grid definition + definitionSource: section.readUInt8(5), /** Number of data points */ numberOfPoints: section.readUInt32BE(6), + // Number of octets for optional list of numbers defining number of points + numberOfOctets: section.readUInt8(10), + // Interpetation of list of numbers defining number of points [Table 3.11](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-11.shtml) + interpretation: section.readUInt8(11), /** Grid definition template number [Table 3.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-1.shtml) */ gridDefinitionTemplate, /** Grid definition values */ diff --git a/src/sections/section-5.ts b/src/sections/section-5.ts index 151c94b..b1fa30f 100644 --- a/src/sections/section-5.ts +++ b/src/sections/section-5.ts @@ -13,7 +13,8 @@ export type DataRepresentationSection = ReturnType */ export const parseSection5 = (section: Buffer) => { const dataRepresentationTemplate = section.readUInt16BE(9) - const dataRepresentation = getTemplate5(dataRepresentationTemplate)(section) + const template = getTemplate5(dataRepresentationTemplate) + const dataRepresentation = template(section) return { /** Number of GRIB section */ @@ -49,7 +50,7 @@ export const lookupSection5 = (drs: DataRepresentationSectionValues) => { /** Data representation template */ dataRepresentationTemplate: lookupTable50(dataRepresentationTemplate), /** Data representation */ - dataRepresentation: lookupTemplate5(dataRepresentationTemplate)(dataRepresentation) + dataRepresentation: lookupTemplate5(dataRepresentationTemplate)(dataRepresentation as any) } } } diff --git a/src/sections/section-6.ts b/src/sections/section-6.ts index db13a2a..335db76 100644 --- a/src/sections/section-6.ts +++ b/src/sections/section-6.ts @@ -1,18 +1,20 @@ import { Buffer } from 'buffer/' - export type BitMapSectionValues = ReturnType export type BitMapSection = ReturnType /** - * Bit-Map Section + * Bit-Map Section * - * [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect6.shtml) + * Consult with [this page](https://confluence.ecmwf.int/display/UDOC/What+is+the+GRIB+bitmap+-+ecCodes+GRIB+FAQ) to understand their purpose. + * [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_sect6.shtml). */ export const parseSection6 = (section: Buffer) => { const bitMapIndicator = section.readUInt8(5) - if (bitMapIndicator !== 255) throw new Error('BitMap Indicator not supported') - + if (![0, 255].includes(bitMapIndicator)) { + throw new Error('BitMap Indicator not supported: ' + String(bitMapIndicator)) + } + return { /** Number of GRIB section */ sectionNumber: section.readUInt8(4), @@ -23,7 +25,9 @@ export const parseSection6 = (section: Buffer) => { /** Section 6 Contents */ contents: { /** Bit-map indicator (See [Table 6.0](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table6-0.shtml)) */ - bitMapIndicator + bitMapIndicator, + // Bit-map + bitMap: bitMapIndicator === 0 ? section.slice(6) : null } } } diff --git a/src/sections/section-7.ts b/src/sections/section-7.ts index d2f075c..9555351 100644 --- a/src/sections/section-7.ts +++ b/src/sections/section-7.ts @@ -3,6 +3,7 @@ import { Buffer } from 'buffer/' import { DataRepresentationSectionValues } from './section-5' import { convertData } from '../data' +import { BitMapSectionValues } from './section-6' export type DataSectionValues = ReturnType export type DataSection = ReturnType @@ -31,11 +32,11 @@ export const parseSection7 = (section: Buffer) => { * @param drs Data Representation Section * @returns Data Section with corresponding string values */ -export const lookupSection7 = (ds: DataSectionValues, drs: DataRepresentationSectionValues) => { +export const lookupSection7 = (ds: DataSectionValues, drs: DataRepresentationSectionValues, bms: BitMapSectionValues) => { return { ...ds, contents: { - data: convertData(drs, ds.contents.data) + data: convertData(drs, bms, ds.contents.data), } } } diff --git a/src/tables/table-5.ts b/src/tables/table-5.ts index 8385566..f5d8df4 100644 --- a/src/tables/table-5.ts +++ b/src/tables/table-5.ts @@ -53,3 +53,22 @@ export const lookupTable51 = (code: number) => { throw new Error(`Table 5.1 missing code value ${code}`) } } + +/** + * Table 5.40 - TYPE OF COMPRESSION USED + * [Read more](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table5-40.shtml) + */ +export const lookupTable540 = (code: number) => { + switch (code) { + case 0: + return 'Lossless' + case 1: + return 'Lossy' + // Range 2-254 is Reserved + case 255: + return 'Missing' + + default: + throw new Error(`Table 5.40 missing code value ${code}`) + } +} diff --git a/src/tables/table.ts b/src/tables/table.ts index e5ad107..ff95ea1 100644 --- a/src/tables/table.ts +++ b/src/tables/table.ts @@ -468,6 +468,8 @@ export const lookupTable0 = (code: number) => { */ export const lookupTableA = (code: number) => { switch (code) { + case 0: + return 'Unknown' case 2: return 'Ultra Violet Index Model' case 3: diff --git a/src/templates/template-3.ts b/src/templates/template-3.ts index 01db02b..46cd567 100644 --- a/src/templates/template-3.ts +++ b/src/templates/template-3.ts @@ -45,10 +45,10 @@ const template30 = (section: Buffer) => { return { /** Shape of Earth [Table 3.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-2.shtml) */ shape: section.readUInt8(14), - /** Number of points along a parallel */ - nx: section.readUInt32BE(30), - /** Number of points along a meridian */ - ny: section.readUInt32BE(34), + /** Number of points along a parallel (W-E) */ + ny: section.readUInt32BE(30), + /** Number of points along a meridian (N-S) */ + nx: section.readUInt32BE(34), /** Basic angle of the initial production domain */ basicAngle, /** Subdivisions of basic angle used to define extreme longitudes and latitudes, and direction increments */ diff --git a/src/templates/template-5.ts b/src/templates/template-5.ts index ca990fc..38769ff 100644 --- a/src/templates/template-5.ts +++ b/src/templates/template-5.ts @@ -1,16 +1,19 @@ import { Buffer } from 'buffer/' -import { lookupTable51 } from '../tables/table-5' +import { lookupTable51, lookupTable540 } from '../tables/table-5' /** * @description Returns a template generator for the given template number * @param template Template number * @returns Template generator */ -export const getTemplate5 = (template: number) => { +export const getTemplate5 = (template: number): typeof template50 | typeof template540 => { switch (template) { + case 40: + return template540 case 0: return template50 + default: throw new Error(`Template 5.${template} not defined`) } @@ -25,7 +28,8 @@ export const lookupTemplate5 = (template: number) => { switch (template) { case 0: return lookupTemplate50 - + case 40: + return lookupTemplate540 default: throw new Error(`Template 5.${template} lookup table not defined`) } @@ -58,3 +62,36 @@ const lookupTemplate50 = (templateValues: ReturnType) => { originalType: lookupTable51(templateValues.originalType) } } + +/** + * Data Representation Template 5.40 + * + * [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp5-40.shtml) + */ +const template540 = (section: Buffer) => { + return { + /** Reference value (R) (IEEE 32-bit floating-point value) */ + referenceValue: section.readFloatBE(11), + /** Binary scale factor (E) */ + binaryScaleFactor: section.readInt16BE(15), + /** Decimal scale factor (D) */ + decimalScaleFactor: section.readInt16BE(17), + /** Number of bits used for each packed value for simple packing, or for each group reference value for complex packing or spatial differencing */ + numberOfBits: section.readUInt8(19), + /** Type of original field values (see Code [Table 5.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table5-1.shtml)) */ + originalType: section.readUInt8(20), + // Type of Compression used. (see [Code Table 5.40](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table5-40.shtml)) + compressionType: section.readUInt8(21), + // Target compression ratio, M:1 (with respect to the bit-depth specified in octet 20), + // when octet 22 indicates Lossy Compression. Otherwise, set to missing. + compressionRatio: section.readUInt8(22) + } +} + +const lookupTemplate540 = (templateValues: ReturnType) => { + return { + ...lookupTemplate50(templateValues), + // Type of Compression used + compressionType: lookupTable540(templateValues.compressionType) + } +}