Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```
Expand Down
162 changes: 76 additions & 86 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
11 changes: 7 additions & 4 deletions src/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@ 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
* @param drs Data Representation Section
* @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`)
}
}
}
57 changes: 57 additions & 0 deletions src/data/jpeg2000.ts
Original file line number Diff line number Diff line change
@@ -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
}
File renamed without changes.
22 changes: 21 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ const parseNoLookup = (data: ArrayBuffer): Array<GRIBPacketValues> => {
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 => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain a bit what this function would be used for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intent was to have a way to get data associated with certain lat, lon from the packet. I wasn't able to find anything for this that's currently in the library.

What's your view on this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's definitely something that is needed, however, it looks like this would only be compatible with GRIBs that utilize Grid Def Template 3.0.

For compatibility, I feel like this function should first check that the packet utilizes a compatible Grid Template.

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'
Loading