diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/IGridDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/IGridDataInfo.java index d5f7731e..874bc436 100644 --- a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/IGridDataInfo.java +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/IGridDataInfo.java @@ -96,7 +96,7 @@ import org.meteoinfo.data.GridData; /** * Read grid data - time * - * @param lonIdx Lontitude index + * @param lonIdx Longitude index * @param latIdx Latitude index * @param varName Variable name * @param levelIdx Level index @@ -107,7 +107,7 @@ import org.meteoinfo.data.GridData; /** * Read grid data - level * - * @param lonIdx Lontitude index + * @param lonIdx Longitude index * @param latIdx Latitude index * @param varName Variable name * @param timeIdx Time index @@ -130,7 +130,7 @@ import org.meteoinfo.data.GridData; * Read grid data - latitude * * @param timeIdx Time index - * @param lonIdx Lontitude index + * @param lonIdx Longitude index * @param varName Variable name * @param levelIdx Level index * @return Grid data diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/MeteoDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/MeteoDataInfo.java index 8c78c653..d6883c84 100644 --- a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/MeteoDataInfo.java +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/MeteoDataInfo.java @@ -41,6 +41,9 @@ import java.util.ArrayList; import org.meteoinfo.data.meteodata.numpy.NumpyDataInfo; import org.meteoinfo.data.meteodata.radar.CMARadarBaseDataInfo; +import org.meteoinfo.data.meteodata.radar.RadarDataType; +import org.meteoinfo.data.meteodata.radar.RadarDataUtil; +import org.meteoinfo.data.meteodata.radar.cinrad.CinradDataInfo; import org.meteoinfo.ndarray.math.ArrayMath; import org.meteoinfo.projection.ProjectionInfo; import java.util.List; @@ -471,31 +474,31 @@ public class MeteoDataInfo { * @return Data info */ public DataInfo getDataInfo(String fileName) { - DataInfo di = null; - try { - RandomAccessFile raf = new RandomAccessFile(fileName, "r"); - if (GrADSDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) { - di = new GrADSDataInfo(); - } else if (NetcdfFiles.canOpen(fileName)) { - di = new NetCDFDataInfo(); - } else if (ARLDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) { - di = new ARLDataInfo(); - } else if (CMARadarBaseDataInfo.canOpen(fileName)) { - di = new CMARadarBaseDataInfo(); - } else if (MatLabDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) { - di = new MatLabDataInfo(); - } else { - di = MICAPSDataInfo.getDataInfo(raf); + DataInfo di = RadarDataUtil.getDataInfo(fileName); + if (di == null) { + try { + RandomAccessFile raf = new RandomAccessFile(fileName, "r"); + if (GrADSDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) { + di = new GrADSDataInfo(); + } else if (NetcdfFiles.canOpen(fileName)) { + di = new NetCDFDataInfo(); + } else if (ARLDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) { + di = new ARLDataInfo(); + } else if (MatLabDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) { + di = new MatLabDataInfo(); + } else { + di = MICAPSDataInfo.getDataInfo(raf); + } + raf.close(); + } catch (IOException | NoSuchMethodException ex) { + return null; + } catch (InvocationTargetException e) { + return null; + } catch (InstantiationException e) { + return null; + } catch (IllegalAccessException e) { + return null; } - raf.close(); - } catch (IOException | NoSuchMethodException ex) { - return null; - } catch (InvocationTargetException e) { - return null; - } catch (InstantiationException e) { - return null; - } catch (IllegalAccessException e) { - return null; } return di; diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/BaseRadarDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/BaseRadarDataInfo.java new file mode 100644 index 00000000..39e80485 --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/BaseRadarDataInfo.java @@ -0,0 +1,546 @@ +package org.meteoinfo.data.meteodata.radar; + +import org.meteoinfo.data.dimarray.Dimension; +import org.meteoinfo.data.meteodata.Attribute; +import org.meteoinfo.data.meteodata.DataInfo; +import org.meteoinfo.data.meteodata.Variable; +import org.meteoinfo.ndarray.*; +import org.meteoinfo.ndarray.math.ArrayMath; +import org.meteoinfo.ndarray.math.ArrayUtil; + +import java.io.RandomAccessFile; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class BaseRadarDataInfo extends DataInfo { + + protected final Map productMap = Stream.of(new Object[][]{{1,"dBT"}, {2,"dBZ"}, + {3,"V"}, {4,"W"}, {5,"SQI"}, {6,"CPA"}, {7,"ZDR"}, {8,"LDR"}, {9,"CC"}, {10,"PhiDP"}, + {11,"KDP"}, {12,"CP"}, {13,"Flag"}, {14,"HCL"}, {15,"CF"}, {16,"SNRH"}, {17,"SNRV"}, + {18,"Flag"}, {19,"Flag"}, {20,"Flag"}, {21,"Flag"}, {22,"Flag"}, {23,"Flag"}, + {24,"Flag"}, {25,"Flag"}, {26,"Flag"}, {27,"Flag"}, {28,"Flag"}, {29,"Flag"}, + {30,"Flag"}, {31,"Flag"}, {32,"Zc"}, {33,"Vc"}, {34,"Wc"}, {35,"ZDRc"}, {0,"Flag"} + }).collect(Collectors.toMap(data -> (Integer) data[0], data -> (String) data[1])); + protected final Map recordMap = new HashMap<>(); + protected final List velocityGroup = new ArrayList<>(Arrays.asList("V", "W")); + protected Dimension radialDim, scanDim, gateRDim, gateVDim; + protected float antennaHeight = 0; + protected float beamWidthVert = 1.f; + protected int logResolution = 1000; + protected int dopplerResolution = 1000; + + /** + * Get record map + * @return Record map + */ + public Map getRecordMap() { + return this.recordMap; + } + + /** + * Is a radial record is in velocity group or not + * @param record The radial record + * @return Velocity group or not + */ + public boolean isVelocityGroup(RadialRecord record) { + return velocityGroup.contains(record.product); + } + + /** + * Is a product is in velocity group or not + * @param product The product name + * @return Velocity group or not + */ + public boolean isVelocityGroup(String product) { + return velocityGroup.contains(product); + } + + protected void makeRefVariables(RadialRecord refRadialRecord) { + Dimension[] dimensions = new Dimension[]{scanDim, radialDim, gateRDim}; + for (RadialRecord radialRecord : this.recordMap.values()) { + if (!radialRecord.isVelocityGroup()) + radialRecord.makeVariable(this, dimensions); + } + + //coordinate variables + Variable elevation = new Variable(); + elevation.setName("elevationR"); + elevation.setDataType(DataType.FLOAT); + elevation.addDimension(scanDim); + elevation.addDimension(radialDim); + elevation.addAttribute(new Attribute("units", "degree")); + elevation.addAttribute(new Attribute("long_name", "elevation angle in degrees")); + this.addVariable(elevation); + + Variable azimuth = new Variable(); + azimuth.setName("azimuthR"); + azimuth.setDataType(DataType.FLOAT); + azimuth.addDimension(scanDim); + azimuth.addDimension(radialDim); + azimuth.addAttribute(new Attribute("units", "degree")); + azimuth.addAttribute(new Attribute("long_name", "azimuth angle in degrees")); + this.addVariable(azimuth); + + Variable distance = new Variable(); + distance.setName("distanceR"); + distance.setDataType(DataType.FLOAT); + distance.addDimension(gateRDim); + distance.addAttribute(new Attribute("units", "m")); + distance.addAttribute(new Attribute("long_name", "radial distance to start of gate")); + this.addVariable(distance); + + Variable nRadials = new Variable(); + nRadials.setName("numRadialsR"); + nRadials.setDataType(DataType.INT); + nRadials.addDimension(scanDim); + nRadials.addAttribute(new Attribute("long_name", "number of valid radials in this scan")); + this.addVariable(nRadials); + + Variable nGates = new Variable(); + nGates.setName("numGatesR"); + nGates.setDataType(DataType.INT); + nGates.addDimension(scanDim); + nGates.addAttribute(new Attribute("long_name", "number of valid gates in this scan")); + this.addVariable(nGates); + + int nScan = scanDim.getLength(); + int nRadial = radialDim.getLength(); + int nGate = gateRDim.getLength(); + Array elevData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); + Array aziData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); + Array nRData = Array.factory(DataType.INT, new int[]{nScan}); + Array nGData = Array.factory(DataType.INT, new int[]{nScan}); + Index elevIndex = elevData.getIndex(); + Index aziIndex = aziData.getIndex(); + for (int i = 0; i < nScan; i++) { + List elevList = refRadialRecord.elevation.get(i); + List aziList = refRadialRecord.azimuth.get(i); + nRData.setInt(i, aziList.size()); + nGData.setInt(i, (int) refRadialRecord.distance.get(i).getSize()); + for (int j = 0; j < nRadial; j++) { + if (j < elevList.size()) { + elevData.setFloat(elevIndex.set(i, j), elevList.get(j)); + aziData.setFloat(aziIndex.set(i, j), aziList.get(j)); + } else { + elevData.setFloat(elevIndex.set(i, j), Float.NaN); + aziData.setFloat(aziIndex.set(i, j), Float.NaN); + } + } + } + Array disData = refRadialRecord.distance.get(0); + + elevation.setCachedData(elevData); + azimuth.setCachedData(aziData); + distance.setCachedData(disData); + nRadials.setCachedData(nRData); + nGates.setCachedData(nGData); + } + + protected void makeVelVariables(RadialRecord velRadialRecord) { + Dimension[] dimensions = new Dimension[]{scanDim, radialDim, gateVDim}; + for (RadialRecord radialRecord : this.recordMap.values()) { + if (radialRecord.isVelocityGroup()) + radialRecord.makeVariable(this, dimensions); + } + + //coordinate variables + Variable elevation = new Variable(); + elevation.setName("elevationV"); + elevation.setDataType(DataType.FLOAT); + elevation.addDimension(scanDim); + elevation.addDimension(radialDim); + elevation.addAttribute(new Attribute("units", "degree")); + elevation.addAttribute(new Attribute("long_name", "elevation angle in degrees")); + this.addVariable(elevation); + + Variable azimuth = new Variable(); + azimuth.setName("azimuthV"); + azimuth.setDataType(DataType.FLOAT); + azimuth.addDimension(scanDim); + azimuth.addDimension(radialDim); + azimuth.addAttribute(new Attribute("units", "degree")); + azimuth.addAttribute(new Attribute("long_name", "azimuth angle in degrees")); + this.addVariable(azimuth); + + Variable distance = new Variable(); + distance.setName("distanceV"); + distance.setDataType(DataType.FLOAT); + distance.addDimension(gateVDim); + distance.addAttribute(new Attribute("units", "m")); + distance.addAttribute(new Attribute("long_name", "radial distance to start of gate")); + this.addVariable(distance); + + Variable nRadials = new Variable(); + nRadials.setName("numRadialsR"); + nRadials.setDataType(DataType.INT); + nRadials.addDimension(scanDim); + nRadials.addAttribute(new Attribute("long_name", "number of valid radials in this scan")); + this.addVariable(nRadials); + + Variable nGates = new Variable(); + nGates.setName("numGatesR"); + nGates.setDataType(DataType.INT); + nGates.addDimension(scanDim); + nGates.addAttribute(new Attribute("long_name", "number of valid gates in this scan")); + this.addVariable(nGates); + + int nScan = scanDim.getLength(); + int nRadial = radialDim.getLength(); + int nGate = gateVDim.getLength(); + Array elevData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); + Array aziData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); + Array nRData = Array.factory(DataType.INT, new int[]{nScan}); + Array nGData = Array.factory(DataType.INT, new int[]{nScan}); + Index elevIndex = elevData.getIndex(); + Index aziIndex = aziData.getIndex(); + for (int i = 0; i < nScan; i++) { + List elevList = velRadialRecord.elevation.get(i); + List aziList = velRadialRecord.azimuth.get(i); + nRData.setInt(i, aziList.size()); + nGData.setInt(i, (int) velRadialRecord.distance.get(i).getSize()); + for (int j = 0; j < nRadial; j++) { + if (j < elevList.size()) { + elevData.setFloat(elevIndex.set(i, j), elevList.get(j)); + aziData.setFloat(aziIndex.set(i, j), aziList.get(j)); + } else { + elevData.setFloat(elevIndex.set(i, j), Float.NaN); + aziData.setFloat(aziIndex.set(i, j), Float.NaN); + } + } + } + Array disData = velRadialRecord.distance.get(0); + + elevation.setCachedData(elevData); + azimuth.setCachedData(aziData); + distance.setCachedData(disData); + nRadials.setCachedData(nRData); + nGates.setCachedData(nGData); + } + + /** + * Get product names + * @return product names + */ + public List getProducts() { + List products = new ArrayList<>(); + for (String product : this.recordMap.keySet()) { + products.add(product); + } + + return products; + } + + /** + * Get scan elevations + * @return Scan elevations + */ + public List getElevations(String product) { + RadialRecord radialRecord = this.recordMap.get(product); + + return radialRecord.fixedElevation; + } + + @Override + public Array read(String varName) { + Variable var = this.getVariable(varName); + int n = var.getDimNumber(); + int[] origin = new int[n]; + int[] size = new int[n]; + int[] stride = new int[n]; + for (int i = 0; i < n; i++) { + origin[i] = 0; + size[i] = var.getDimLength(i); + stride[i] = 1; + } + + Array r = read(varName, origin, size, stride); + + return r; + } + + @Override + public Array read(String varName, int[] origin, int[] size, int[] stride) { + try { + Variable variable = this.getVariable(varName); + if (variable.hasCachedData()) { + return variable.getCachedData().section(origin, size, stride).copy(); + } + + Section section = new Section(origin, size, stride); + RadialRecord record = this.recordMap.get(varName); + Array dataArray = Array.factory(DataType.FLOAT, section.getShape()); + Range zRange = section.getRange(0); + Range yRange = section.getRange(1); + Range xRange = section.getRange(2); + IndexIterator iter = dataArray.getIndexIterator(); + for (int s = zRange.first(); s <= zRange.last(); s += zRange.stride()) { + List arrays = record.getDataArray(s); + for (int i = yRange.first(); i <= yRange.last(); i += yRange.stride()) { + if (i < arrays.size()) { + Array array = arrays.get(i); + for (int j = xRange.first(); j <= xRange.last(); j += xRange.stride()) { + if (j < array.getSize()) + iter.setFloatNext(array.getFloat(j)); + else + iter.setFloatNext(Float.NaN); + } + } else { + for (int j = xRange.first(); j <= xRange.last(); j += xRange.stride()) { + iter.setFloatNext(Float.NaN); + } + } + } + } + + Attribute aoAttr = variable.findAttribute("add_offset"); + Attribute sfAttr = variable.findAttribute("scale_factor"); + if (aoAttr != null || sfAttr != null) { + Number add_offset = 0.f; + Number scale_factor = 1.f; + if (aoAttr != null) { + switch (aoAttr.getDataType()) { + case DOUBLE: + add_offset = aoAttr.getValues().getDouble(0); + break; + case FLOAT: + case INT: + add_offset = aoAttr.getValues().getFloat(0); + break; + } + } + if (sfAttr != null) { + switch (sfAttr.getDataType()) { + case DOUBLE: + scale_factor = sfAttr.getValues().getDouble(0); + break; + case FLOAT: + case INT: + scale_factor = sfAttr.getValues().getFloat(0); + break; + } + } + dataArray = ArrayMath.add(ArrayMath.mul(dataArray, scale_factor), add_offset); + } + + return dataArray; + } catch (InvalidRangeException e) { + return null; + } + } + + @Override + public List getGlobalAttributes() { + return this.attributes; + } + + /** + * Read grid ppi data + * @param varName Variable name + * @param scanIdx Scan index + * @param xa X coordinates array + * @param ya Y coordinates array + * @param h Radar height + * @return Grid ppi data + */ + public Array readGridData(String varName, int scanIdx, Array xa, Array ya, Float h) { + RadialRecord record = this.recordMap.get(varName); + if (h == null) { + h = antennaHeight; + } + Array[] rr = Transform.cartesianToAntennaElevation(xa, ya, record.fixedElevation.get(scanIdx), h); + Array azimuth = rr[0]; + Array ranges = rr[1]; + Array data = Array.factory(DataType.FLOAT, xa.getShape()); + IndexIterator iterA = azimuth.getIndexIterator(); + IndexIterator iterR = ranges.getIndexIterator(); + IndexIterator iterData = data.getIndexIterator(); + float v; + while (iterData.hasNext()) { + v = record.interpolateValue(scanIdx, iterA.getFloatNext(), iterR.getFloatNext()); + iterData.setFloatNext(v); + } + + return data; + } + + /** + * Read CR data + * @param varName Variable name + * @param xa X coordinates array - 2D + * @param ya Y coordinates array - 2D + * @param h Radar height + * @return CR data + */ + public Array getCRData(String varName, Array xa, Array ya, Float h) { + RadialRecord record = this.recordMap.get(varName); + int nScan = record.getScanNumber(); + if (h == null) { + h = antennaHeight; + } + + int[] shape = xa.getShape(); + int ny = shape[0]; + int nx = shape[1]; + Array data = Array.factory(DataType.FLOAT, shape); + Index2D index2D = (Index2D) data.getIndex(); + float v; + for (int s = 0; s < nScan; s++) { + Array[] rr = Transform.cartesianToAntennaElevation(xa, ya, record.fixedElevation.get(s), h); + Array azimuth = rr[0]; + Array ranges = rr[1]; + IndexIterator iterA = azimuth.getIndexIterator(); + IndexIterator iterR = ranges.getIndexIterator(); + if (s == 0) { + for (int i = 0; i < ny; i++) { + for (int j = 0; j < nx; j++) { + v = record.interpolateValue(s, iterA.getFloatNext(), iterR.getFloatNext()); + data.setFloat(index2D.set(i, j), v); + } + } + } else { + float v1; + for (int i = 0; i < ny; i++) { + for (int j = 0; j < nx; j++) { + v = record.interpolateValue(s, iterA.getFloatNext(), iterR.getFloatNext()); + index2D.set(i, j); + v1 = data.getFloat(index2D); + if (Float.isNaN(v1) || (v > v1)) + data.setFloat(index2D, v); + } + } + } + } + + return data; + } + + /** + * Read CAPPI data + * @param varName Variable name + * @param xa X coordinates array + * @param ya Y coordinates array + * @param z Z coordinates value + * @param h Radar height + * @return Grid ppi data + */ + public Array getCAPPIData(String varName, Array xa, Array ya, float z, Float h) { + RadialRecord record = this.recordMap.get(varName); + if (h == null) { + h = antennaHeight; + } + Array[] rr = Transform.cartesianToAntenna(xa, ya, z, h); + Array azimuth = rr[0]; + Array ranges = rr[1]; + Array elevation = rr[2]; + Array data = Array.factory(DataType.FLOAT, xa.getShape()); + IndexIterator iterA = azimuth.getIndexIterator(); + IndexIterator iterR = ranges.getIndexIterator(); + IndexIterator iterE = elevation.getIndexIterator(); + IndexIterator iterData = data.getIndexIterator(); + float v; + float halfBeamWidth = beamWidthVert / 2; + while (iterData.hasNext()) { + v = record.interpolateValue(iterE.getFloatNext(), iterA.getFloatNext(), + iterR.getFloatNext(), halfBeamWidth); + iterData.setFloatNext(v); + } + + return data; + } + + /** + * Read grid 3d data + * @param varName Variable name + * @param xa X coordinates array + * @param ya Y coordinates array + * @param z Z coordinates array + * @param h Radar height + * @return Grid ppi data + */ + public Array getGrid3DData(String varName, Array xa, Array ya, Array za, Float h) { + RadialRecord record = this.recordMap.get(varName); + if (h == null) { + h = antennaHeight; + } + + int nz = (int) za.getSize(); + int[] shape2D = xa.getShape(); + int[] shape3D = new int[]{nz, shape2D[0], shape2D[1]}; + Array data = Array.factory(DataType.FLOAT, shape3D); + IndexIterator iterData = data.getIndexIterator(); + IndexIterator iterZ = za.getIndexIterator(); + float halfBeamWidth = beamWidthVert / 2; + while(iterZ.hasNext()) { + float z = iterZ.getFloatNext(); + Array[] rr = Transform.cartesianToAntenna(xa, ya, z, h); + Array azimuth = rr[0]; + Array ranges = rr[1]; + Array elevation = rr[2]; + IndexIterator iterA = azimuth.getIndexIterator(); + IndexIterator iterR = ranges.getIndexIterator(); + IndexIterator iterE = elevation.getIndexIterator(); + float v; + while (iterA.hasNext()) { + v = record.interpolateValue(iterE.getFloatNext(), iterA.getFloatNext(), + iterR.getFloatNext(), halfBeamWidth); + iterData.setFloatNext(v); + } + } + + return data; + } + + /** + * Get VCS data + * @param varName Variable name + * @param startX Start x, km + * @param startY Start y, km + * @param endX End x, km + * @param endY End y, km + * @return VCS data + */ + public Array[] getVCSData(String varName, float startX, float startY, float endX, float endY) { + RadialRecord record = this.recordMap.get(varName); + int nScan = record.getScanNumber(); + float halfBeamWidth = this.beamWidthVert / 2; + float binRes = isVelocityGroup(varName) ? this.dopplerResolution : this.logResolution; + float height = this.antennaHeight; + float startEndDistance = (float) Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)); + int nPoints = (int) (startEndDistance * 1000 / binRes + 1); + Array xa = ArrayUtil.lineSpace(startX, endX, nPoints); + Array ya = ArrayUtil.lineSpace(startY, endY, nPoints); + Array aa = Transform.xyToAzimuth(xa, ya); + int[] shape = new int[]{nScan, 2, nPoints}; + Array data = Array.factory(DataType.FLOAT, shape); + Array meshXY = Array.factory(DataType.FLOAT, shape); + Array meshZ = Array.factory(DataType.FLOAT, shape); + Index dataIndex = data.getIndex(); + Index meshXYIndex = meshXY.getIndex(); + Index meshZIndex = meshZ.getIndex(); + float x, y, z1, z2, dis, azi, v, ele; + for (int i = 0; i < nScan; i++) { + ele = record.fixedElevation.get(i); + for (int j = 0; j < nPoints; j++) { + x = xa.getFloat(j); + y = ya.getFloat(j); + dis = (float) Math.sqrt(x * x + y * y); + azi = aa.getFloat(j); + v = record.getValue(i, azi, dis * 1000); + z1 = Transform.toCartesianZ(dis * 1000, (float) Math.toRadians(ele - + halfBeamWidth), height) / 1000.f; + z2 = Transform.toCartesianZ(dis * 1000, (float) Math.toRadians(ele + + halfBeamWidth), height) / 1000.f; + for (int k = 0; k < 2; k++) { + data.setFloat(dataIndex.set(i, k, j), v); + meshXY.setFloat(meshXYIndex.set(i, k, j), dis); + } + meshZ.setFloat(meshZIndex.set(i, 0, j), z1); + meshZ.setFloat(meshZIndex.set(i, 1, j), z2); + } + } + + return new Array[]{data, meshXY, meshZ}; + } + +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CCRadarDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CCRadarDataInfo.java new file mode 100644 index 00000000..18ce9b5a --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CCRadarDataInfo.java @@ -0,0 +1,275 @@ +package org.meteoinfo.data.meteodata.radar; + +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; +import org.meteoinfo.data.dimarray.Dimension; +import org.meteoinfo.data.meteodata.Attribute; +import org.meteoinfo.data.meteodata.DataInfo; +import org.meteoinfo.ndarray.Array; +import org.meteoinfo.ndarray.DataType; +import org.meteoinfo.ndarray.math.ArrayUtil; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class CCRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo { + + @Override + public boolean isValidFile(java.io.RandomAccessFile raf) { + return false; + } + + void setScaleOffset(RadialRecord record, int vResolution) { + switch (record.product) { + case "dBZ": + record.scale = 0.5f; + record.offset = -33.f; + break; + case "V": + if (vResolution == 2) { + record.scale = 0.5f; + record.offset = -64.5f; + } else { + record.scale = 1.f; + record.offset = -129.f; + } + break; + case "W": + record.scale = 0.5f; + record.offset = -64.5f; + break; + } + } + + @Override + public void readDataInfo(String fileName) { + this.fileName = fileName; + if (fileName.endsWith(".bz2")) { + try { + BZip2CompressorInputStream inputStream = new BZip2CompressorInputStream(Files.newInputStream(Paths.get(fileName))); + readDataInfo(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + try { + BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(fileName))); + readDataInfo(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + void readDataInfo(InputStream is) { + try { + int index = 0; + byte[] rhBytes = new byte[SABRadarDataInfo.RadialHeader.length]; + while (is.read(rhBytes) != -1) { + SABRadarDataInfo.RadialHeader radialHeader = new SABRadarDataInfo.RadialHeader(rhBytes); + if (index == 0) { + this.logResolution = radialHeader.gateSizeOfReflectivity; + this.dopplerResolution = radialHeader.gateSizeOfDoppler; + } + if (!radialHeader.hasReflectivityData()) { + is.read(new byte[460]); + } + for (String product : radialHeader.getProducts()) { + RadialRecord record; + if (this.recordMap.containsKey(product)) { + record = this.recordMap.get(product); + } else { + record = new RadialRecord(product); + record.setBinLength(1); + setScaleOffset(record, radialHeader.resolutionOfVelocity); + this.recordMap.put(product, record); + } + if (radialHeader.radialNumber == 1) { + record.fixedElevation.add(radialHeader.getElevation()); + record.elevation.add(new ArrayList<>()); + record.azimuth.add(new ArrayList<>()); + record.azimuthMinIndex.add(0); + if (isVelocityGroup(record)) { + record.disResolution.add(radialHeader.gateSizeOfDoppler); + record.distance.add(ArrayUtil.arrayRange1(radialHeader.rangeToFirstGateOfDop, + radialHeader.gatesNumberOfDoppler, radialHeader.gateSizeOfDoppler)); + } else { + record.disResolution.add(radialHeader.gateSizeOfReflectivity); + record.distance.add(ArrayUtil.arrayRange1(radialHeader.rangeToFirstGateOfRef, + radialHeader.gatesNumberOfReflectivity, radialHeader.gateSizeOfReflectivity)); + } + record.newScanData(); + } + record.elevation.get(record.elevation.size() - 1).add(radialHeader.getElevation()); + record.addAzimuth(radialHeader.getAzimuth()); + int dataLength = isVelocityGroup(record) ? radialHeader.gatesNumberOfDoppler : radialHeader.gatesNumberOfReflectivity; + byte[] bytes = new byte[dataLength]; + is.read(bytes); + record.addDataBytes(bytes); + if (isVelocityGroup(record)) { + if (dataLength < 920) { + is.read(new byte[920 - dataLength]); + } + } else { + if (dataLength < 460) { + is.read(new byte[460 - dataLength]); + } + } + } + if (!radialHeader.hasDopplerData()) { + is.read(new byte[920 + 920]); + } + is.read(new byte[4]); + index += 1; + } + is.close(); + + this.addAttribute(new Attribute("featureType", "RADIAL")); + this.addAttribute(new Attribute("DataType", "Radial")); + + //Add dimensions and variables + RadialRecord refRadialRecord = this.recordMap.get("dBZ"); + radialDim = new Dimension(); + radialDim.setName("radial"); + radialDim.setLength(refRadialRecord.getMaxRadials()); + this.addDimension(radialDim); + scanDim = new Dimension(); + scanDim.setName("scan"); + scanDim.setLength(refRadialRecord.getScanNumber()); + this.addDimension(scanDim); + gateRDim = new Dimension(); + gateRDim.setName("gateR"); + gateRDim.setLength(refRadialRecord.getGateNumber(0)); + this.addDimension(gateRDim); + makeRefVariables(refRadialRecord); + + RadialRecord velRadialRecord = this.recordMap.get("V"); + gateVDim = new Dimension(); + gateVDim.setName("gateV"); + gateVDim.setLength(velRadialRecord.getGateNumber(0)); + this.addDimension(gateVDim); + makeVelVariables(velRadialRecord); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public RadarDataType getRadarDataType() { + return RadarDataType.SAB; + } + + static class RadialHeader { + public static int length = 128; + public int mSecond; // collection time for this radial, msecs since midnight + public short julianDate; // prob "collection time" + public short uRange; // unambiguous range + public int azimuth; // azimuth angle + public short radialNumber; // radial number within the elevation + public short radialStatus; + public short elevation; + public short elNumber; // elevation number + public int rangeToFirstGateOfRef; // range to first gate of reflectivity (m) may be negative + public int rangeToFirstGateOfDop; // range to first gate of doppler (m) may be negative + public int gateSizeOfReflectivity; // reflectivity data gate size (m) + public int gateSizeOfDoppler; // doppler data gate size (m) + public int gatesNumberOfReflectivity; // number of reflectivity gates + public int gatesNumberOfDoppler; // number of velocity or spectrum width gates + public short cutSectorNumber; + public int calibrationConst; + public short ptrOfReflectivity; + public short ptrOfVelocity; + public short ptrOfSpectrumWidth; + public int resolutionOfVelocity; + public short vcpNumber; + public short nyquist; + + /** + * Constructor + * @param is InputStream + */ + public RadialHeader(byte[] inBytes) throws IOException { + ByteBuffer byteBuffer = ByteBuffer.wrap(inBytes); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.position(28); + mSecond = byteBuffer.getInt(); + julianDate = byteBuffer.getShort(); + uRange = byteBuffer.getShort(); + azimuth = DataType.unsignedShortToInt(byteBuffer.getShort()); + radialNumber = byteBuffer.getShort(); + radialStatus = byteBuffer.getShort(); + elevation = byteBuffer.getShort(); + elNumber = byteBuffer.getShort(); + rangeToFirstGateOfRef = byteBuffer.getShort(); + rangeToFirstGateOfDop = byteBuffer.getShort(); + gateSizeOfReflectivity = byteBuffer.getShort(); + gateSizeOfDoppler = byteBuffer.getShort(); + gatesNumberOfReflectivity = byteBuffer.getShort(); + gatesNumberOfDoppler = byteBuffer.getShort(); + cutSectorNumber = byteBuffer.getShort(); + calibrationConst = byteBuffer.getShort(); + ptrOfReflectivity = byteBuffer.getShort(); + ptrOfVelocity = byteBuffer.getShort(); + ptrOfSpectrumWidth = byteBuffer.getShort(); + resolutionOfVelocity = byteBuffer.getShort(); + vcpNumber = byteBuffer.getShort(); + byteBuffer.position(byteBuffer.position() + 14); + nyquist = byteBuffer.getShort(); + } + + /** + * Has reflectivity data or not + * @return Has reflectivity data + */ + public boolean hasReflectivityData() { + return gatesNumberOfReflectivity > 0; + } + + /** + * Has doppler data or not + * @return Has doppler data + */ + public boolean hasDopplerData() { + return gatesNumberOfDoppler > 0; + } + + /** + * Get product names + * @return Product names + */ + public List getProducts() { + List products = new ArrayList<>(); + if (hasReflectivityData()) { + products.add("dBZ"); + } + if (hasDopplerData()) { + products.add("V"); + products.add("W"); + } + + return products; + } + + /** + * Get azimuth + * @return Azimuth + */ + public float getAzimuth() { + return azimuth / 8.f * 180.f / 4096.f; + } + + /** + * Get elevation + * @return Elevation + */ + public float getElevation() { + return elevation / 8.f * 180.f / 4096.f; + } + } +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java index febf355f..cd70294f 100644 --- a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java @@ -20,23 +20,13 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; -public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { +public class CMARadarBaseDataInfo extends BaseRadarDataInfo implements IRadarDataInfo { private GenericHeader genericHeader; private SiteConfig siteConfig; private TaskConfig taskConfig; private List cutConfigs; private List radialHeaders; - private final Map productMap = Stream.of(new Object[][]{{1,"dBT"}, {2,"dBZ"}, - {3,"V"}, {4,"W"}, {5,"SQI"}, {6,"CPA"}, {7,"ZDR"}, {8,"LDR"}, {9,"CC"}, {10,"PhiDP"}, - {11,"KDP"}, {12,"CP"}, {13,"Flag"}, {14,"HCL"}, {15,"CF"}, {16,"SNRH"}, {17,"SNRV"}, - {18,"Flag"}, {19,"Flag"}, {20,"Flag"}, {21,"Flag"}, {22,"Flag"}, {23,"Flag"}, - {24,"Flag"}, {25,"Flag"}, {26,"Flag"}, {27,"Flag"}, {28,"Flag"}, {29,"Flag"}, - {30,"Flag"}, {31,"Flag"}, {32,"Zc"}, {33,"Vc"}, {34,"Wc"}, {35,"ZDRc"}, {0,"Flag"} - }).collect(Collectors.toMap(data -> (Integer) data[0], data -> (String) data[1])); - private final Map recordMap = new HashMap<>(); - private final List velocityGroup = new ArrayList<>(Arrays.asList("V", "W")); - private Dimension radialDim, scanDim, gateRDim, gateVDim; /** * Constructor @@ -45,6 +35,15 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { this.meteoDataType = MeteoDataType.RADAR; } + /** + * Get radar data type + * @return Radar data type + */ + @Override + public RadarDataType getRadarDataType() { + return RadarDataType.STANDARD; + } + /** * Get generic header * @return Generic header @@ -85,78 +84,6 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { return this.radialHeaders; } - /** - * Get record map - * @return Record map - */ - public Map getRecordMap() { - return this.recordMap; - } - - @Override - public GridArray getGridArray(String varName) { - return null; - } - - @Override - public GridData getGridData_LonLat(int timeIdx, String varName, int levelIdx) { - return null; - } - - @Override - public GridData getGridData_TimeLat(int lonIdx, String varName, int levelIdx) { - return null; - } - - @Override - public GridData getGridData_TimeLon(int latIdx, String varName, int levelIdx) { - return null; - } - - @Override - public GridData getGridData_LevelLat(int lonIdx, String varName, int timeIdx) { - return null; - } - - @Override - public GridData getGridData_LevelLon(int latIdx, String varName, int timeIdx) { - return null; - } - - @Override - public GridData getGridData_LevelTime(int latIdx, String varName, int lonIdx) { - return null; - } - - @Override - public GridData getGridData_Time(int lonIdx, int latIdx, String varName, int levelIdx) { - return null; - } - - @Override - public GridData getGridData_Level(int lonIdx, int latIdx, String varName, int timeIdx) { - return null; - } - - @Override - public GridData getGridData_Lon(int timeIdx, int latIdx, String varName, int levelIdx) { - return null; - } - - @Override - public GridData getGridData_Lat(int timeIdx, int lonIdx, String varName, int levelIdx) { - return null; - } - - /** - * Is a radial record is in velocity group or not - * @param record The radial record - * @return Velocity group or not - */ - public boolean isVelocityGroup(RadialRecord record) { - return velocityGroup.contains(record.product); - } - @Override public boolean isValidFile(RandomAccessFile raf) { try { @@ -229,6 +156,8 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { try { genericHeader = new GenericHeader(raf); siteConfig = new SiteConfig(raf); + this.antennaHeight = siteConfig.antennaHeight; + this.beamWidthVert = siteConfig.beamWidthVert; //Add global attributes this.addAttribute(new Attribute("StationCode", siteConfig.siteCode)); @@ -248,8 +177,14 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { //Read radial data cutConfigs = new ArrayList<>(); + CutConfig cutConfig; for (int i = 0; i < taskConfig.cutNumber; i++) { - cutConfigs.add(new CutConfig(raf)); + cutConfig = new CutConfig(raf); + cutConfigs.add(cutConfig); + if (i == 0) { + this.logResolution = cutConfig.logResolution; + this.dopplerResolution = cutConfig.dopplerResolution; + } } radialHeaders = new ArrayList<>(); byte[] rhBytes = new byte[RadialHeader.length]; @@ -264,8 +199,8 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { } else { record = new RadialRecord(product); record.setBinLength(momentHeader.binLength); - record.scale = momentHeader.scale; - record.offset = momentHeader.offset; + record.scale = 1.f / momentHeader.scale; + record.offset = -momentHeader.offset / (float) momentHeader.scale; this.recordMap.put(product, record); } if (radialHeader.radialNumber == 1) { @@ -331,181 +266,6 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { } } - private void makeRefVariables(RadialRecord refRadialRecord) { - Dimension[] dimensions = new Dimension[]{scanDim, radialDim, gateRDim}; - for (RadialRecord radialRecord : this.recordMap.values()) { - if (!radialRecord.isVelocityGroup()) - radialRecord.makeVariable(this, dimensions); - } - - //coordinate variables - Variable elevation = new Variable(); - elevation.setName("elevationR"); - elevation.setDataType(DataType.FLOAT); - elevation.addDimension(scanDim); - elevation.addDimension(radialDim); - elevation.addAttribute(new Attribute("units", "degree")); - elevation.addAttribute(new Attribute("long_name", "elevation angle in degrees")); - this.addVariable(elevation); - - Variable azimuth = new Variable(); - azimuth.setName("azimuthR"); - azimuth.setDataType(DataType.FLOAT); - azimuth.addDimension(scanDim); - azimuth.addDimension(radialDim); - azimuth.addAttribute(new Attribute("units", "degree")); - azimuth.addAttribute(new Attribute("long_name", "azimuth angle in degrees")); - this.addVariable(azimuth); - - Variable distance = new Variable(); - distance.setName("distanceR"); - distance.setDataType(DataType.FLOAT); - distance.addDimension(gateRDim); - distance.addAttribute(new Attribute("units", "m")); - distance.addAttribute(new Attribute("long_name", "radial distance to start of gate")); - this.addVariable(distance); - - Variable nRadials = new Variable(); - nRadials.setName("numRadialsR"); - nRadials.setDataType(DataType.INT); - nRadials.addDimension(scanDim); - nRadials.addAttribute(new Attribute("long_name", "number of valid radials in this scan")); - this.addVariable(nRadials); - - Variable nGates = new Variable(); - nGates.setName("numGatesR"); - nGates.setDataType(DataType.INT); - nGates.addDimension(scanDim); - nGates.addAttribute(new Attribute("long_name", "number of valid gates in this scan")); - this.addVariable(nGates); - - int nScan = scanDim.getLength(); - int nRadial = radialDim.getLength(); - int nGate = gateRDim.getLength(); - Array elevData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); - Array aziData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); - Array nRData = Array.factory(DataType.INT, new int[]{nScan}); - Array nGData = Array.factory(DataType.INT, new int[]{nScan}); - Index elevIndex = elevData.getIndex(); - Index aziIndex = aziData.getIndex(); - for (int i = 0; i < nScan; i++) { - List elevList = refRadialRecord.elevation.get(i); - List aziList = refRadialRecord.azimuth.get(i); - nRData.setInt(i, aziList.size()); - nGData.setInt(i, (int) refRadialRecord.distance.get(i).getSize()); - for (int j = 0; j < nRadial; j++) { - if (j < elevList.size()) { - elevData.setFloat(elevIndex.set(i, j), elevList.get(j)); - aziData.setFloat(aziIndex.set(i, j), aziList.get(j)); - } else { - elevData.setFloat(elevIndex.set(i, j), Float.NaN); - aziData.setFloat(aziIndex.set(i, j), Float.NaN); - } - } - } - Array disData = refRadialRecord.distance.get(0); - - elevation.setCachedData(elevData); - azimuth.setCachedData(aziData); - distance.setCachedData(disData); - nRadials.setCachedData(nRData); - nGates.setCachedData(nGData); - } - - private void makeVelVariables(RadialRecord velRadialRecord) { - Dimension[] dimensions = new Dimension[]{scanDim, radialDim, gateVDim}; - for (RadialRecord radialRecord : this.recordMap.values()) { - if (radialRecord.isVelocityGroup()) - radialRecord.makeVariable(this, dimensions); - } - - //coordinate variables - Variable elevation = new Variable(); - elevation.setName("elevationV"); - elevation.setDataType(DataType.FLOAT); - elevation.addDimension(scanDim); - elevation.addDimension(radialDim); - elevation.addAttribute(new Attribute("units", "degree")); - elevation.addAttribute(new Attribute("long_name", "elevation angle in degrees")); - this.addVariable(elevation); - - Variable azimuth = new Variable(); - azimuth.setName("azimuthV"); - azimuth.setDataType(DataType.FLOAT); - azimuth.addDimension(scanDim); - azimuth.addDimension(radialDim); - azimuth.addAttribute(new Attribute("units", "degree")); - azimuth.addAttribute(new Attribute("long_name", "azimuth angle in degrees")); - this.addVariable(azimuth); - - Variable distance = new Variable(); - distance.setName("distanceV"); - distance.setDataType(DataType.FLOAT); - distance.addDimension(gateVDim); - distance.addAttribute(new Attribute("units", "m")); - distance.addAttribute(new Attribute("long_name", "radial distance to start of gate")); - this.addVariable(distance); - - Variable nRadials = new Variable(); - nRadials.setName("numRadialsR"); - nRadials.setDataType(DataType.INT); - nRadials.addDimension(scanDim); - nRadials.addAttribute(new Attribute("long_name", "number of valid radials in this scan")); - this.addVariable(nRadials); - - Variable nGates = new Variable(); - nGates.setName("numGatesR"); - nGates.setDataType(DataType.INT); - nGates.addDimension(scanDim); - nGates.addAttribute(new Attribute("long_name", "number of valid gates in this scan")); - this.addVariable(nGates); - - int nScan = scanDim.getLength(); - int nRadial = radialDim.getLength(); - int nGate = gateVDim.getLength(); - Array elevData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); - Array aziData = Array.factory(DataType.FLOAT, new int[]{nScan, nRadial}); - Array nRData = Array.factory(DataType.INT, new int[]{nScan}); - Array nGData = Array.factory(DataType.INT, new int[]{nScan}); - Index elevIndex = elevData.getIndex(); - Index aziIndex = aziData.getIndex(); - for (int i = 0; i < nScan; i++) { - List elevList = velRadialRecord.elevation.get(i); - List aziList = velRadialRecord.azimuth.get(i); - nRData.setInt(i, aziList.size()); - nGData.setInt(i, (int) velRadialRecord.distance.get(i).getSize()); - for (int j = 0; j < nRadial; j++) { - if (j < elevList.size()) { - elevData.setFloat(elevIndex.set(i, j), elevList.get(j)); - aziData.setFloat(aziIndex.set(i, j), aziList.get(j)); - } else { - elevData.setFloat(elevIndex.set(i, j), Float.NaN); - aziData.setFloat(aziIndex.set(i, j), Float.NaN); - } - } - } - Array disData = velRadialRecord.distance.get(0); - - elevation.setCachedData(elevData); - azimuth.setCachedData(aziData); - distance.setCachedData(disData); - nRadials.setCachedData(nRData); - nGates.setCachedData(nGData); - } - - /** - * Get product names - * @return product names - */ - public List getProducts() { - List products = new ArrayList<>(); - for (String product : this.recordMap.keySet()) { - products.add(product); - } - - return products; - } - /** * Get scan elevations * @return Scan elevations @@ -520,104 +280,6 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { return elevations; } - /** - * Get scan elevations - * @return Scan elevations - */ - public List getElevations(String product) { - RadialRecord radialRecord = this.recordMap.get(product); - - return radialRecord.fixedElevation; - } - - @Override - public Array read(String varName) { - Variable var = this.getVariable(varName); - int n = var.getDimNumber(); - int[] origin = new int[n]; - int[] size = new int[n]; - int[] stride = new int[n]; - for (int i = 0; i < n; i++) { - origin[i] = 0; - size[i] = var.getDimLength(i); - stride[i] = 1; - } - - Array r = read(varName, origin, size, stride); - - return r; - } - - @Override - public Array read(String varName, int[] origin, int[] size, int[] stride) { - try { - Variable variable = this.getVariable(varName); - if (variable.hasCachedData()) { - return variable.getCachedData().section(origin, size, stride).copy(); - } - - Section section = new Section(origin, size, stride); - RadialRecord record = this.recordMap.get(varName); - Array dataArray = Array.factory(DataType.FLOAT, section.getShape()); - Range zRange = section.getRange(0); - Range yRange = section.getRange(1); - Range xRange = section.getRange(2); - IndexIterator iter = dataArray.getIndexIterator(); - for (int s = zRange.first(); s <= zRange.last(); s += zRange.stride()) { - List arrays = record.getDataArray(s); - for (int i = yRange.first(); i <= yRange.last(); i += yRange.stride()) { - if (i < arrays.size()) { - Array array = arrays.get(i); - for (int j = xRange.first(); j <= xRange.last(); j += xRange.stride()) { - if (j < array.getSize()) - iter.setFloatNext(array.getFloat(j)); - else - iter.setFloatNext(Float.NaN); - } - } else { - for (int j = xRange.first(); j <= xRange.last(); j += xRange.stride()) { - iter.setFloatNext(Float.NaN); - } - } - } - } - - Attribute aoAttr = variable.findAttribute("add_offset"); - Attribute sfAttr = variable.findAttribute("scale_factor"); - if (aoAttr != null || sfAttr != null) { - Number add_offset = 0.f; - Number scale_factor = 1.f; - if (aoAttr != null) { - switch (aoAttr.getDataType()) { - case DOUBLE: - add_offset = aoAttr.getValues().getDouble(0); - break; - case FLOAT: - case INT: - add_offset = aoAttr.getValues().getFloat(0); - break; - } - } - if (sfAttr != null) { - switch (sfAttr.getDataType()) { - case DOUBLE: - scale_factor = sfAttr.getValues().getDouble(0); - break; - case FLOAT: - case INT: - scale_factor = sfAttr.getValues().getFloat(0); - break; - } - } - dataArray = ArrayMath.div(ArrayMath.sub(dataArray, add_offset), scale_factor); - } - - return dataArray; - } catch (InvalidRangeException e) { - return null; - } - } - @Override public List getGlobalAttributes() { return this.attributes; diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/IRadarDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/IRadarDataInfo.java new file mode 100644 index 00000000..f3300c39 --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/IRadarDataInfo.java @@ -0,0 +1,10 @@ +package org.meteoinfo.data.meteodata.radar; + +public interface IRadarDataInfo { + + /** + * Get radar data type + * @return Radar data type + */ + public abstract RadarDataType getRadarDataType(); +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadarDataType.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadarDataType.java new file mode 100644 index 00000000..c90face6 --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadarDataType.java @@ -0,0 +1,11 @@ +package org.meteoinfo.data.meteodata.radar; + +public enum RadarDataType { + STANDARD, + SAB, + CC, + CC20, + SC, + PA, + UN_DEFINED +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadarDataUtil.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadarDataUtil.java new file mode 100644 index 00000000..0a2e6b90 --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadarDataUtil.java @@ -0,0 +1,148 @@ +package org.meteoinfo.data.meteodata.radar; + +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; +import org.meteoinfo.common.DataConvert; +import org.meteoinfo.data.meteodata.DataInfo; + +import java.io.RandomAccessFile; +import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; + +public class RadarDataUtil { + + /** + * Get radar data type + * @param raf RandomAccessFile object + * @return Radar data type + */ + public static RadarDataType getRadarDataType(RandomAccessFile raf) { + try { + raf.seek(0); + byte[] bytes = new byte[136]; + raf.read(bytes); + byte[] magicBytes = Arrays.copyOf(bytes, 4); + int magic = DataConvert.bytes2Int(magicBytes, ByteOrder.LITTLE_ENDIAN); + if (magic == 1297371986) { + return RadarDataType.STANDARD; + } + + magicBytes = Arrays.copyOfRange(bytes, 14, 16); + if (Arrays.equals(magicBytes, new byte[]{1, 0})) { + return RadarDataType.SAB; + } + + magicBytes = Arrays.copyOfRange(bytes, 8, 12); + if (Arrays.equals(magicBytes, new byte[]{16, 0, 0, 0})) { + return RadarDataType.PA; + } + + String radarT = new String(bytes); + if (radarT.contains("CINRAD/SC") || radarT.contains("CINRAD/CD")) { + return RadarDataType.SC; + } else if (radarT.contains("CINRADC")) { + return RadarDataType.CC; + } else if (!radarT.contains("CINRADC") && radarT.contains("CINRAD/CC")) { + return RadarDataType.CC20; + } + } catch (Exception e) { + return null; + } + + return null; + } + + /** + * Get radar data type + * @param fileName Data file name + * @return Radar data type + */ + public static RadarDataType getRadarDataType(String fileName) { + try { + byte[] bytes = new byte[136]; + if (fileName.endsWith("bz2")) { + BZip2CompressorInputStream inputStream = new BZip2CompressorInputStream(Files.newInputStream(Paths.get(fileName))); + inputStream.read(bytes); + inputStream.close(); + } else { + RandomAccessFile raf = new RandomAccessFile(fileName, "r"); + raf.seek(0); + raf.read(bytes); + raf.close(); + } + byte[] magicBytes = Arrays.copyOf(bytes, 4); + int magic = DataConvert.bytes2Int(magicBytes, ByteOrder.LITTLE_ENDIAN); + if (magic == 1297371986) { + return RadarDataType.STANDARD; + } + + magicBytes = Arrays.copyOfRange(bytes, 14, 16); + if (Arrays.equals(magicBytes, new byte[]{1, 0})) { + return RadarDataType.SAB; + } + + magicBytes = Arrays.copyOfRange(bytes, 8, 12); + if (Arrays.equals(magicBytes, new byte[]{16, 0, 0, 0})) { + return RadarDataType.PA; + } + + String radarT = new String(bytes); + if (radarT.contains("CINRAD/SC") || radarT.contains("CINRAD/CD")) { + return RadarDataType.SC; + } else if (radarT.contains("CINRADC")) { + return RadarDataType.CC; + } else if (!radarT.contains("CINRADC") && radarT.contains("CINRAD/CC")) { + return RadarDataType.CC20; + } + } catch (Exception e) { + return null; + } + + return null; + } + + /** + * Get radar data info + * @param radarDataType Radar data type + * @return The DataInfo object + */ + public static DataInfo getDataInfo(RadarDataType radarDataType) { + if (radarDataType == null) { + return null; + } else { + switch (radarDataType) { + case STANDARD: + return new CMARadarBaseDataInfo(); + case SAB: + return new SABRadarDataInfo(); + /*case CC: + return new CCRadarDataInfo(); + case SC: + return new SCRadarDataInfo();*/ + default: + return null; + } + } + } + + /** + * Get radar data info + * @param raf RandomAccessFile object + * @return The DataInfo object + */ + public static DataInfo getDataInfo(RandomAccessFile raf) { + RadarDataType radarDataType = getRadarDataType(raf); + return getDataInfo(radarDataType); + } + + /** + * Get radar data info + * @param fileName Data file name + * @return The DataInfo object + */ + public static DataInfo getDataInfo(String fileName) { + RadarDataType radarDataType = getRadarDataType(fileName); + return getDataInfo(radarDataType); + } +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadialRecord.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadialRecord.java index 8fce2267..60174de7 100644 --- a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadialRecord.java +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/RadialRecord.java @@ -19,8 +19,8 @@ public class RadialRecord { private int binLength; private DataType dataType; private int fillValue; - public int scale; - public int offset; + public float scale = 1; + public float offset = 0; public List fixedElevation = new ArrayList<>(); public List> elevation = new ArrayList<>(); public List> azimuth = new ArrayList<>(); @@ -113,6 +113,18 @@ public class RadialRecord { this.data.add(new ArrayList<>()); } + /** + * Add a data array + * @param array Data array + */ + public void addDataArray(Array array) { + if (this.data.isEmpty()) { + this.data.add(new ArrayList<>()); + } + + this.data.get(this.data.size() - 1).add(array); + } + /** * Add a data bytes * @param bytes Data bytes @@ -151,7 +163,7 @@ public class RadialRecord { array = Array.factory(this.dataType, new int[]{bytes.length}); for (int i = 0; i < bytes.length; i++) { v = (float) DataType.unsignedByteToShort(bytes[i]); - v = (v - offset) / scale; + v = v * scale + offset; array.setFloat(i, v); } } else { @@ -160,7 +172,7 @@ public class RadialRecord { for (int i = 0; i < n; i++) { short s = DataConvert.bytes2Short(new byte[]{bytes[i*2], bytes[i*2+1]}, ByteOrder.LITTLE_ENDIAN); v = (float) DataType.unsignedShortToInt(s); - v = (v - offset) / scale; + v = v * scale + offset; array.setFloat(i, v); } } @@ -244,7 +256,7 @@ public class RadialRecord { * @param dataInfo The data info * @param dimensions Dimensions */ - public void makeVariable(CMARadarBaseDataInfo dataInfo, Dimension[] dimensions) { + public void makeVariable(BaseRadarDataInfo dataInfo, Dimension[] dimensions) { Variable variable = new Variable(); variable.setName(this.product); variable.setDataType(this.dataType); @@ -261,7 +273,7 @@ public class RadialRecord { * @param dataInfo The data info * @param xyzDim xyz dimension */ - public void makeVariables(CMARadarBaseDataInfo dataInfo, Dimension xyzDim) { + public void makeVariables(BaseRadarDataInfo dataInfo, Dimension xyzDim) { for (int i = 0; i < getScanNumber(); i++) { String suffix = "_s" + String.valueOf(i + 1); Dimension radialDim = new Dimension(DimensionType.Y); @@ -495,7 +507,7 @@ public class RadialRecord { } if (!Float.isNaN(v)) - v = (v - this.offset) / this.scale; + v = v * scale + this.offset; return v; } @@ -575,7 +587,7 @@ public class RadialRecord { } if (!Float.isNaN(v)) - v = (v - this.offset) / this.scale; + v = v * this.scale + this.offset; return v; } diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/SABRadarDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/SABRadarDataInfo.java new file mode 100644 index 00000000..0540333e --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/SABRadarDataInfo.java @@ -0,0 +1,280 @@ +package org.meteoinfo.data.meteodata.radar; + +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; +import org.meteoinfo.common.DataConvert; +import org.meteoinfo.data.dimarray.Dimension; +import org.meteoinfo.data.meteodata.Attribute; +import org.meteoinfo.data.meteodata.DataInfo; +import org.meteoinfo.data.meteodata.Variable; +import org.meteoinfo.ndarray.*; +import org.meteoinfo.ndarray.math.ArrayUtil; + +import java.io.BufferedInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SABRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo { + + @Override + public boolean isValidFile(java.io.RandomAccessFile raf) { + return false; + } + + void setScaleOffset(RadialRecord record, int vResolution) { + switch (record.product) { + case "dBZ": + record.scale = 0.5f; + record.offset = -33.f; + break; + case "V": + if (vResolution == 2) { + record.scale = 0.5f; + record.offset = -64.5f; + } else { + record.scale = 1.f; + record.offset = -129.f; + } + break; + case "W": + record.scale = 0.5f; + record.offset = -64.5f; + break; + } + } + + @Override + public void readDataInfo(String fileName) { + this.fileName = fileName; + if (fileName.endsWith(".bz2")) { + try { + BZip2CompressorInputStream inputStream = new BZip2CompressorInputStream(Files.newInputStream(Paths.get(fileName))); + readDataInfo(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + try { + BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(fileName))); + readDataInfo(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + void readDataInfo(InputStream is) { + try { + int index = 0; + byte[] rhBytes = new byte[RadialHeader.length]; + while (is.read(rhBytes) != -1) { + RadialHeader radialHeader = new RadialHeader(rhBytes); + if (index == 0) { + this.logResolution = radialHeader.gateSizeOfReflectivity; + this.dopplerResolution = radialHeader.gateSizeOfDoppler; + } + if (!radialHeader.hasReflectivityData()) { + is.read(new byte[460]); + } + for (String product : radialHeader.getProducts()) { + RadialRecord record; + if (this.recordMap.containsKey(product)) { + record = this.recordMap.get(product); + } else { + record = new RadialRecord(product); + record.setBinLength(1); + setScaleOffset(record, radialHeader.resolutionOfVelocity); + this.recordMap.put(product, record); + } + if (radialHeader.radialNumber == 1) { + record.fixedElevation.add(radialHeader.getElevation()); + record.elevation.add(new ArrayList<>()); + record.azimuth.add(new ArrayList<>()); + record.azimuthMinIndex.add(0); + if (isVelocityGroup(record)) { + record.disResolution.add(radialHeader.gateSizeOfDoppler); + record.distance.add(ArrayUtil.arrayRange1(radialHeader.rangeToFirstGateOfDop, + radialHeader.gatesNumberOfDoppler, radialHeader.gateSizeOfDoppler)); + } else { + record.disResolution.add(radialHeader.gateSizeOfReflectivity); + record.distance.add(ArrayUtil.arrayRange1(radialHeader.rangeToFirstGateOfRef, + radialHeader.gatesNumberOfReflectivity, radialHeader.gateSizeOfReflectivity)); + } + record.newScanData(); + } + record.elevation.get(record.elevation.size() - 1).add(radialHeader.getElevation()); + record.addAzimuth(radialHeader.getAzimuth()); + int dataLength = isVelocityGroup(record) ? radialHeader.gatesNumberOfDoppler : radialHeader.gatesNumberOfReflectivity; + byte[] bytes = new byte[dataLength]; + is.read(bytes); + record.addDataBytes(bytes); + if (isVelocityGroup(record)) { + if (dataLength < 920) { + is.read(new byte[920 - dataLength]); + } + } else { + if (dataLength < 460) { + is.read(new byte[460 - dataLength]); + } + } + } + if (!radialHeader.hasDopplerData()) { + is.read(new byte[920 + 920]); + } + is.read(new byte[4]); + index += 1; + } + is.close(); + + this.addAttribute(new Attribute("featureType", "RADIAL")); + this.addAttribute(new Attribute("DataType", "Radial")); + + //Add dimensions and variables + RadialRecord refRadialRecord = this.recordMap.get("dBZ"); + radialDim = new Dimension(); + radialDim.setName("radial"); + radialDim.setLength(refRadialRecord.getMaxRadials()); + this.addDimension(radialDim); + scanDim = new Dimension(); + scanDim.setName("scan"); + scanDim.setLength(refRadialRecord.getScanNumber()); + this.addDimension(scanDim); + gateRDim = new Dimension(); + gateRDim.setName("gateR"); + gateRDim.setLength(refRadialRecord.getGateNumber(0)); + this.addDimension(gateRDim); + makeRefVariables(refRadialRecord); + + RadialRecord velRadialRecord = this.recordMap.get("V"); + gateVDim = new Dimension(); + gateVDim.setName("gateV"); + gateVDim.setLength(velRadialRecord.getGateNumber(0)); + this.addDimension(gateVDim); + makeVelVariables(velRadialRecord); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public RadarDataType getRadarDataType() { + return RadarDataType.SAB; + } + + static class RadialHeader { + public static int length = 128; + public int mSecond; // collection time for this radial, msecs since midnight + public short julianDate; // prob "collection time" + public short uRange; // unambiguous range + public int azimuth; // azimuth angle + public short radialNumber; // radial number within the elevation + public short radialStatus; + public short elevation; + public short elNumber; // elevation number + public int rangeToFirstGateOfRef; // range to first gate of reflectivity (m) may be negative + public int rangeToFirstGateOfDop; // range to first gate of doppler (m) may be negative + public int gateSizeOfReflectivity; // reflectivity data gate size (m) + public int gateSizeOfDoppler; // doppler data gate size (m) + public int gatesNumberOfReflectivity; // number of reflectivity gates + public int gatesNumberOfDoppler; // number of velocity or spectrum width gates + public short cutSectorNumber; + public int calibrationConst; + public short ptrOfReflectivity; + public short ptrOfVelocity; + public short ptrOfSpectrumWidth; + public int resolutionOfVelocity; + public short vcpNumber; + public short nyquist; + + /** + * Constructor + * @param is InputStream + */ + public RadialHeader(byte[] inBytes) throws IOException { + ByteBuffer byteBuffer = ByteBuffer.wrap(inBytes); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + byteBuffer.position(28); + mSecond = byteBuffer.getInt(); + julianDate = byteBuffer.getShort(); + uRange = byteBuffer.getShort(); + azimuth = DataType.unsignedShortToInt(byteBuffer.getShort()); + radialNumber = byteBuffer.getShort(); + radialStatus = byteBuffer.getShort(); + elevation = byteBuffer.getShort(); + elNumber = byteBuffer.getShort(); + rangeToFirstGateOfRef = byteBuffer.getShort(); + rangeToFirstGateOfDop = byteBuffer.getShort(); + gateSizeOfReflectivity = byteBuffer.getShort(); + gateSizeOfDoppler = byteBuffer.getShort(); + gatesNumberOfReflectivity = byteBuffer.getShort(); + gatesNumberOfDoppler = byteBuffer.getShort(); + cutSectorNumber = byteBuffer.getShort(); + calibrationConst = byteBuffer.getShort(); + ptrOfReflectivity = byteBuffer.getShort(); + ptrOfVelocity = byteBuffer.getShort(); + ptrOfSpectrumWidth = byteBuffer.getShort(); + resolutionOfVelocity = byteBuffer.getShort(); + vcpNumber = byteBuffer.getShort(); + byteBuffer.position(byteBuffer.position() + 14); + nyquist = byteBuffer.getShort(); + } + + /** + * Has reflectivity data or not + * @return Has reflectivity data + */ + public boolean hasReflectivityData() { + return gatesNumberOfReflectivity > 0; + } + + /** + * Has doppler data or not + * @return Has doppler data + */ + public boolean hasDopplerData() { + return gatesNumberOfDoppler > 0; + } + + /** + * Get product names + * @return Product names + */ + public List getProducts() { + List products = new ArrayList<>(); + if (hasReflectivityData()) { + products.add("dBZ"); + } + if (hasDopplerData()) { + products.add("V"); + products.add("W"); + } + + return products; + } + + /** + * Get azimuth + * @return Azimuth + */ + public float getAzimuth() { + return azimuth / 8.f * 180.f / 4096.f; + } + + /** + * Get elevation + * @return Elevation + */ + public float getElevation() { + return elevation / 8.f * 180.f / 4096.f; + } + } +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/SCRadarDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/SCRadarDataInfo.java new file mode 100644 index 00000000..e77aee92 --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/SCRadarDataInfo.java @@ -0,0 +1,34 @@ +package org.meteoinfo.data.meteodata.radar; + +import org.meteoinfo.data.meteodata.DataInfo; +import org.meteoinfo.ndarray.Array; + +import java.io.RandomAccessFile; + +public class SCRadarDataInfo extends DataInfo implements IRadarDataInfo { + + @Override + public boolean isValidFile(RandomAccessFile raf) { + return false; + } + + @Override + public void readDataInfo(String fileName) { + + } + + @Override + public Array read(String varName) { + return null; + } + + @Override + public Array read(String varName, int[] origin, int[] size, int[] stride) { + return null; + } + + @Override + public RadarDataType getRadarDataType() { + return RadarDataType.SAB; + } +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/cinrad/Cinrad2Record.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/cinrad/Cinrad2Record.java new file mode 100644 index 00000000..a5a0dace --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/cinrad/Cinrad2Record.java @@ -0,0 +1,208 @@ +package org.meteoinfo.data.meteodata.radar.cinrad; + +public class Cinrad2Record { + + /** Reflectivity moment identifier */ + public static final int REFLECTIVITY = 1; + + /** Radial Velocity moment identifier */ + public static final int VELOCITY_HI = 2; + + /** Radial Velocity moment identifier */ + public static final int VELOCITY_LOW = 4; + + /** Spectrum Width moment identifier */ + public static final int SPECTRUM_WIDTH = 3; + + /** Low doppler resolution code */ + public static final int DOPPLER_RESOLUTION_LOW_CODE = 4; + + /** High doppler resolution code */ + public static final int DOPPLER_RESOLUTION_HIGH_CODE = 2; + + /** Horizontal beam width */ + public static final float HORIZONTAL_BEAM_WIDTH = (float) 1.5; // LOOK + + public static byte MISSING_DATA = (byte) 1; + public static final byte BELOW_THRESHOLD = (byte) 0; + + /** Size of the file header, aka title */ + static int FILE_HEADER_SIZE = 0; + + /** Size of the CTM record header */ + private static int CTM_HEADER_SIZE = 14; + + /** Size of the message header, to start of the data message */ + private static final int MESSAGE_HEADER_SIZE = 28; + + /** Size of the entire message, if its a radar data message */ + private static int RADAR_DATA_SIZE = 2432; + + public static String getDatatypeName(int datatype) { + switch (datatype) { + case REFLECTIVITY: + return "Reflectivity"; + case VELOCITY_HI: + case VELOCITY_LOW: + return "RadialVelocity"; + case SPECTRUM_WIDTH: + return "SpectrumWidth"; + default: + throw new IllegalArgumentException(); + } + } + + public static String getDatatypeUnits(int datatype) { + switch (datatype) { + case REFLECTIVITY: + return "dBz"; + + case VELOCITY_HI: + case VELOCITY_LOW: + case SPECTRUM_WIDTH: + return "m/s"; + } + throw new IllegalArgumentException(); + } + + public static float getDatatypeScaleFactor(int datatype) { + switch (datatype) { + case REFLECTIVITY: + if (CinradDataInfo.isCC) + return 0.1f; + if (CinradDataInfo.isCC20) + return 0.5f; + else + return 0.5f; + case VELOCITY_LOW: + if (CinradDataInfo.isSC) + return 0.3673f; + else if (CinradDataInfo.isCC) + return 0.1f; + else + return 1.0f; + case VELOCITY_HI: + case SPECTRUM_WIDTH: + if (CinradDataInfo.isSC) + return 0.1822f; + else if (CinradDataInfo.isCC) + return 0.1f; + else if (CinradDataInfo.isCC20) + return 1.0f; + else + return 0.5f; + default: + throw new IllegalArgumentException(); + } + } + + public static float getDatatypeAddOffset(int datatype) { + switch (datatype) { + case REFLECTIVITY: + if (CinradDataInfo.isSC) + return -32.0f; + else if (CinradDataInfo.isCC) + return 0.0f; + else if (CinradDataInfo.isCC20) + return -32.0f; + else + return -33.0f; + case VELOCITY_LOW: + if (CinradDataInfo.isSC) + return 0.0f; + else if (CinradDataInfo.isCC) + return 0.0f; + else if (CinradDataInfo.isCC20) + return 0.0f; + else + return -129.0f; + case VELOCITY_HI: + case SPECTRUM_WIDTH: + if (CinradDataInfo.isSC) + return 0.0f; + else if (CinradDataInfo.isCC) + return 0.0f; + else if (CinradDataInfo.isCC20) + return 0.0f; + else + return -64.5f; + default: + throw new IllegalArgumentException(); + } + } + + public static String getMessageTypeName(int code) { + switch (code) { + case 1: + return "digital radar data"; + case 2: + return "RDA status data"; + case 3: + return "performance/maintainence data"; + case 4: + return "console message - RDA to RPG"; + case 5: + return "maintainence log data"; + case 6: + return "RDA control ocmmands"; + case 7: + return "volume coverage pattern"; + case 8: + return "clutter censor zones"; + case 9: + return "request for data"; + case 10: + return "console message - RPG to RDA"; + case 11: + return "loop back test - RDA to RPG"; + case 12: + return "loop back test - RPG to RDA"; + case 13: + return "clutter filter bypass map - RDA to RPG"; + case 14: + return "edited clutter filter bypass map - RDA to RPG"; + case 15: + return "Notchwidth Map"; + case 18: + return "RDA Adaptation data"; + default: + return "unknown " + code; + } + } + + public static String getRadialStatusName(int code) { + switch (code) { + case 0: + return "start of new elevation"; + case 1: + return "intermediate radial"; + case 2: + return "end of elevation"; + case 3: + return "begin volume scan"; + case 4: + return "end volume scan"; + default: + return "unknown " + code; + } + } + + public static String getVolumeCoveragePatternName(int code) { + switch (code) { + case 11: + return "16 elevation scans every 5 mins"; + case 12: + return "14 elevation scan every 4.1 mins"; + case 21: + return "11 elevation scans every 6 mins"; + case 31: + return "8 elevation scans every 10 mins"; + case 32: + return "7 elevation scans every 10 mins"; + case 121: + return "9 elevations, 20 scans every 5 minutes"; + default: + return "unknown " + code; + } + } +} diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/cinrad/CinradDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/cinrad/CinradDataInfo.java new file mode 100644 index 00000000..c379c646 --- /dev/null +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/cinrad/CinradDataInfo.java @@ -0,0 +1,75 @@ +package org.meteoinfo.data.meteodata.radar.cinrad; + +import org.meteoinfo.data.GridArray; +import org.meteoinfo.data.GridData; +import org.meteoinfo.data.meteodata.DataInfo; +import org.meteoinfo.data.meteodata.IGridDataInfo; +import org.meteoinfo.ndarray.Array; + +import java.io.IOException; +import java.io.RandomAccessFile; + +public class CinradDataInfo extends DataInfo { + + private static final int MISSING_INT = -9999; + private static final float MISSING_FLOAT = Float.NaN; + public static boolean isSC = false; + public static boolean isCC = false; + public static boolean isCC20 = false; + + @Override + public boolean isValidFile(RandomAccessFile raf) { + return isCINRAD(raf); + } + + public boolean isCINRAD(RandomAccessFile raf) { + try { + raf.seek(0); + + byte[] b128 = new byte[136]; + raf.read(b128); + String radarT = new String(b128); + + if (radarT.contains("CINRAD/SC") || radarT.contains("CINRAD/CD")) { + isSC = true; + isCC = false; + isCC20 = false; + return true; + } else if (radarT.contains("CINRADC")) { + isCC = true; + isSC = false; + isCC20 = false; + return true; + } else if (!radarT.contains("CINRADC") && radarT.contains("CINRAD/CC")) { + isCC20 = true; + isSC = false; + isCC = false; + return true; + } else { + isSC = false; + isCC = false; + isCC20 = false; + return false; + } + } catch (IOException ioe) { + return false; + } + + } + + @Override + public void readDataInfo(String fileName) { + + } + + @Override + public Array read(String varName) { + return null; + } + + @Override + public Array read(String varName, int[] origin, int[] size, int[] stride) { + return null; + } + +} diff --git a/meteoinfo-lab/milconfig.xml b/meteoinfo-lab/milconfig.xml index 3dcf6a43..f351896a 100644 --- a/meteoinfo-lab/milconfig.xml +++ b/meteoinfo-lab/milconfig.xml @@ -1,6 +1,6 @@ - + @@ -12,21 +12,21 @@ - - + + - + - + @@ -34,5 +34,5 @@
- +