JAVA读取NC文件的工具包

本文介绍了NetCDF文件格式,其用于气象数据存储,涵盖变量、维度和属性的概念,并重点讲解了netcdf包的使用以及NcUtil工具类提供的便捷读取切片功能。通过实例代码展示了如何通过NcUtil进行数据读取和限制条件设置。
摘要由CSDN通过智能技术生成

1、NC文件

NetCDF全称为network Common Data Format,中文译法为“网络通用数据格式”,对程序员来说,它和zip、jpeg、bmp文件格式类似,都是一种文件格式的标准。

  • netcdf文件开始的目的是用于存储气象科学中的数据,现在已经成为许多数据采集软件的生成文件的格式。
  • 从数学上来说,netcdf存储的数据就是一个多自变量的单值函数。用公式来说就是f(x,y,z,…)=value, 函数的自变量x,y,z等在netcdf中叫做(dimension)或坐标轴(axix),函数值value在netcdf中叫做变量(Variables).而自变量和函数值在物理学上的一些性质,比如计量单位(量纲)、物理学名称等等在netcdf中就叫属性(Attributes).
netcdf文件的内容
  1. 变量(Variables)
    • 变量对应着真实的物理数据。
  2. 维(dimension)
    • 一个维对应着函数中的某个自变量,或者说函数图象中的一个坐标轴
  3. 属性(Attribute)
    • 属性对变量值和维的具体物理含义的注释或者说解释。
2、netcdf包

通过Java读取nc文件,目前比较活的是netcdf包,提供了非常全面的nc文件读写。但许多方法的使用并不容易理解。这里我在netcdf包的基础上,提供了一些便于使用的读取nc文件的工具类

netcdf包可以通过Maven仓库获取

pom.xml

<!-- 读取 .nc 文件 -->
<dependency>
    <groupId>edu.ucar</groupId>
    <artifactId>netcdf4</artifactId>
    <version>4.5.5</version>
</dependency>
3、NcUtil

NcUtil.java

import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Variable;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public interface NcUtils {

    /**
     * 读取变量中的全部数据,对于 3 维及以上的数据,是切片的数据
     * 即某一变量的某一层数据
     *
     * @return 如果是 3 维数据,取 0 维 0 层切片;如果是 4 维数据,取 0 维 0 层、 1 维 0 层切片;
     * @throws IOException           变量读取数据{@code variable.read()}时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     * @param variable
     */
    NcData getDataOfSlice(Variable variable) throws IOException, InvalidRangeException;

    /**
     * 读取变量中的全部数据,对于 3 维及以上的数据,是切片的数据
     * 即某一变量的某一层数据
     *
     * @param variable    具体的某一变量
     * @param positionOf0 如果是 3 维数据,该参数代表第 0 维切片层数;如果是 4 维数据,该参数代表第 1 维切片层数
     * @return 如果是 3 维数据,取 0 维 positionOf0 层切片;如果是 4 维数据,取 0 维 0 层、1 维 positionOf0 层切片
     * @throws IOException           变量读取数据{@code variable.read()}时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     */
    NcData getDataOfSlice(Variable variable, @Nullable Integer positionOf0) throws IOException, InvalidRangeException;

    /**
     * 读取变量中的全部数据,对于 3 维及以上的数据,是切片的数据
     * 即某一变量的某一层数据
     *
     * @param variable    具体的某一变量
     * @param positionOf0 如果是 3 维数据,该参数代表第 0 维切片层数;如果是 4 维数据,该参数代表第 1 维切片层数
     * @param positionOf1 如果是 3 维数据,不会使用该参数;如果是 4 维数据,该参数代表第 0 维切片层数
     * @return 如果是 3 维数据,取 0 维 positionOf0 层切片;如果是 4 维数据,取 0 维 positionOf1 层、1 维 positionOf0 层切片
     * @throws IOException           变量读取数据{@code variable.read()}时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     */
    NcData getDataOfSlice(Variable variable, @Nullable Integer positionOf0, @Nullable Integer positionOf1) throws IOException, InvalidRangeException;

    /**
     * 读取变量集合中的全部数据,对于 3 维及以上的数据,是切片的数据
     *
     * @param variables 变量列表
     * @return 如果是 3 维数据,取 0 维 0 层切片;如果是 4 维数据,取 0 维 0 层、 1 维 0 层切片;
     * @throws IOException           变量读取数据 {@code variable.read()} 时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     */
    Map<String, NcData> getAllDataOfSlice(List<Variable> variables) throws IOException, InvalidRangeException;

    /**
     * 读取变量集合中的全部数据,对于 3 维及以上的数据,是切片的数据
     *
     * @param variables   变量列表
     * @param positionOf0 如果是 3 维数据,该参数代表第 0 维切片层数;如果是 4 维数据,该参数代表第 1 维切片层数
     * @return 如果是 3 维数据,取 0 维 positionOf0 层切片;如果是 4 维数据,取 0 维 0 层、1 维 positionOf0 层切片
     * @throws IOException           变量读取数据 {@code variable.read()} 时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     */
    Map<String, NcData> getAllDataOfSlice(List<Variable> variables, @Nullable Integer positionOf0) throws IOException, InvalidRangeException;

    /**
     * 读取变量集合中的全部数据,对于 3 维及以上的数据,是切片的数据
     *
     * @param variables   变量列表
     * @param positionOf0 如果是 3 维数据,该参数代表第 0 维切片层数;如果是 4 维数据,该参数代表第 1 维切片层数
     * @param positionOf1 如果是 3 维数据,不会使用该参数;如果是 4 维数据,该参数代表第 0 维切片层数
     * @return 如果是 3 维数据,取 0 维 positionOf0 层切片;如果是 4 维数据,取 0 维 positionOf1 层、1 维 positionOf0 层切片
     * @throws IOException           变量读取数据 {@code variable.read()} 时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     */
    Map<String, NcData> getAllDataOfSlice(List<Variable> variables, @Nullable Integer positionOf0, @Nullable Integer positionOf1) throws IOException, InvalidRangeException;

    /**
     * 设置维度条件,读取变量中的数据
     * 该方法至多设置3个维度条件
     *
     * @param variable     具体的某一变量
     * @param ncDimensions 维度实体类
     * @return  返回带有限制参数的维度对应变量nc数据,以及变量nc数据
     * @throws IOException           变量读取数据{@code variable.read()}时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     */
    NcData getDataWithLimit(@NotNull Variable variable,@NotNull NcDimension... ncDimensions) throws IOException, InvalidRangeException;

    /**
     * 设置维度条件,读取变量集合中的所有数据
     * 该方法至多设置3个维度条件
     *
     * @param variables     变量列表
     * @param ncDimensions 维度实体类
     * @return  返回带有限制参数的维度对应变量nc数据,以及变量列表nc数据
     * @throws IOException           变量读取数据{@code variable.read()}时抛出的异常
     * @throws InvalidRangeException 切片过程中,重新生成 Range 对象时抛出的范围异常,已做保护措施,应该不会抛出该异常
     */
    Map<String,NcData> getAllDataWithLimit(@NotNull List<Variable> variables,@NotNull NcDimension... ncDimensions) throws IOException, InvalidRangeException;

}

