001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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}