/* * Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package ucar.nc2.dt.radial; import ucar.ma2.*; import ucar.nc2.Attribute; import ucar.nc2.Variable; import ucar.nc2.VariableSimpleIF; import ucar.nc2.constants.FeatureType; import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.dt.RadialDatasetSweep; import ucar.nc2.dt.TypedDataset; import ucar.nc2.dt.TypedDatasetFactoryIF; import ucar.nc2.time.CalendarDate; import ucar.nc2.time.CalendarDateUnit; import ucar.nc2.units.DateUnit; import ucar.unidata.geoloc.Earth; import ucar.unidata.geoloc.LatLonPointImpl; import ucar.unidata.geoloc.LatLonRect; import java.io.IOException; import java.util.*; /** * Created by IntelliJ IDEA. * User: yuanho * Date: Jun 13, 2011 * Time: 1:15:48 PM * To change this template use File | Settings | File Templates. */ public class CFnetCDF2Dataset extends RadialDatasetSweepAdapter implements TypedDatasetFactoryIF { private NetcdfDataset ds = null; private double latv, lonv, elev; private double[] time; private float[] elevation; private float[] azimuth; private float[] range; private int[] rayStartIdx; private int[] rayEndIdx; private int[] ray_n_gates; private int[] ray_start_index; private int nsweeps; ///////////////////////////////////////////////// // TypedDatasetFactoryIF public boolean isMine(NetcdfDataset ds) { String convention = ds.findAttValueIgnoreCase(null, "Conventions", null); return (null != convention) && convention.startsWith("CF/Radial"); } public TypedDataset open(NetcdfDataset ncd, ucar.nc2.util.CancelTask task, StringBuilder errlog) throws IOException { return new CFnetCDF2Dataset(ncd); } public FeatureType getScientificDataType() { return FeatureType.RADIAL; } public CFnetCDF2Dataset() { } /** * Constructor. * * @param ds Source NetCDF dataset */ public CFnetCDF2Dataset(NetcdfDataset ds) { this.ds = ds; desc = "CF/Radial radar dataset"; init(); for (Variable var : ds.getVariables()) { addRadialVariable(ds, var); } } public void init() { setEarthLocation(); try { Variable t = ds.findVariable("time"); Array tArray = t.read(); time = (double[]) tArray.copyTo1DJavaArray(); Variable ele = ds.findVariable("elevation"); Array eArray = ele.read(); elevation = (float[]) eArray.copyTo1DJavaArray(); Variable azi = ds.findVariable("azimuth"); Array aArray = azi.read(); azimuth = (float[]) aArray.copyTo1DJavaArray(); Variable rng = ds.findVariable("range"); Array rArray = rng.read(); range = (float[]) rArray.copyTo1DJavaArray(); Variable sidx0 = ds.findVariable("sweep_start_ray_index"); rayStartIdx = (int[]) sidx0.read().copyTo1DJavaArray(); Variable sidx1 = ds.findVariable("sweep_end_ray_index"); rayEndIdx = (int[]) sidx1.read().copyTo1DJavaArray(); nsweeps = ds.findDimension("sweep").getLength(); Variable var = ds.findVariable("ray_n_gates"); if (var != null) ray_n_gates = (int[]) var.read().copyTo1DJavaArray(); var = ds.findVariable("ray_start_index"); if (var != null) ray_start_index = (int[]) var.read().copyTo1DJavaArray(); setTimeUnits(); } catch (Exception e) { throw new RuntimeException(e); } setStartDate(); setEndDate(); setBoundingBox(); } protected void setBoundingBox() { LatLonRect bb; if (origin == null) { return; } double dLat = Math.toDegrees(getMaximumRadialDist() / Earth.getRadius()); double latRadians = Math.toRadians(origin.getLatitude()); double dLon = dLat * Math.cos(latRadians); double lat1 = origin.getLatitude() - dLat / 2; double lon1 = origin.getLongitude() - dLon / 2; bb = new LatLonRect(new LatLonPointImpl(lat1, lon1), dLat, dLon); boundingBox = bb; } double getMaximumRadialDist() { double maxdist = 0.0; for (Object dataVariable : dataVariables) { RadialVariable rv = (RadialVariable) dataVariable; Sweep sp = rv.getSweep(0); double dist = sp.getGateNumber() * sp.getGateSize(); if (dist > maxdist) { maxdist = dist; } } return maxdist; } protected void setEarthLocation() { try { Variable ga = ds.findVariable("latitude"); if (ga != null) { latv = ga.readScalarDouble(); } else { latv = 0.0; } ga = ds.findVariable("longitude"); if (ga != null) { lonv = ga.readScalarDouble(); } else { lonv = 0.0; } ga = ds.findVariable("altitude"); if (ga != null) { elev = ga.readScalarDouble(); } else { elev = 0.0; } } catch (IOException e) { } origin = new ucar.unidata.geoloc.EarthLocationImpl(latv, lonv, elev); } @Override public ucar.unidata.geoloc.EarthLocation getCommonOrigin() { return origin; } @Override public String getRadarID() { Attribute ga = ds.findGlobalAttribute("Station"); if (ga != null) { return ga.getStringValue(); } else { return "XXXX"; } } @Override public String getRadarName() { Attribute ga = ds.findGlobalAttribute("instrument_name"); if (ga != null) { return ga.getStringValue(); } else { return "Unknown Station"; } } @Override public String getDataFormat() { return "CF/RadialNetCDF"; } @Override public boolean isVolume() { return true; } public boolean isHighResolution(NetcdfDataset nds) { return true; } public boolean isStationary() { Variable lat = ds.findVariable("latitude"); return lat.getSize() == 1; } protected void setTimeUnits() throws Exception { Variable t = ds.findVariable("time"); String ut = t.getUnitsString(); dateUnits = new DateUnit(ut); calDateUnits = CalendarDateUnit.of(null, ut); } protected void setStartDate() { String datetime = ds.findAttValueIgnoreCase(null, "time_coverage_start", null); if (datetime != null) { startDate = CalendarDate.parseISOformat(null, datetime).toDate(); } else { startDate = calDateUnits.makeCalendarDate(time[0]).toDate(); } } protected void setEndDate() { String datetime = ds.findAttValueIgnoreCase(null, "time_coverage_end", null); if (datetime != null) { endDate = CalendarDate.parseISOformat(null, datetime).toDate(); } else { endDate = calDateUnits.makeCalendarDate(time[time.length - 1]).toDate(); } } public void clearDatasetMemory() { for (VariableSimpleIF rvar : getDataVariables()) { RadialVariable radVar = (RadialVariable) rvar; radVar.clearVariableMemory(); } } /** * _more_ * * @param nds _more_ * @param var _more_ */ protected void addRadialVariable(NetcdfDataset nds, Variable var) { RadialVariable rsvar = null; String vName = var.getShortName(); int tIdx = var.findDimensionIndex("time"); int rIdx = var.findDimensionIndex("range"); int ptsIdx = var.findDimensionIndex("n_points"); if (((tIdx == 0) && (rIdx == 1)) || (ptsIdx == 0)) { VariableSimpleIF v = new MyRadialVariableAdapter(vName, var.getAttributes()); rsvar = makeRadialVariable(nds, v, var); } if (rsvar != null) { dataVariables.add(rsvar); } } /** * _more_ * * @param nds _more_ * @param v _more_ * @param v0 _more_ * @return _more_ */ protected RadialVariable makeRadialVariable(NetcdfDataset nds, VariableSimpleIF v, Variable v0) { // this function is null in level 2 return new CFRadial2Variable(nds, v0); } /** * _more_ * * @return _more_ */ public String getInfo() { StringBuffer sbuff = new StringBuffer(); sbuff.append("CFRadial2Dataset\n"); sbuff.append(super.getDetailInfo()); sbuff.append("\n\n"); sbuff.append(parseInfo.toString()); return sbuff.toString(); } /** * Class description * * @author Enter your name here... * @version Enter version here..., Mon, Jun 13, '11 */ private class CFRadial2Variable extends MyRadialVariableAdapter implements RadialDatasetSweep.RadialVariable { /** * _more_ */ ArrayList<CFRadial2Sweep> sweeps; /** * _more_ */ String name; private boolean flattened; /** * _more_ * * @param nds _more_ * @param v0 _more_ */ private CFRadial2Variable(NetcdfDataset nds, Variable v0) { super(v0.getShortName(), v0.getAttributes()); sweeps = new ArrayList<>(); name = v0.getShortName(); int[] shape = v0.getShape(); int ngates = shape[v0.getRank() - 1]; flattened = v0.findDimensionIndex("n_points") == 0; for (int i = 0; i < nsweeps; i++) { // For flattened (1D stored data) find max number of gates if (flattened) { ngates = ray_n_gates[rayStartIdx[i]]; for (int ray = rayStartIdx[i]; ray <= rayEndIdx[i]; ++ray) ngates = ray_n_gates[ray] > ngates ? ray_n_gates[ray] : ngates; } sweeps.add(new CFRadial2Sweep(v0, i, ngates, rayStartIdx[i], rayEndIdx[i])); } } /** * _more_ * * @return _more_ */ public String toString() { return name; } /** * _more_ * * @return _more_ */ public int getNumSweeps() { return nsweeps; } /** * _more_ * * @param sweepNo _more_ * @return _more_ */ public Sweep getSweep(int sweepNo) { return sweeps.get(sweepNo); } /** * _more_ * * @return _more_ */ public int getNumRadials() { return azimuth.length; } // a 3D array nsweep * nradials * ngates // if high resolution data, it will be transferred to the same dimension /** * _more_ * * @return _more_ * @throws IOException _more_ */ public float[] readAllData() throws IOException { Array allData; Sweep spn = sweeps.get(0); Variable v = spn.getsweepVar(); Attribute missing = v.findAttribute("_FillValue"); float missingVal = missing == null ? Float.NaN : missing.getNumericValue().floatValue(); int minRadial = getMinRadialNumber(); int radials = getNumRadials(); int gates = range.length; try { allData = v.read(); } catch (IOException e) { throw new IOException(e.getMessage()); } if (flattened) { float[] fa0 = (float[]) allData.get1DJavaArray(float.class); float[] fa = new float[minRadial * gates * nsweeps]; Arrays.fill(fa, missingVal); for (int s = 0; s < nsweeps; ++s) { for (int r = 0; r < minRadial; ++r) { System.arraycopy(fa0, ray_start_index[rayStartIdx[s] + r], fa, s * minRadial * gates + r * gates, ray_n_gates[rayStartIdx[s] + r]); } } return fa; } else if (minRadial == radials) { return (float[]) allData.get1DJavaArray(float.class); } else { float[] fa = new float[minRadial * gates * nsweeps]; float[] fa0 = (float[]) allData.get1DJavaArray(float.class); int pos = 0; for (int i = 0; i < nsweeps; i++) { int startIdx = rayStartIdx[i]; // int endIdx = rayEndIdx[i]; int len = minRadial * gates; System.arraycopy(fa0, startIdx * gates, fa, pos, len); pos = pos + len; } return fa; } } public int getMinRadialNumber() { int minRadialNumber = Integer.MAX_VALUE; for (int i = 0; i < nsweeps; i++) { Sweep swp = this.sweeps.get(i); int radialNumber = swp.getRadialNumber(); if (radialNumber < minRadialNumber) { minRadialNumber = radialNumber; } } return minRadialNumber; } /** * _more_ */ public void clearVariableMemory() { for (int i = 0; i < nsweeps; i++) { } } ////////////////////////////////////////////////////////////////////// // Checking all azi to make sure there is no missing data at sweep // level, since the coordinate is 1D at this level, this checking also // remove those missing radials within a sweep. /** * Class description * * @author Enter your name here... * @version Enter version here..., Mon, Jun 13, '11 */ private class CFRadial2Sweep implements RadialDatasetSweep.Sweep { /** * _more_ */ double meanElevation = Double.NaN; /** * _more_ */ double meanAzimuth = Double.NaN; /** * _more_ */ int ngates; /** * _more_ */ public int startIdx, endIdx, numRays; /** * _more_ */ int sweepno; /** * _more_ */ Variable sweepVar; /** * _more_ * * @param v _more_ * @param sweepno _more_ * @param gates _more_ * @param startIdx _more_ * @param endIdx _more_ */ CFRadial2Sweep(Variable v, int sweepno, int gates, int startIdx, int endIdx) { this.sweepVar = v; this.sweepno = sweepno; this.ngates = gates; this.startIdx = startIdx; this.endIdx = endIdx; this.numRays = endIdx - startIdx + 1; } public int getStartIdx() { return startIdx; } public int getEndIdx() { return endIdx; } /** * _more_ * * @return _more_ */ public Variable getsweepVar() { return sweepVar; } /* read 2d sweep data nradials * ngates */ /** * _more_ * * @return _more_ * @throws java.io.IOException _more_ */ public float[] readData() throws java.io.IOException { return sweepData(); } /** * _more_ * * @return _more_ */ private float[] sweepData() throws IOException { int[] origin; int[] shape; // init section try { if (flattened) { // Get the 1D data for the sweep origin = new int[1]; origin[0] = ray_start_index[startIdx]; shape = new int[1]; shape[0] = ray_start_index[endIdx] + ray_n_gates[endIdx] - origin[0]; Array tempArray = sweepVar.read(origin, shape).reduce(); float[] tempD = (float[]) tempArray.get1DJavaArray(Float.TYPE); // Figure out what to use as the initializer float missingVal = Float.NaN; Attribute missing = sweepVar.findAttribute("_FillValue"); if (missing != null) missingVal = missing.getNumericValue().floatValue(); // Create evenly strided output array and fill float[] ret = new float[ngates * numRays]; Arrays.fill(ret, missingVal); int srcInd = 0; for (int ray = 0; ray < numRays; ++ray) { int gates = ray_n_gates[startIdx + ray]; System.arraycopy(tempD, srcInd, ret, ray * ngates, gates); srcInd += gates; } return ret; } else { origin = new int[2]; origin[0] = startIdx; shape = sweepVar.getShape(); shape[0] = numRays; Array sweepTmp = sweepVar.read(origin, shape).reduce(); return (float[]) sweepTmp.get1DJavaArray(Float.TYPE); } } catch (ucar.ma2.InvalidRangeException e) { throw new IOException(e); } } /** * Return data for 1 ray * * @param ray _more_ * @return _more_ * @throws java.io.IOException _more_ */ public float[] readData(int ray) throws java.io.IOException { return rayData(ray); } /* read the radial data from the radial variable */ /** * _more_ * * @param ray _more_ * @return _more_ * @throws java.io.IOException _more_ */ public float[] rayData(int ray) throws java.io.IOException { int[] origin; int[] shape; // init section if (flattened) { origin = new int[1]; origin[0] = ray_start_index[startIdx + ray]; shape = new int[1]; shape[0] = ray_n_gates[startIdx + ray]; } else { origin = new int[2]; origin[0] = startIdx + ray; shape = sweepVar.getShape(); shape[0] = 1; } try { Array sweepTmp = sweepVar.read(origin, shape).reduce(); return (float[]) sweepTmp.get1DJavaArray(Float.TYPE); } catch (ucar.ma2.InvalidRangeException e) { throw new IOException(e); } } /** * _more_ */ public void setMeanElevation() { double sum = 0.0; int sumSize = 0; for (int i = 0; i < numRays; i++) { if (!Double.isNaN(elevation[i])) { sum = sum + elevation[startIdx + i]; sumSize++; } } if (sumSize > 0) meanElevation = sum / sumSize; } /** * _more_ * * @return _more_ */ public float getMeanElevation() { if (Double.isNaN(meanElevation)) { setMeanElevation(); } return (float) meanElevation; } /** * _more_ * * @return _more_ */ public int getGateNumber() { return ngates; } /** * _more_ * * @return _more_ */ public int getRadialNumber() { return numRays; } /** * _more_ * * @return _more_ */ public RadialDatasetSweep.Type getType() { return null; } /** * _more_ * * @param ray _more_ * @return _more_ */ public ucar.unidata.geoloc.EarthLocation getOrigin(int ray) { return origin; } /** * _more_ * * @return _more_ */ public Date getStartingTime() { return startDate; } /** * _more_ * * @return _more_ */ public Date getEndingTime() { return endDate; } /** * _more_ * * @return _more_ */ public int getSweepIndex() { return sweepno; } /** * _more_ */ public void setMeanAzimuth() { double sum = 0.0; int sumSize = 0; for (int i = 0; i < numRays; i++) { if (!Double.isNaN(azimuth[i])) { sum = sum + azimuth[startIdx + i]; sumSize++; } } if (sumSize > 0) meanAzimuth = sum / sumSize; } /** * _more_ * * @return _more_ */ public float getMeanAzimuth() { if (Double.isNaN(meanAzimuth)) { setMeanAzimuth(); } return (float) meanAzimuth; } /** * _more_ * * @return _more_ */ public boolean isConic() { return true; } /** * _more_ * * @param ray _more_ * @return _more_ * @throws IOException _more_ */ public float getElevation(int ray) throws IOException { return elevation[ray + startIdx]; } /** * _more_ * * @return _more_ * @throws IOException _more_ */ public float[] getElevation() throws IOException { float[] elev = new float[numRays]; System.arraycopy(elevation, startIdx, elev, 0, numRays); return elev; } /** * _more_ * * @return _more_ * @throws IOException _more_ */ public float[] getAzimuth() throws IOException { float[] azimu = new float[numRays]; System.arraycopy(azimuth, startIdx, azimu, 0, numRays); return azimu; } /** * _more_ * * @param ray _more_ * @return _more_ * @throws IOException _more_ */ public float getAzimuth(int ray) throws IOException { return azimuth[ray + startIdx]; } /** * _more_ * * @param gate _more_ * @return _more_ * @throws IOException _more_ */ public float getRadialDistance(int gate) throws IOException { return range[gate]; } /** * _more_ * * @param ray _more_ * @return _more_ * @throws IOException _more_ */ public float getTime(int ray) throws IOException { return (float) time[ray + startIdx]; } /** * _more_ * * @return _more_ */ public float getBeamWidth() { return 0.95f; // degrees, info from Chris Burkhart } /** * _more_ * * @return _more_ */ public float getNyquistFrequency() { return 0; // LOOK this may be radial specific } /** * _more_ * * @return _more_ */ public float getRangeToFirstGate() { try { return getRadialDistance(0); } catch (IOException e) { e.printStackTrace(); return 0.0f; } } /** * _more_ * * @return _more_ */ public float getGateSize() { try { return getRadialDistance(1) - getRadialDistance(0); } catch (IOException e) { e.printStackTrace(); return 0.0f; } } /** * _more_ * * @return _more_ */ public boolean isGateSizeConstant() { return true; } /** * _more_ */ public void clearSweepMemory() { } } // LevelII2Sweep class } // LevelII2Variable }