NcUtilImpl.java

package util.nc;

import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;
import ucar.nc2.Dimension;
import ucar.nc2.Variable;
import util.MathUtils;

import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class NcUtilsImpl implements NcUtils {
    @Override
    public NcData getDataOfSlice(Variable variable) throws IOException, InvalidRangeException {
        return getDataOfSlice(variable, null);
    }

    @Override
    public NcData getDataOfSlice(Variable variable, Integer positionOf0) throws IOException, InvalidRangeException {
        return getDataOfSlice(variable, null, null);
    }

    @Override
    public NcData getDataOfSlice(Variable variable, Integer positionOf0, Integer positionOf1) throws IOException, InvalidRangeException {
        if (variable == null)
            return null;
        NcData ncData = new NcData();
        ncData.setDataType(variable.getDataType());

        int rank = variable.getRank();    //return shape.length,返回维度个数
        ncData.setDimensions(rank);     //设置维度
        /**
         * 根据维度和变量数据类型的不同,返回不同的数组
         */
        switch (rank) {
            case 1:
                if (ncData.getDataType() == DataType.FLOAT)
                    ncData.setData1Df((float[]) variable.read().getStorage());
                else
                    ncData.setData1Dd((double[]) variable.read().getStorage());
                break;
            case 2:
                if (ncData.getDataType() == DataType.FLOAT)
                    ncData.setData2Df((float[][]) variable.read().copyToNDJavaArray());
                else
                    ncData.setData2Dd((double[][]) variable.read().copyToNDJavaArray());
                break;
            case 3:
                //variable.getRanges(),返回决定该变量的各个维度的长度
                List<Range> ranges3D = new ArrayList<>(variable.getRanges());
                Range range3D = ranges3D.get(0);    //返回第 0 维度的长度
                positionOf0 = positionOf0 == null ? 0 : MathUtils.middle(positionOf0, 0, range3D.length() - 1);
                ranges3D.set(0, new Range(range3D.getName(), positionOf0, positionOf0, range3D.stride()));   //重新设置第0维度的长度,即获取第0维度的positionOf0层
                if (ncData.getDataType() == DataType.FLOAT)
                    ncData.setData2Df((float[][]) variable.read(ranges3D).reduce().copyToNDJavaArray());
                else
                    ncData.setData2Dd((double[][]) variable.read(ranges3D).reduce().copyToNDJavaArray());
                break;
            case 4:
                List<Range> ranges4D = new ArrayList<>(variable.getRanges());
                Range range4D0 = ranges4D.get(0);
                positionOf1 = positionOf1 == null ? 0 : MathUtils.middle(positionOf1, 0, range4D0.length() - 1);
                ranges4D.set(0, new Range(range4D0.getName(), positionOf1, positionOf1, range4D0.stride()));
                Range range4D1 = ranges4D.get(1);
                positionOf0 = positionOf0 == null ? 0 : MathUtils.middle(positionOf0, 0, range4D1.length() - 1);
                ranges4D.set(1, new Range(range4D1.getName(), positionOf0, positionOf0, range4D1.stride()));
                if (ncData.getDataType() == DataType.FLOAT)
                    ncData.setData2Df((float[][]) variable.read(ranges4D).reduce().copyToNDJavaArray());
                else
                    ncData.setData2Dd((double[][]) variable.read(ranges4D).reduce().copyToNDJavaArray());
                break;
        }
        return ncData;
    }

    @Override
    public Map<String, NcData> getAllDataOfSlice(List<Variable> variables) throws IOException, InvalidRangeException {
        return getAllDataOfSlice(variables, null);
    }

    @Override
    public Map<String, NcData> getAllDataOfSlice(List<Variable> variables, Integer positionOf0) throws IOException, InvalidRangeException {
        return getAllDataOfSlice(variables, null, null);
    }

    @Override
    public Map<String, NcData> getAllDataOfSlice(List<Variable> variables, Integer positionOf0, Integer positionOf1) throws IOException, InvalidRangeException {
        Map<String, NcData> map = new HashMap<>();
        for (Variable variable : variables) {
            map.put(variable.getFullName(), getDataOfSlice(variable, positionOf0, positionOf1));
        }
        return map;
    }

    @Override
    public NcData getDataWithLimit(Variable variable, NcDimension... ncDimensions) throws IOException, InvalidRangeException {
        if (variable == null)
            return null;
        NcData ncData = new NcData();
        ncData.setDataType(variable.getDataType());    //设置nc实体类的数据类型
        int limitLength = ncDimensions.length;  //有限制条件的维度数
        int rank = variable.getRank();    //return shape.length,返回维度个数
        ncData.setDimensions(rank);     //设置维度

        int[] origin = new int[rank];
        int[] s = new int[rank];
        for (int i = 0; i < s.length; i++) {
            s[i] = 1;
        }
        setOriginAndSection(variable, origin, s, ncDimensions); //重新给origin和s赋值
        //获取有限制条件的维度集合
        switch (rank) {
            case 1:
                if (ncData.getDataType() == DataType.FLOAT)
                    ncData.setData1Df((float[]) variable.read(origin, s).getStorage());
                else
                    ncData.setData1Dd((double[]) variable.read(origin, s).getStorage());
                break;
            case 2:
                if (ncData.getDataType() == DataType.FLOAT)
                    ncData.setData2Df((float[][]) variable.read(origin, s).copyToNDJavaArray());
                else
                    ncData.setData2Dd((double[][]) variable.read(origin, s).copyToNDJavaArray());
                break;
            case 3:
                if (ncData.getDataType() == DataType.FLOAT)
                    ncData.setData3Df((float[][][]) variable.read(origin, s).copyToNDJavaArray());
                else
                    ncData.setData3Dd((double[][][]) variable.read(origin, s).copyToNDJavaArray());
                break;
            case 4:
                //仅降低一个维度,转为三维数组
                for (int i = 0; i < s.length; i++) {
                    if (s[i] == 1) {
                        if (ncData.getDataType() == DataType.FLOAT)
                            ncData.setData3Df((float[][][]) variable.read(origin, s).reduce(i).copyToNDJavaArray());
                        else
                            ncData.setData3Dd((double[][][]) variable.read(origin, s).reduce(i).copyToNDJavaArray());
                        break;
                    }
                }
                break;
        }

        return ncData;
    }

    @Override
    public Map<String, NcData> getAllDataWithLimit(List<Variable> variables, NcDimension... ncDimensions) throws IOException, InvalidRangeException {
        Map<String, NcData> map = new HashMap<>();
        for (Variable variable : variables) {
            map.put(variable.getFullName(), getDataWithLimit(variable, ncDimensions));
        }
        return map;
    }

    private void setOriginAndSection(@NotNull Variable variable, int[] origin, int[] s, @NotNull NcDimension... ncDimensions) {
        int startIndex = 0;
        for (NcDimension ncDimension : ncDimensions) {
            Dimension dimension = ncDimension.getDimension();
            int dimensionIndex = variable.findDimensionIndex(dimension.getFullName());
            //判断variable是否存在该dimension
            if (dimensionIndex != -1) {
                startIndex = ncDimension.getStartIndex();
                origin[dimensionIndex] = MathUtils.middle(startIndex, 0, dimension.getLength() - 1);
                s[dimensionIndex] = MathUtils.middle(ncDimension.getSection(), 1, dimension.getLength() - 1 - startIndex);
            }
        }
    }
}

