001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.data;
030
031import java.io.IOException;
032
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import ucar.unidata.io.RandomAccessFile;
037
038import ucar.ma2.Array;
039import ucar.ma2.DataType;
040import ucar.ma2.InvalidRangeException;
041import ucar.ma2.Section;
042
043import ucar.nc2.Attribute;
044import ucar.nc2.Dimension;
045import ucar.nc2.Group;
046import ucar.nc2.NetcdfFile;
047import ucar.nc2.Variable;
048import ucar.nc2.constants.AxisType;
049import ucar.nc2.constants._Coordinate;
050import ucar.nc2.iosp.AbstractIOServiceProvider;
051import ucar.nc2.util.CancelTask;
052
053public class GpmIosp extends AbstractIOServiceProvider {
054
055    private static final String LAT = "Latitude";
056
057    private static final String LON = "Longitude";
058
059    private static final String TC_PREFIX = "Tc_";
060
061    private static final Logger logger = LoggerFactory.getLogger(GpmIosp.class);
062
063    private NetcdfFile hdfFile;
064
065    private static int[] getDimensionLengths(NetcdfFile hdf, String groupName) throws IOException {
066        Group group = hdf.findGroup(groupName);
067        Variable tc = group.findVariableLocal("Tc");
068        
069        return new int[] {
070            tc.getDimension(0).getLength(),
071            tc.getDimension(1).getLength(),
072            tc.getDimension(2).getLength()
073        };
074    }
075
076    private static void addVar(NetcdfFile nc, Group g, int channel) {
077        String varName = TC_PREFIX + channel;
078        Variable v = new Variable(nc, g, null, varName, DataType.FLOAT, "line ele");
079        v.addAttribute(new Attribute("coordinates", "latitude longitude"));
080        g.addVariable(v);
081    }
082
083    private static int variableToChannel(String variableName) {
084        int result = -1;
085        if (variableName.startsWith("Tc_")) {
086            String temp = variableName.substring(3);
087            result = Integer.valueOf(temp);
088        }
089        return result;
090    }
091
092    private static void addLatitude(NetcdfFile nc, Group g) {
093        Variable lat = new Variable(nc, g, null, LAT, DataType.FLOAT, "line ele");
094        lat.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString()));
095        g.addVariable(lat);
096    }
097
098    private static void addLongitude(NetcdfFile nc, Group g) {
099        Variable lon = new Variable(nc, g, null, LON, DataType.FLOAT, "line ele");
100        lon.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString()));
101        g.addVariable(lon);
102    }
103
104
105    @Override public boolean isValidFile(RandomAccessFile raf)
106        throws IOException
107    {
108        // this isn't exactly a suitable test--but maybe it'll work for now?
109        // the better check is to probably try opening the location as a netcdf
110        // file and looking for S1/S2 groups and possibly S1/Tc S2/Tc vars?
111        String location = raf.getLocation();
112        return location.contains("1C-R-CS") && location.endsWith(".HDF");
113    }
114
115    @Override public void open(RandomAccessFile raf, NetcdfFile ncfile,
116                     CancelTask cancelTask) throws IOException
117    {
118        try {
119            hdfFile = NetcdfFile.open(raf.getLocation(), "ucar.nc2.iosp.hdf5.H5iosp", -1, (CancelTask)null, (Object)null);
120            createGroupFromHdf(ncfile, "S1");
121            createGroupFromHdf(ncfile, "S2");
122            ncfile.finish();
123        } catch (ClassNotFoundException e) {
124            logger.error("error loading HDF5 IOSP", e);
125        } catch (IllegalAccessException e) {
126            logger.error("java reflection error", e);
127        } catch (InstantiationException e) {
128            logger.error("error instantiating", e);
129        }
130    }
131
132    private void createGroupFromHdf(NetcdfFile ncOut, String groupName)
133        throws IOException
134    {
135        Group s1 = new Group(ncOut, null, groupName);
136        int[] dimLen = getDimensionLengths(hdfFile, groupName);
137        ncOut.addGroup(null, s1);
138        s1.addDimension(new Dimension("line", dimLen[0]));
139        s1.addDimension(new Dimension("ele", dimLen[1]));
140        addLatitude(ncOut, s1);
141        addLongitude(ncOut, s1);
142        for (int i = 0; i < dimLen[2]; i++) {
143            addVar(ncOut, s1, i);
144        }
145    }
146
147    @Override public Array readData(Variable variable, Section section)
148        throws IOException, InvalidRangeException
149    {
150        String variableName = variable.getShortName();
151        String groupName = variable.getParentGroup().getFullName();
152        Group hdfGroup = hdfFile.findGroup(groupName);
153        Array result;
154
155        if (variableName.equals(LAT) || variableName.equals(LON)) {
156            Variable hdfVariable = hdfFile.findVariable(hdfGroup, variableName);
157            result = hdfVariable.read();
158        } else if (variableName.startsWith("Tc_")) {
159            int channel = variableToChannel(variableName);
160            int[] counts = getDimensionLengths(hdfFile, groupName);
161            Variable hdfVariable = hdfFile.findVariable(hdfGroup, "Tc");
162            result = readChannel(hdfVariable, counts[0], counts[1], channel);
163        } else {
164            result = null;
165        }
166        return result;
167    }
168
169    private Array readChannel(Variable v, int lines, int elements, int channel)
170        throws IOException, InvalidRangeException
171    {
172        // "S1/Tc" and "S2/Tc" (aka "v") is laid out like "line, ele, chan"
173        // this origin/size business is the notation required for per-channel
174        // access
175        // see "Reading data from a Variable" from
176        // http://www.unidata.ucar.edu/software/thredds/current/netcdf-java/tutorial/NetcdfFile.html
177        int[] origin = { 0, 0, channel };
178        int[] size = { lines, elements, 1};
179        return v.read(origin, size).reduce();
180    }
181
182    @Override public String getFileTypeId() {
183        return "GPM-1C-R-CS";
184    }
185
186    @Override public String getFileTypeDescription() {
187        return "No Idea!";
188    }
189
190    @Override public void close() throws IOException {
191        logger.trace("getting called");
192        hdfFile.close();
193    }
194
195    public static void main(String args[]) throws IOException, IllegalAccessException, InstantiationException {
196        NetcdfFile.registerIOProvider(GpmIosp.class);
197        NetcdfFile ncfile = NetcdfFile.open("/Users/jbeavers/Downloads/mike-gpm-script/1C-R-CS-95W50N74W39N.GPM.GMI.XCAL2014-N.20150109-S122429-E122829.004914.V03C.HDF");
198        System.out.println("ncfile = \n" + ncfile);
199    }
200}