mirror of
https://github.com/meteoinfo/MeteoInfo.git
synced 2025-12-08 20:36:05 +00:00
add SA/SB radar data support
This commit is contained in:
parent
1794f0def1
commit
04ac485574
@ -96,7 +96,7 @@ import org.meteoinfo.data.GridData;
|
|||||||
/**
|
/**
|
||||||
* Read grid data - time
|
* Read grid data - time
|
||||||
*
|
*
|
||||||
* @param lonIdx Lontitude index
|
* @param lonIdx Longitude index
|
||||||
* @param latIdx Latitude index
|
* @param latIdx Latitude index
|
||||||
* @param varName Variable name
|
* @param varName Variable name
|
||||||
* @param levelIdx Level index
|
* @param levelIdx Level index
|
||||||
@ -107,7 +107,7 @@ import org.meteoinfo.data.GridData;
|
|||||||
/**
|
/**
|
||||||
* Read grid data - level
|
* Read grid data - level
|
||||||
*
|
*
|
||||||
* @param lonIdx Lontitude index
|
* @param lonIdx Longitude index
|
||||||
* @param latIdx Latitude index
|
* @param latIdx Latitude index
|
||||||
* @param varName Variable name
|
* @param varName Variable name
|
||||||
* @param timeIdx Time index
|
* @param timeIdx Time index
|
||||||
@ -130,7 +130,7 @@ import org.meteoinfo.data.GridData;
|
|||||||
* Read grid data - latitude
|
* Read grid data - latitude
|
||||||
*
|
*
|
||||||
* @param timeIdx Time index
|
* @param timeIdx Time index
|
||||||
* @param lonIdx Lontitude index
|
* @param lonIdx Longitude index
|
||||||
* @param varName Variable name
|
* @param varName Variable name
|
||||||
* @param levelIdx Level index
|
* @param levelIdx Level index
|
||||||
* @return Grid data
|
* @return Grid data
|
||||||
|
|||||||
@ -41,6 +41,9 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
import org.meteoinfo.data.meteodata.numpy.NumpyDataInfo;
|
import org.meteoinfo.data.meteodata.numpy.NumpyDataInfo;
|
||||||
import org.meteoinfo.data.meteodata.radar.CMARadarBaseDataInfo;
|
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.ndarray.math.ArrayMath;
|
||||||
import org.meteoinfo.projection.ProjectionInfo;
|
import org.meteoinfo.projection.ProjectionInfo;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -471,31 +474,31 @@ public class MeteoDataInfo {
|
|||||||
* @return Data info
|
* @return Data info
|
||||||
*/
|
*/
|
||||||
public DataInfo getDataInfo(String fileName) {
|
public DataInfo getDataInfo(String fileName) {
|
||||||
DataInfo di = null;
|
DataInfo di = RadarDataUtil.getDataInfo(fileName);
|
||||||
try {
|
if (di == null) {
|
||||||
RandomAccessFile raf = new RandomAccessFile(fileName, "r");
|
try {
|
||||||
if (GrADSDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) {
|
RandomAccessFile raf = new RandomAccessFile(fileName, "r");
|
||||||
di = new GrADSDataInfo();
|
if (GrADSDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) {
|
||||||
} else if (NetcdfFiles.canOpen(fileName)) {
|
di = new GrADSDataInfo();
|
||||||
di = new NetCDFDataInfo();
|
} else if (NetcdfFiles.canOpen(fileName)) {
|
||||||
} else if (ARLDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) {
|
di = new NetCDFDataInfo();
|
||||||
di = new ARLDataInfo();
|
} else if (ARLDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) {
|
||||||
} else if (CMARadarBaseDataInfo.canOpen(fileName)) {
|
di = new ARLDataInfo();
|
||||||
di = new CMARadarBaseDataInfo();
|
} else if (MatLabDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) {
|
||||||
} else if (MatLabDataInfo.class.getDeclaredConstructor().newInstance().isValidFile(raf)) {
|
di = new MatLabDataInfo();
|
||||||
di = new MatLabDataInfo();
|
} else {
|
||||||
} else {
|
di = MICAPSDataInfo.getDataInfo(raf);
|
||||||
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;
|
return di;
|
||||||
|
|||||||
@ -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<Integer, String> 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<String, RadialRecord> recordMap = new HashMap<>();
|
||||||
|
protected final List<String> 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<String, RadialRecord> 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<Float> elevList = refRadialRecord.elevation.get(i);
|
||||||
|
List<Float> 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<Float> elevList = velRadialRecord.elevation.get(i);
|
||||||
|
List<Float> 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<String> getProducts() {
|
||||||
|
List<String> products = new ArrayList<>();
|
||||||
|
for (String product : this.recordMap.keySet()) {
|
||||||
|
products.add(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
return products;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get scan elevations
|
||||||
|
* @return Scan elevations
|
||||||
|
*/
|
||||||
|
public List<Float> 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<Array> 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<Attribute> 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};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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<String> getProducts() {
|
||||||
|
List<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,23 +20,13 @@ import java.util.*;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo {
|
public class CMARadarBaseDataInfo extends BaseRadarDataInfo implements IRadarDataInfo {
|
||||||
|
|
||||||
private GenericHeader genericHeader;
|
private GenericHeader genericHeader;
|
||||||
private SiteConfig siteConfig;
|
private SiteConfig siteConfig;
|
||||||
private TaskConfig taskConfig;
|
private TaskConfig taskConfig;
|
||||||
private List<CutConfig> cutConfigs;
|
private List<CutConfig> cutConfigs;
|
||||||
private List<RadialHeader> radialHeaders;
|
private List<RadialHeader> radialHeaders;
|
||||||
private final Map<Integer, String> 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<String, RadialRecord> recordMap = new HashMap<>();
|
|
||||||
private final List<String> velocityGroup = new ArrayList<>(Arrays.asList("V", "W"));
|
|
||||||
private Dimension radialDim, scanDim, gateRDim, gateVDim;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -45,6 +35,15 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo {
|
|||||||
this.meteoDataType = MeteoDataType.RADAR;
|
this.meteoDataType = MeteoDataType.RADAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get radar data type
|
||||||
|
* @return Radar data type
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public RadarDataType getRadarDataType() {
|
||||||
|
return RadarDataType.STANDARD;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get generic header
|
* Get generic header
|
||||||
* @return Generic header
|
* @return Generic header
|
||||||
@ -85,78 +84,6 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo {
|
|||||||
return this.radialHeaders;
|
return this.radialHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get record map
|
|
||||||
* @return Record map
|
|
||||||
*/
|
|
||||||
public Map<String, RadialRecord> 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
|
@Override
|
||||||
public boolean isValidFile(RandomAccessFile raf) {
|
public boolean isValidFile(RandomAccessFile raf) {
|
||||||
try {
|
try {
|
||||||
@ -229,6 +156,8 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo {
|
|||||||
try {
|
try {
|
||||||
genericHeader = new GenericHeader(raf);
|
genericHeader = new GenericHeader(raf);
|
||||||
siteConfig = new SiteConfig(raf);
|
siteConfig = new SiteConfig(raf);
|
||||||
|
this.antennaHeight = siteConfig.antennaHeight;
|
||||||
|
this.beamWidthVert = siteConfig.beamWidthVert;
|
||||||
|
|
||||||
//Add global attributes
|
//Add global attributes
|
||||||
this.addAttribute(new Attribute("StationCode", siteConfig.siteCode));
|
this.addAttribute(new Attribute("StationCode", siteConfig.siteCode));
|
||||||
@ -248,8 +177,14 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo {
|
|||||||
|
|
||||||
//Read radial data
|
//Read radial data
|
||||||
cutConfigs = new ArrayList<>();
|
cutConfigs = new ArrayList<>();
|
||||||
|
CutConfig cutConfig;
|
||||||
for (int i = 0; i < taskConfig.cutNumber; i++) {
|
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<>();
|
radialHeaders = new ArrayList<>();
|
||||||
byte[] rhBytes = new byte[RadialHeader.length];
|
byte[] rhBytes = new byte[RadialHeader.length];
|
||||||
@ -264,8 +199,8 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo {
|
|||||||
} else {
|
} else {
|
||||||
record = new RadialRecord(product);
|
record = new RadialRecord(product);
|
||||||
record.setBinLength(momentHeader.binLength);
|
record.setBinLength(momentHeader.binLength);
|
||||||
record.scale = momentHeader.scale;
|
record.scale = 1.f / momentHeader.scale;
|
||||||
record.offset = momentHeader.offset;
|
record.offset = -momentHeader.offset / (float) momentHeader.scale;
|
||||||
this.recordMap.put(product, record);
|
this.recordMap.put(product, record);
|
||||||
}
|
}
|
||||||
if (radialHeader.radialNumber == 1) {
|
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<Float> elevList = refRadialRecord.elevation.get(i);
|
|
||||||
List<Float> 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<Float> elevList = velRadialRecord.elevation.get(i);
|
|
||||||
List<Float> 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<String> getProducts() {
|
|
||||||
List<String> products = new ArrayList<>();
|
|
||||||
for (String product : this.recordMap.keySet()) {
|
|
||||||
products.add(product);
|
|
||||||
}
|
|
||||||
|
|
||||||
return products;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get scan elevations
|
* Get scan elevations
|
||||||
* @return Scan elevations
|
* @return Scan elevations
|
||||||
@ -520,104 +280,6 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo {
|
|||||||
return elevations;
|
return elevations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get scan elevations
|
|
||||||
* @return Scan elevations
|
|
||||||
*/
|
|
||||||
public List<Float> 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<Array> 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
|
@Override
|
||||||
public List<Attribute> getGlobalAttributes() {
|
public List<Attribute> getGlobalAttributes() {
|
||||||
return this.attributes;
|
return this.attributes;
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package org.meteoinfo.data.meteodata.radar;
|
||||||
|
|
||||||
|
public enum RadarDataType {
|
||||||
|
STANDARD,
|
||||||
|
SAB,
|
||||||
|
CC,
|
||||||
|
CC20,
|
||||||
|
SC,
|
||||||
|
PA,
|
||||||
|
UN_DEFINED
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,8 +19,8 @@ public class RadialRecord {
|
|||||||
private int binLength;
|
private int binLength;
|
||||||
private DataType dataType;
|
private DataType dataType;
|
||||||
private int fillValue;
|
private int fillValue;
|
||||||
public int scale;
|
public float scale = 1;
|
||||||
public int offset;
|
public float offset = 0;
|
||||||
public List<Float> fixedElevation = new ArrayList<>();
|
public List<Float> fixedElevation = new ArrayList<>();
|
||||||
public List<List<Float>> elevation = new ArrayList<>();
|
public List<List<Float>> elevation = new ArrayList<>();
|
||||||
public List<List<Float>> azimuth = new ArrayList<>();
|
public List<List<Float>> azimuth = new ArrayList<>();
|
||||||
@ -113,6 +113,18 @@ public class RadialRecord {
|
|||||||
this.data.add(new ArrayList<>());
|
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
|
* Add a data bytes
|
||||||
* @param bytes Data bytes
|
* @param bytes Data bytes
|
||||||
@ -151,7 +163,7 @@ public class RadialRecord {
|
|||||||
array = Array.factory(this.dataType, new int[]{bytes.length});
|
array = Array.factory(this.dataType, new int[]{bytes.length});
|
||||||
for (int i = 0; i < bytes.length; i++) {
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
v = (float) DataType.unsignedByteToShort(bytes[i]);
|
v = (float) DataType.unsignedByteToShort(bytes[i]);
|
||||||
v = (v - offset) / scale;
|
v = v * scale + offset;
|
||||||
array.setFloat(i, v);
|
array.setFloat(i, v);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -160,7 +172,7 @@ public class RadialRecord {
|
|||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
short s = DataConvert.bytes2Short(new byte[]{bytes[i*2], bytes[i*2+1]}, ByteOrder.LITTLE_ENDIAN);
|
short s = DataConvert.bytes2Short(new byte[]{bytes[i*2], bytes[i*2+1]}, ByteOrder.LITTLE_ENDIAN);
|
||||||
v = (float) DataType.unsignedShortToInt(s);
|
v = (float) DataType.unsignedShortToInt(s);
|
||||||
v = (v - offset) / scale;
|
v = v * scale + offset;
|
||||||
array.setFloat(i, v);
|
array.setFloat(i, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +256,7 @@ public class RadialRecord {
|
|||||||
* @param dataInfo The data info
|
* @param dataInfo The data info
|
||||||
* @param dimensions Dimensions
|
* @param dimensions Dimensions
|
||||||
*/
|
*/
|
||||||
public void makeVariable(CMARadarBaseDataInfo dataInfo, Dimension[] dimensions) {
|
public void makeVariable(BaseRadarDataInfo dataInfo, Dimension[] dimensions) {
|
||||||
Variable variable = new Variable();
|
Variable variable = new Variable();
|
||||||
variable.setName(this.product);
|
variable.setName(this.product);
|
||||||
variable.setDataType(this.dataType);
|
variable.setDataType(this.dataType);
|
||||||
@ -261,7 +273,7 @@ public class RadialRecord {
|
|||||||
* @param dataInfo The data info
|
* @param dataInfo The data info
|
||||||
* @param xyzDim xyz dimension
|
* @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++) {
|
for (int i = 0; i < getScanNumber(); i++) {
|
||||||
String suffix = "_s" + String.valueOf(i + 1);
|
String suffix = "_s" + String.valueOf(i + 1);
|
||||||
Dimension radialDim = new Dimension(DimensionType.Y);
|
Dimension radialDim = new Dimension(DimensionType.Y);
|
||||||
@ -495,7 +507,7 @@ public class RadialRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!Float.isNaN(v))
|
if (!Float.isNaN(v))
|
||||||
v = (v - this.offset) / this.scale;
|
v = v * scale + this.offset;
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@ -575,7 +587,7 @@ public class RadialRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!Float.isNaN(v))
|
if (!Float.isNaN(v))
|
||||||
v = (v - this.offset) / this.scale;
|
v = v * this.scale + this.offset;
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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<String> getProducts() {
|
||||||
|
List<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<MeteoInfo File="milconfig.xml" Type="configurefile">
|
<MeteoInfo File="milconfig.xml" Type="configurefile">
|
||||||
<Path OpenPath="D:\Working\MIScript\Jython\mis\dataframe">
|
<Path OpenPath="D:\Working\MIScript\Jython\mis\io\radar\cinrad">
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\scatter"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\scatter"/>
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite"/>
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart"/>
|
||||||
@ -12,21 +12,21 @@
|
|||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\netcdf"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\netcdf"/>
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar"/>
|
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar\cinrad"/>
|
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe\series"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe\series"/>
|
||||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe"/>
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe"/>
|
||||||
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar"/>
|
||||||
|
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar\cinrad"/>
|
||||||
</Path>
|
</Path>
|
||||||
<File>
|
<File>
|
||||||
<OpenedFiles>
|
<OpenedFiles>
|
||||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\test_read_1.py"/>
|
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\test_read_1.py"/>
|
||||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_cma_base_cappi.py"/>
|
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_cma_base_cappi.py"/>
|
||||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\dataframe\isin_1.py"/>
|
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_sc_1.py"/>
|
||||||
</OpenedFiles>
|
</OpenedFiles>
|
||||||
<RecentFiles>
|
<RecentFiles>
|
||||||
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\test_read_1.py"/>
|
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\test_read_1.py"/>
|
||||||
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_cma_base_cappi.py"/>
|
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_cma_base_cappi.py"/>
|
||||||
<RecentFile File="D:\Working\MIScript\Jython\mis\dataframe\isin_1.py"/>
|
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_sc_1.py"/>
|
||||||
</RecentFiles>
|
</RecentFiles>
|
||||||
</File>
|
</File>
|
||||||
<Font>
|
<Font>
|
||||||
@ -34,5 +34,5 @@
|
|||||||
</Font>
|
</Font>
|
||||||
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
|
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
|
||||||
<Figure DoubleBuffering="true"/>
|
<Figure DoubleBuffering="true"/>
|
||||||
<Startup MainFormLocation="-7,0" MainFormSize="1354,829"/>
|
<Startup MainFormLocation="-7,-7" MainFormSize="1293,765"/>
|
||||||
</MeteoInfo>
|
</MeteoInfo>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user