NcData.java(实体类)

package util.nc;

import ucar.ma2.DataType;

import java.util.Arrays;

/**
 * nc数据的实体类
 */
public class NcData {

    /**
     * 维度
     */
    private int dimensions;

    /**
     * 数据类型
     * nc基本数据类型有六种:boolean,byte,char,short,int,long,float,double
     * 用的是{@link ucar.ma2.DataType}
     */
    private DataType dataType;

    private float[] data1Df;    //float类型的一维数组

    private float[][] data2Df;    //float类型的二维数组

    private float[][][] data3Df;    //float类型的三维数组

    private double[] data1Dd;    //double类型的一维数组

    private double[][] data2Dd;    //double类型的二维数组

    private double[][][] data3Dd;    //double类型的三维数组

    @Override
    public String toString() {
        if (dataType == DataType.FLOAT) {
            return "NcData{" +
                    "dimensions=" + dimensions +
                    ", dataType=" + dataType +
                    ", data1Df=" + Arrays.toString(data1Df) +
                    ", data2Df=" + Arrays.deepToString(data2Df) +
                    ", data3Df=" + Arrays.deepToString(data3Df) +
                    '}';
        } else {
            return "NcData{" +
                    "dimensions=" + dimensions +
                    ", dataType=" + dataType +
                    ", data1Dd=" + Arrays.toString(data1Dd) +
                    ", data2Dd=" + Arrays.deepToString(data2Dd) +
                    ", data3Dd=" + Arrays.deepToString(data3Dd) +
                    '}';
        }
    }

