Zarr
As of version 5.5.1, the netCDF-Java library provides read-only support for the Zarr v2 data model.
Any dataset that adheres to the v2 spec can be read into a NetcdfFile object, as long as the following is true:
- all filters and compressors used by the dataset must be known to the netCDF-Java library (see Filters)
- the underlying storage of the dataset must be a directory store, zip store, or object store
Enabling Zarr support
To use Zarr in the netCDF-Java library, you must include the cdm-zarr module in your netCDF-Java build.
See here for more information on including optional modules.
How to read a Zarr dataset
Reading a Zarr dataset is syntactically the same as reading a netCDF file:
// a local file path
NetcdfFile directoryStoreZarr = NetcdfFiles.open(pathToDirectoryStore);
// a local file path + '.zip'
NetcdfFile zipdirectoryStoreZarr = NetcdfFiles.open(pathToZipStore);
// an object store path, sarting with 'cdms3:' and ending with 'delimiter=' + the store delimiter
NetcdfFile objectStoreZarr = NetcdfFiles.open(pathToObjectStore);
If the file is a legal Zarr dataset, the library will map it to a NetcdfFile object for reading.
See reading CDM files for more examples on accessing data once the NetcdfFile object is returned.
Filters
As of netCDF-Java version 5.5.1, a ucar.nc2.filter package is included, that provides a suite of implemented filters and compressors,
as well as a mechanism for user-supplied filters. This package is used by both the Zarr and HDF5 IOSPs, and is available for public use.
The current list of filters included natively in the netCDF-Java library is:
- Deflate (zlib)
- Shuffle
- 32-bit Checksum (CRC, Fletcher, and Adler)
- ScaleOffset
This list is still expanding, but if the filter you are looking for is not provided at this time, you are able to provide it yourself (See Implementing a Filter) for details.)
Implementing a Filter
To add a user-supplied Filter to the netDF-Java library, you will have to provide two classes:
- A class that extends the
ucar.nc2.filter.Filterabstract class - A class that implements the
ucar.nc2.filter.FilterProviderinterface
Once implemented, you will need to include these classes as JAR files in your classpath. See here for more information.
Filter implementation
To implement a user-supplied filter, you will need to extend the abstract ucar.nc2.filter.Filter class, and provide implementations for the following methods:
getNamereturns aStringidentifier for the filter (see note below on filter names)getIdreturns anintidentifier for the filter (see note below on filter ids)encodetakes abyte[]of unfiltered data and returns abyte[]of filtered datadecodetakes abyte[]of filtered data and returns abyte[]of unfiltered data
Your Filter class should look something like this:
byte[] dataOut = null;
public class MyFilter extends Filter {
static final String name = "myFilter";
static final int id = 32768;
@Override
public String getName() {
return name;
}
@Override
public int getId() {
return id;
}
@Override
public byte[] encode(byte[] dataIn) throws IOException {
// your encoding implementation here
return dataOut;
}
@Override
public byte[] decode(byte[] dataIn) throws IOException {
// your decoding implementation here
return dataOut;
}
}
getName and getId When reading data, IOSPs can look up filters by either a
String or int (name or id). Currently, the ucar.nc2.filter
package is shared by the Zarr and HDF5 IOSPs. The Zarr IOSP looks up filters by name; if you plan to use your third party filter with Zarr data,
the string returned by getName should match that specified by the NumCodecs library.
The HDF5 IOSP looks up filters by id; if you plan to use your third party filter with HDF5 data, the int returned by getId should adhere to the
guidelines set by the HDF group.
If your filter is registered with the HDF group, your getId method should return the HDF id. If your filter is not registered with the HDF group,
FilterProvider implementation
For the netCDF-Java library to find your Filter implementation, you will need to provide a FilterProvider as well.
public class MyFilterProvider implements FilterProvider {
@Override
public String getName() {
// returns a string identifier for your filter
return MyFilter.name; // should match name of your Filter class
}
@Override
public int getId() {
// returns a numeric identifier for your filter
return MyFilter.id; // should match id of your Filter class
}
@Override
public Filter create(Map<String, Object> properties) {
return new MyFilter(properties); // return an instance of your filter
}
}
There are two more methods in the FilterProvider interface: the canProvide methods. By default, these methods work as follows:
boolean canProvide(String name)returns true if the string returned bygetName()matches the string passed to the methodboolean canProvide(int id)returns true if the int returned bygetId()matches the int passed to the method
It is unlikely that you would want to override these methods, as the netCDF-Java IOSPs look for the correct filter implementations
for a dataset by either name or numeric id. However, it is possible to write your own implementation of canProvide; for example, the
following FilterProvider returns an instance of a DefaultFilter regardless of the name or id provided.
public class DefaultFilterProvider implements FilterProvider {
@Override
public String getName() {
return DefaultFilter.name;
}
@Override
public int getId() {
return DefaultFilter.id;
}
@Override
public boolean canProvide(String name) {
return true;
}
@Override
public boolean canProvide(int id) {
return true;
}
@Override
public Filter create(Map<String, Object> properties) {
return new DefaultFilter(properties); // return an instance of your filter
}
}