    public int getDimensions() {
        return dimensions;
    }

    public void setDimensions(int dimensions) {
        this.dimensions = dimensions;
    }

    public DataType getDataType() {
        return dataType;
    }

    public void setDataType(DataType dataType) {
        this.dataType = dataType;
    }

    public float[] getData1Df() {
        return data1Df;
    }

    public void setData1Df(float[] data1Df) {
        this.data1Df = data1Df;
    }

    public float[][] getData2Df() {
        return data2Df;
    }

    public void setData2Df(float[][] data2Df) {
        this.data2Df = data2Df;
    }

    public float[][][] getData3Df() {
        return data3Df;
    }

    public void setData3Df(float[][][] data3Df) {
        this.data3Df = data3Df;
    }

    public double[] getData1Dd() {
        return data1Dd;
    }

    public void setData1Dd(double[] data1Dd) {
        this.data1Dd = data1Dd;
    }

    public double[][] getData2Dd() {
        return data2Dd;
    }

    public void setData2Dd(double[][] data2Dd) {
        this.data2Dd = data2Dd;
    }

    public double[][][] getData3Dd() {
        return data3Dd;
    }

    public void setData3Dd(double[][][] data3Dd) {
        this.data3Dd = data3Dd;
    }
}

NcDimension.java

package util.nc;

import ucar.ma2.DataType;
import ucar.nc2.Dimension;

/**
 * nc文件维度实体类
 */
public class NcDimension {

    private Dimension dimension;  //维度变量
    private DataType dataType;  //维度变量数据类型
    private int dimensionIndex; //维度的下标
    private int startIndex;  //维度变量的起始下标
    private int section;    //维度变量从起始下标位置,获取的长度

    public NcDimension() {
    }

    public NcDimension(Dimension dimension, int startIndex, int section) {
        this.dimension = dimension;
        this.startIndex = startIndex;
        this.section = section;
    }

    public Dimension getDimension() {
        return dimension;
    }

    public void setDimension(Dimension dimension) {
        this.dimension = dimension;
    }

    public DataType getDataType() {
        return dataType;
    }

    public void setDataType(DataType dataType) {
        this.dataType = dataType;
    }

    public int getDimensionIndex() {
        return dimensionIndex;
    }

    public void setDimensionIndex(int dimensionIndex) {
        this.dimensionIndex = dimensionIndex;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    public int getSection() {
        return section;
    }

    public void setSection(int section) {
        this.section = section;
    }
}

相关资源下载:读取nc文件的工具包

  • 8
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值