mirror of
https://github.com/meteoinfo/MeteoInfo.git
synced 2025-12-08 20:36:05 +00:00
support SA/SB, SC, PA, CC radar data files
This commit is contained in:
parent
ec4e09f946
commit
da789f25be
@ -221,10 +221,10 @@ public class GraphicFactory {
|
||||
PolylineShape pls;
|
||||
List<PointD> points;
|
||||
double x, y;
|
||||
IndexIterator xIter = xdata.getIndexIterator();
|
||||
IndexIterator yIter = ydata.getIndexIterator();
|
||||
ColorBreak cb;
|
||||
if (xdata.getRank() == 1) {
|
||||
IndexIterator xIter = xdata.getIndexIterator();
|
||||
IndexIterator yIter = ydata.getIndexIterator();
|
||||
points = new ArrayList<>();
|
||||
int i = 0;
|
||||
while (xIter.hasNext()) {
|
||||
@ -265,14 +265,16 @@ public class GraphicFactory {
|
||||
int[] shape = xdata.getShape();
|
||||
int yn = shape[0];
|
||||
int xn = shape[1];
|
||||
for (int j = 0; j < yn; j++) {
|
||||
points = new ArrayList<>();
|
||||
cb = cbs.get(j);
|
||||
Index2D xIndex = (Index2D) xdata.getIndex();
|
||||
Index2D yIndex = (Index2D) ydata.getIndex();
|
||||
for (int i = 0; i < xn; i++) {
|
||||
x = xIter.getDoubleNext();
|
||||
y = yIter.getDoubleNext();
|
||||
//x = xdata.getDouble(j * xn + i);
|
||||
//y = ydata.getDouble(j * xn + i);
|
||||
points = new ArrayList<>();
|
||||
cb = cbs.get(i);
|
||||
for (int j = 0; j < yn; j++) {
|
||||
xIndex.set(j, i);
|
||||
yIndex.set(j, i);
|
||||
x = xdata.getDouble(xIndex);
|
||||
y = ydata.getDouble(yIndex);
|
||||
if (Double.isNaN(y) || Double.isNaN(x)) {
|
||||
if (points.isEmpty()) {
|
||||
continue;
|
||||
|
||||
@ -67,7 +67,7 @@ import java.util.zip.ZipInputStream;
|
||||
public static String getVersion(){
|
||||
String version = GlobalUtil.class.getPackage().getImplementationVersion();
|
||||
if (version == null || version.equals("")) {
|
||||
version = "3.8.9";
|
||||
version = "3.8.10";
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
@ -39,9 +39,6 @@ import java.time.LocalDateTime;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.meteoinfo.data.meteodata.numpy.NumpyDataInfo;
|
||||
import org.meteoinfo.data.meteodata.radar.CMARadarBaseDataInfo;
|
||||
import org.meteoinfo.data.meteodata.radar.RadarDataType;
|
||||
import org.meteoinfo.data.meteodata.radar.RadarDataUtil;
|
||||
import org.meteoinfo.ndarray.math.ArrayMath;
|
||||
import org.meteoinfo.projection.ProjectionInfo;
|
||||
|
||||
@ -8,6 +8,8 @@ import org.meteoinfo.ndarray.*;
|
||||
import org.meteoinfo.ndarray.math.ArrayMath;
|
||||
import org.meteoinfo.ndarray.math.ArrayUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@ -27,8 +29,8 @@ public abstract class BaseRadarDataInfo extends DataInfo {
|
||||
protected Dimension radialDim, scanDim, gateRDim, gateVDim;
|
||||
protected float antennaHeight = 0;
|
||||
protected float beamWidthVert = 1.f;
|
||||
protected int logResolution = 1000;
|
||||
protected int dopplerResolution = 1000;
|
||||
protected float logResolution = 1000;
|
||||
protected float dopplerResolution = 1000;
|
||||
|
||||
/**
|
||||
* Get record map
|
||||
@ -56,6 +58,23 @@ public abstract class BaseRadarDataInfo extends DataInfo {
|
||||
return velocityGroup.contains(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDataInfo(String fileName) {
|
||||
this.fileName = fileName;
|
||||
try {
|
||||
InputStream inputStream = RadarDataUtil.getInputStream(fileName);
|
||||
readDataInfo(inputStream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data info
|
||||
* @param is The InputStream
|
||||
*/
|
||||
abstract void readDataInfo(InputStream is);
|
||||
|
||||
protected void makeRefVariables(RadialRecord refRadialRecord) {
|
||||
Dimension[] dimensions = new Dimension[]{scanDim, radialDim, gateRDim};
|
||||
for (RadialRecord radialRecord : this.recordMap.values()) {
|
||||
@ -293,6 +312,12 @@ public abstract class BaseRadarDataInfo extends DataInfo {
|
||||
}
|
||||
}
|
||||
|
||||
Attribute mvAttr = variable.findAttribute("missing_value");
|
||||
if (mvAttr != null) {
|
||||
Number missingValue = mvAttr.getNumericValue();
|
||||
ArrayMath.missingToNaN(dataArray, missingValue);
|
||||
}
|
||||
|
||||
Attribute aoAttr = variable.findAttribute("add_offset");
|
||||
Attribute sfAttr = variable.findAttribute("scale_factor");
|
||||
if (aoAttr != null || sfAttr != null) {
|
||||
|
||||
@ -13,123 +13,118 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class CCRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo {
|
||||
|
||||
private List<CutConfig> cutConfigs;
|
||||
private int radarHeaderSize = 1024;
|
||||
private int perRadialSize = 3000;
|
||||
private int messageHeaderSize = 28;
|
||||
|
||||
@Override
|
||||
public boolean isValidFile(java.io.RandomAccessFile raf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setScaleOffset(RadialRecord record, int vResolution) {
|
||||
void setScaleOffset(RadialRecord record) {
|
||||
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;
|
||||
record.scale = 0.1f;
|
||||
record.offset = 0.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDataInfo(String fileName) {
|
||||
this.fileName = fileName;
|
||||
if (fileName.endsWith(".bz2")) {
|
||||
void readDataInfo(InputStream is) {
|
||||
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);
|
||||
}
|
||||
byte[] headerBytes = new byte[this.radarHeaderSize];
|
||||
is.read(headerBytes);
|
||||
byte[] bytes = Arrays.copyOf(headerBytes, RadarHeader.length);
|
||||
RadarHeader radarHeader = new RadarHeader(bytes);
|
||||
|
||||
int sweepN = radarHeader.getSweepNumber();
|
||||
cutConfigs = new ArrayList<>();
|
||||
int idx = RadarHeader.length;
|
||||
for (int i = 0; i < sweepN; i++) {
|
||||
bytes = Arrays.copyOfRange(headerBytes, idx, idx + CutConfig.length);
|
||||
idx += CutConfig.length;
|
||||
CutConfig cutConfig = new CutConfig(bytes);
|
||||
cutConfigs.add(cutConfig);
|
||||
if (i == 0) {
|
||||
this.logResolution = cutConfig.usBindWidth;
|
||||
this.dopplerResolution = cutConfig.usBindWidth;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
idx = 878;
|
||||
bytes = Arrays.copyOfRange(headerBytes, idx, idx + RadarHeader2.length);
|
||||
RadarHeader2 radarHeader2 = new RadarHeader2(bytes);
|
||||
|
||||
List<String> products = new ArrayList<>(Arrays.asList("dBZ", "V", "W"));
|
||||
for (String product : products) {
|
||||
RadialRecord record = new RadialRecord(product);
|
||||
record.setRadarDataType(RadarDataType.CC);
|
||||
record.setBinLength(2);
|
||||
record.setDataType(DataType.SHORT);
|
||||
setScaleOffset(record);
|
||||
this.recordMap.put(product, record);
|
||||
}
|
||||
if (radialHeader.radialNumber == 1) {
|
||||
record.fixedElevation.add(radialHeader.getElevation());
|
||||
|
||||
for (int i = 0; i < sweepN; i++) {
|
||||
CutConfig cutConfig = cutConfigs.get(i);
|
||||
int radialN = cutConfig.usBinNumber;
|
||||
float azimuth = 0;
|
||||
float aDelta = 360.f / cutConfig.usRecordNumber;
|
||||
for (int j = 0; j < cutConfig.usRecordNumber; j++) {
|
||||
bytes = new byte[this.perRadialSize];
|
||||
is.read(bytes);
|
||||
idx = 0;
|
||||
for (String product : products) {
|
||||
RadialRecord record = this.recordMap.get(product);
|
||||
if (j == 0) {
|
||||
record.fixedElevation.add(cutConfig.getAngle());
|
||||
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.disResolution.add((float) cutConfig.usBindWidth);
|
||||
record.distance.add(ArrayUtil.arrayRange1(300,
|
||||
radialN, cutConfig.usBindWidth));
|
||||
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]);
|
||||
record.elevation.get(record.elevation.size() - 1).add(cutConfig.getAngle());
|
||||
record.addAzimuth(azimuth);
|
||||
byte[] data = Arrays.copyOfRange(bytes, idx, idx + 2 * radialN);
|
||||
idx += 2 * radialN;
|
||||
Array dataArray = Array.factory(record.getDataType(), new int[]{radialN});
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
for (int k = 0; k < radialN; k++) {
|
||||
dataArray.setShort(k, byteBuffer.getShort());
|
||||
}
|
||||
} else {
|
||||
if (dataLength < 460) {
|
||||
is.read(new byte[460 - dataLength]);
|
||||
record.addDataArray(dataArray);
|
||||
}
|
||||
azimuth += aDelta;
|
||||
}
|
||||
}
|
||||
if (!radialHeader.hasDopplerData()) {
|
||||
is.read(new byte[920 + 920]);
|
||||
}
|
||||
is.read(new byte[4]);
|
||||
index += 1;
|
||||
}
|
||||
is.close();
|
||||
|
||||
this.addAttribute(new Attribute("Country", radarHeader.cCountry));
|
||||
this.addAttribute(new Attribute("Province", radarHeader.cProvince));
|
||||
this.addAttribute(new Attribute("StationName", radarHeader.cStation));
|
||||
this.addAttribute(new Attribute("StationCode", radarHeader.cStationNumber));
|
||||
this.addAttribute(new Attribute("StationLongitude", radarHeader.getLongitude()));
|
||||
this.addAttribute(new Attribute("StationLatitude", radarHeader.getLatitude()));
|
||||
this.addAttribute(new Attribute("AntennaHeight", radarHeader.getHeight()));
|
||||
this.addAttribute(new Attribute("featureType", "RADIAL"));
|
||||
this.addAttribute(new Attribute("DataType", "Radial"));
|
||||
this.addAttribute(new Attribute("RadarDataType", "CC"));
|
||||
|
||||
//Add dimensions and variables
|
||||
RadialRecord refRadialRecord = this.recordMap.get("dBZ");
|
||||
@ -165,111 +160,287 @@ public class CCRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo
|
||||
return RadarDataType.CC;
|
||||
}
|
||||
|
||||
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;
|
||||
/**
|
||||
* Radar header inner class
|
||||
*/
|
||||
static class RadarHeader {
|
||||
public static int length = 218;
|
||||
public String cFileType; //16 bytes CINRADC
|
||||
public String cCountry; //30 bytes, country name
|
||||
public String cProvince; //20 bytes, province name
|
||||
public String cStation; //40 bytes, station name
|
||||
public String cStationNumber; //10 bytes, station ID
|
||||
public String cRadarType; //20 bytes, radar type
|
||||
public String cLongitude; //16 bytes, longitude string
|
||||
public String cLatitude; //16 bytes, latitude string
|
||||
public int lLongitudeValue; //longitude
|
||||
public int lLatitudeValue; //latitude
|
||||
public int lHeight; //height
|
||||
public short sMaxAngle;
|
||||
public short sOptAngle;
|
||||
public short ucSYear1;
|
||||
public short ucSYear2;
|
||||
public short ucSMonth;
|
||||
public short ucSDay;
|
||||
public short ucSHour;
|
||||
public short ucSMinute;
|
||||
public short ucSSecond;
|
||||
public short ucTimeFrom;
|
||||
public short ucEYear1;
|
||||
public short ucEYear2;
|
||||
public short ucEMonth;
|
||||
public short ucEDay;
|
||||
public short ucEHour;
|
||||
public short ucEMinute;
|
||||
public short ucESecond;
|
||||
public short ucScanMode;
|
||||
public int ulSmilliSecond;
|
||||
public short usRHIA;
|
||||
public short sRHIL;
|
||||
public short sRHIH;
|
||||
public int usEchoType;
|
||||
public int usProdCode;
|
||||
public short ucCalibration;
|
||||
public byte[] remain1; //3 bytes
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param is InputStream
|
||||
* @param bytes The byte array
|
||||
* @throws IOException
|
||||
*/
|
||||
public RadialHeader(byte[] inBytes) throws IOException {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(inBytes);
|
||||
public RadarHeader(byte[] bytes) throws IOException {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
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();
|
||||
bytes = new byte[16];
|
||||
byteBuffer.get(bytes);
|
||||
cFileType = new String(bytes);
|
||||
bytes = new byte[30];
|
||||
byteBuffer.get(bytes);
|
||||
cCountry = new String(bytes, "GB2312");
|
||||
bytes = new byte[20];
|
||||
byteBuffer.get(bytes);
|
||||
cProvince = new String(bytes, "GB2312");
|
||||
bytes = new byte[40];
|
||||
byteBuffer.get(bytes);
|
||||
cStation = new String(bytes, "GB2312");
|
||||
bytes = new byte[10];
|
||||
byteBuffer.get(bytes);
|
||||
cStationNumber = new String(bytes, "GB2312");
|
||||
bytes = new byte[20];
|
||||
byteBuffer.get(bytes);
|
||||
cRadarType = new String(bytes, "GB2312");
|
||||
bytes = new byte[16];
|
||||
byteBuffer.get(bytes);
|
||||
cLongitude = new String(bytes, "GB2312");
|
||||
bytes = new byte[16];
|
||||
byteBuffer.get(bytes);
|
||||
cLatitude = new String(bytes, "GB2312");
|
||||
lLongitudeValue = byteBuffer.getInt();
|
||||
lLatitudeValue = byteBuffer.getInt();
|
||||
lHeight = byteBuffer.getInt();
|
||||
sMaxAngle = byteBuffer.getShort();
|
||||
sOptAngle = byteBuffer.getShort();
|
||||
ucSYear1 = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucSYear2 = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucSMonth = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucSDay = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucSHour = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucSMinute = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucSSecond = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucTimeFrom = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucEYear1 = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucEYear2 = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucEMonth = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucEDay = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucEHour = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucEMinute = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucESecond = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
ucScanMode = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
if (ucScanMode < 100 && ucScanMode != 10) {
|
||||
throw new IOException("Error reading CINRAD CC data: Unsupported product: RHI/FFT");
|
||||
}
|
||||
|
||||
ulSmilliSecond = byteBuffer.getInt();
|
||||
usRHIA = byteBuffer.getShort();
|
||||
sRHIL = byteBuffer.getShort();
|
||||
sRHIH = byteBuffer.getShort();
|
||||
usEchoType = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
if (usEchoType != 0x408a) // only support vppi at this moment
|
||||
throw new IOException("Error reading CINRAD CC data: Unsupported level 2 data");
|
||||
|
||||
usProdCode = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
if (usProdCode != 0x8003) // only support vppi at this moment
|
||||
throw new IOException("Error reading CINRAD CC data: Unsupported product: RHI/FFT");
|
||||
|
||||
ucCalibration = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Has reflectivity data or not
|
||||
* @return Has reflectivity data
|
||||
* Get longitude
|
||||
* @return Longitude
|
||||
*/
|
||||
public boolean hasReflectivityData() {
|
||||
return gatesNumberOfReflectivity > 0;
|
||||
public float getLongitude() {
|
||||
return lLongitudeValue / 3600000.f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has doppler data or not
|
||||
* @return Has doppler data
|
||||
* Get latitude
|
||||
* @return Latitude
|
||||
*/
|
||||
public boolean hasDopplerData() {
|
||||
return gatesNumberOfDoppler > 0;
|
||||
public float getLatitude() {
|
||||
return lLatitudeValue / 3600000.f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product names
|
||||
* @return Product names
|
||||
* Get height
|
||||
* @return Height
|
||||
*/
|
||||
public List<String> getProducts() {
|
||||
List<String> products = new ArrayList<>();
|
||||
if (hasReflectivityData()) {
|
||||
products.add("dBZ");
|
||||
}
|
||||
if (hasDopplerData()) {
|
||||
products.add("V");
|
||||
products.add("W");
|
||||
}
|
||||
|
||||
return products;
|
||||
public float getHeight() {
|
||||
return lHeight / 1000.f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get azimuth
|
||||
* @return Azimuth
|
||||
* Get start time
|
||||
* @return Start time
|
||||
*/
|
||||
public float getAzimuth() {
|
||||
return azimuth / 8.f * 180.f / 4096.f;
|
||||
public LocalDateTime getStartTime() {
|
||||
int sYear = ucSYear1 * 100 + ucSYear2;
|
||||
return LocalDateTime.of(sYear, ucSMonth, ucSDay, ucSHour, ucSMinute, ucSSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get elevation
|
||||
* @return Elevation
|
||||
* Get end time
|
||||
* @return end time
|
||||
*/
|
||||
public float getElevation() {
|
||||
return elevation / 8.f * 180.f / 4096.f;
|
||||
public LocalDateTime getEndTime() {
|
||||
int eYear = ucEYear1 * 100 + ucEYear2;
|
||||
return LocalDateTime.of(eYear, ucEMonth, ucEDay, ucEHour, ucEMinute, ucESecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sweep number
|
||||
* @return Sweep number
|
||||
*/
|
||||
public int getSweepNumber() {
|
||||
int sweepN = 0;
|
||||
if (ucScanMode == 10) {
|
||||
sweepN = 1;
|
||||
} else if (ucScanMode >= 100) {
|
||||
sweepN = ucScanMode - 100;
|
||||
}
|
||||
|
||||
return sweepN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut configure inner class
|
||||
*/
|
||||
static class CutConfig {
|
||||
public static int length = 22;
|
||||
public int usMaxV;
|
||||
public int usMaxL;
|
||||
public int usBindWidth;
|
||||
public int usBinNumber;
|
||||
public int usRecordNumber;
|
||||
public int usArotate;
|
||||
public int usPrf1;
|
||||
public int usPrf2;
|
||||
public int usSpulseW;
|
||||
public short usAngle;
|
||||
public short cSweepStatus;
|
||||
public short cAmbiguousp;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param bytes The byte array
|
||||
*/
|
||||
public CutConfig(byte[] bytes) throws IOException {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
usMaxV = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usMaxL = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usBindWidth = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usBinNumber = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usRecordNumber = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usArotate = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usPrf1 = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usPrf2 = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usSpulseW = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usAngle = byteBuffer.getShort();
|
||||
cSweepStatus = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
cAmbiguousp = DataType.unsignedByteToShort(byteBuffer.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get angle
|
||||
* @return Angle
|
||||
*/
|
||||
public float getAngle() {
|
||||
return usAngle / 100.f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radar header 2 inner class
|
||||
*/
|
||||
static class RadarHeader2 {
|
||||
public static int length = 146;
|
||||
public byte[] remain2; //2 bytes
|
||||
public int lAntennaG;
|
||||
public int lPower;
|
||||
public int lWavelength;
|
||||
|
||||
public int usBeamH;
|
||||
public int usBeamL;
|
||||
public int usPolarization;
|
||||
public int usLogA;
|
||||
public int usLineA;
|
||||
public int usAGCP;
|
||||
public int usFreqMode;
|
||||
public int usFreqRepeat;
|
||||
public int usPPPPulse;
|
||||
public int usFFTPoint;
|
||||
public int usProcessType;
|
||||
|
||||
public short ucClutterT;
|
||||
public short cSidelobe;
|
||||
public short ucVelocityT;
|
||||
public short ucFilderP;
|
||||
public short ucNoiseT;
|
||||
public short ucSQIT;
|
||||
public short ucIntensityC;
|
||||
public short ucIntensityR;
|
||||
public short ucCalNoise;
|
||||
public short ucCalPower;
|
||||
public short ucCalPulseWidth;
|
||||
public short ucCalWorkFreq;
|
||||
public short ucCalLog;
|
||||
|
||||
public byte[] remain3; //92 bytes
|
||||
public int liDataOffset;
|
||||
public byte[] remain4; //1 byte
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param bytes The byte array
|
||||
*/
|
||||
public RadarHeader2(byte[] bytes) throws IOException {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
byteBuffer.position(2);
|
||||
lAntennaG = byteBuffer.getInt();
|
||||
lPower = byteBuffer.getInt();
|
||||
lWavelength = byteBuffer.getInt();
|
||||
|
||||
usBeamH = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usBeamL = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usPolarization = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usLogA = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usLineA = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usAGCP = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usFreqMode = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
usFreqRepeat = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,496 +0,0 @@
|
||||
package org.meteoinfo.data.meteodata.radar;
|
||||
|
||||
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
|
||||
import org.meteoinfo.common.DataConvert;
|
||||
import org.meteoinfo.data.GridArray;
|
||||
import org.meteoinfo.data.GridData;
|
||||
import org.meteoinfo.data.dimarray.Dimension;
|
||||
import org.meteoinfo.data.dimarray.DimensionType;
|
||||
import org.meteoinfo.data.meteodata.*;
|
||||
import org.meteoinfo.ndarray.*;
|
||||
import org.meteoinfo.ndarray.math.ArrayMath;
|
||||
import org.meteoinfo.ndarray.math.ArrayUtil;
|
||||
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.io.*;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CMARadarBaseDataInfo extends BaseRadarDataInfo implements IRadarDataInfo {
|
||||
|
||||
private GenericHeader genericHeader;
|
||||
private SiteConfig siteConfig;
|
||||
private TaskConfig taskConfig;
|
||||
private List<CutConfig> cutConfigs;
|
||||
private List<RadialHeader> radialHeaders;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public CMARadarBaseDataInfo() {
|
||||
this.meteoDataType = MeteoDataType.RADAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radar data type
|
||||
* @return Radar data type
|
||||
*/
|
||||
@Override
|
||||
public RadarDataType getRadarDataType() {
|
||||
return RadarDataType.STANDARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generic header
|
||||
* @return Generic header
|
||||
*/
|
||||
public GenericHeader getGenericHeader() {
|
||||
return this.genericHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site config
|
||||
* @return Site config
|
||||
*/
|
||||
public SiteConfig getSiteConfig() {
|
||||
return this.siteConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task config
|
||||
* @return Task config
|
||||
*/
|
||||
public TaskConfig getTaskConfig() {
|
||||
return this.taskConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cut config list
|
||||
* @return Cut config list
|
||||
*/
|
||||
public List<CutConfig> getCutConfigs() {
|
||||
return this.cutConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radial header list
|
||||
* @return Radial header list
|
||||
*/
|
||||
public List<RadialHeader> getRadialHeaders() {
|
||||
return this.radialHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidFile(RandomAccessFile raf) {
|
||||
try {
|
||||
raf.seek(0);
|
||||
byte[] bytes = new byte[4];
|
||||
raf.read(bytes);
|
||||
int magic = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
if (magic == 1297371986) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the data file format
|
||||
* @param fileName Data file name
|
||||
* @return Boolean
|
||||
*/
|
||||
public static boolean canOpen(String fileName) {
|
||||
try {
|
||||
byte[] bytes = new byte[4];
|
||||
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();
|
||||
}
|
||||
int magic = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
if (magic == 1297371986) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
//RandomAccessFile raf = new RandomAccessFile(fileName, "r");
|
||||
//readDataInfo(raf);
|
||||
BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(fileName)));
|
||||
readDataInfo(inputStream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void readDataInfo(InputStream raf) {
|
||||
try {
|
||||
genericHeader = new GenericHeader(raf);
|
||||
siteConfig = new SiteConfig(raf);
|
||||
this.antennaHeight = siteConfig.antennaHeight;
|
||||
this.beamWidthVert = siteConfig.beamWidthVert;
|
||||
|
||||
//Add global attributes
|
||||
this.addAttribute(new Attribute("StationCode", siteConfig.siteCode));
|
||||
this.addAttribute(new Attribute("StationName", siteConfig.siteName));
|
||||
this.addAttribute(new Attribute("StationLatitude", siteConfig.latitude));
|
||||
this.addAttribute(new Attribute("StationLongitude", siteConfig.longitude));
|
||||
this.addAttribute(new Attribute("AntennaHeight", siteConfig.antennaHeight));
|
||||
this.addAttribute(new Attribute("GroundHeight", siteConfig.groundHeight));
|
||||
this.addAttribute(new Attribute("RadarType", siteConfig.getRadarType()));
|
||||
this.addAttribute(new Attribute("featureType", "RADIAL"));
|
||||
this.addAttribute(new Attribute("DataType", "Radial"));
|
||||
|
||||
//Read task configuration
|
||||
taskConfig = new TaskConfig(raf);
|
||||
this.addAttribute(new Attribute("TaskName", taskConfig.taskName));
|
||||
this.addAttribute(new Attribute("TaskDescription", taskConfig.taskDescription));
|
||||
|
||||
//Read radial data
|
||||
cutConfigs = new ArrayList<>();
|
||||
CutConfig cutConfig;
|
||||
for (int i = 0; i < taskConfig.cutNumber; i++) {
|
||||
cutConfig = new CutConfig(raf);
|
||||
cutConfigs.add(cutConfig);
|
||||
if (i == 0) {
|
||||
this.logResolution = cutConfig.logResolution;
|
||||
this.dopplerResolution = cutConfig.dopplerResolution;
|
||||
}
|
||||
}
|
||||
radialHeaders = new ArrayList<>();
|
||||
byte[] rhBytes = new byte[RadialHeader.length];
|
||||
while (raf.read(rhBytes) != -1) {
|
||||
RadialHeader radialHeader = new RadialHeader(rhBytes);
|
||||
for (int i = 0; i < radialHeader.momentNumber; i++) {
|
||||
MomentHeader momentHeader = new MomentHeader(raf);
|
||||
String product = this.productMap.get(momentHeader.dataType);
|
||||
RadialRecord record;
|
||||
if (this.recordMap.containsKey(product)) {
|
||||
record = this.recordMap.get(product);
|
||||
} else {
|
||||
record = new RadialRecord(product);
|
||||
record.setBinLength(momentHeader.binLength);
|
||||
record.scale = 1.f / momentHeader.scale;
|
||||
record.offset = -momentHeader.offset / (float) momentHeader.scale;
|
||||
this.recordMap.put(product, record);
|
||||
}
|
||||
if (radialHeader.radialNumber == 1) {
|
||||
record.fixedElevation.add(cutConfigs.get(radialHeader.elevationNumber - 1).elevation);
|
||||
record.elevation.add(new ArrayList<>());
|
||||
record.azimuth.add(new ArrayList<>());
|
||||
record.azimuthMinIndex.add(0);
|
||||
if (isVelocityGroup(record)) {
|
||||
record.disResolution.add(cutConfigs.get(radialHeader.elevationNumber - 1).dopplerResolution);
|
||||
record.distance.add(ArrayUtil.arrayRange1(0, momentHeader.dataLength / momentHeader.binLength,
|
||||
cutConfigs.get(radialHeader.elevationNumber - 1).dopplerResolution));
|
||||
} else {
|
||||
record.disResolution.add(cutConfigs.get(radialHeader.elevationNumber - 1).logResolution);
|
||||
record.distance.add(ArrayUtil.arrayRange1(0, momentHeader.dataLength / momentHeader.binLength,
|
||||
cutConfigs.get(radialHeader.elevationNumber - 1).logResolution));
|
||||
}
|
||||
record.newScanData();
|
||||
}
|
||||
record.elevation.get(record.elevation.size() - 1).add(radialHeader.elevation);
|
||||
record.addAzimuth(radialHeader.azimuth);
|
||||
byte[] bytes = new byte[momentHeader.dataLength];
|
||||
raf.read(bytes);
|
||||
record.addDataBytes(bytes);
|
||||
}
|
||||
radialHeaders.add(radialHeader);
|
||||
}
|
||||
raf.close();
|
||||
|
||||
//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);
|
||||
|
||||
/*Dimension xyzDim = new Dimension(DimensionType.OTHER);
|
||||
xyzDim.setShortName("xyz");
|
||||
xyzDim.setDimValue(Array.factory(DataType.INT, new int[]{3}, new int[]{1,2,3}));
|
||||
this.addDimension(xyzDim);
|
||||
for (String product : this.recordMap.keySet()) {
|
||||
this.recordMap.get(product).makeVariables(this, xyzDim);
|
||||
}*/
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scan elevations
|
||||
* @return Scan elevations
|
||||
*/
|
||||
public List<Float> getElevations() {
|
||||
List<Float> elevations = new ArrayList<>();
|
||||
for (CutConfig cutConfig : this.cutConfigs) {
|
||||
if (!elevations.contains(cutConfig.elevation))
|
||||
elevations.add(cutConfig.elevation);
|
||||
}
|
||||
|
||||
return elevations;
|
||||
}
|
||||
|
||||
@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 = (float) siteConfig.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 = (float) siteConfig.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 = (float) siteConfig.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 = this.siteConfig.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 = (float) siteConfig.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 = this.siteConfig.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.siteConfig.beamWidthVert / 2;
|
||||
float binRes = this.cutConfigs.get(0).logResolution;
|
||||
float height = this.siteConfig.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};
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,14 @@
|
||||
package org.meteoinfo.data.meteodata.radar;
|
||||
|
||||
import org.meteoinfo.common.DataConvert;
|
||||
import org.meteoinfo.ndarray.DataType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class GenericHeader {
|
||||
public static int length = 32;
|
||||
@ -59,4 +62,18 @@ public class GenericHeader {
|
||||
reserved = new byte[16];
|
||||
in.read(reserved);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param inBytes The input bytes
|
||||
*/
|
||||
public GenericHeader(byte[] inBytes) throws IOException {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(inBytes);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
magicNumber = byteBuffer.getInt();
|
||||
majorVersion = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
minorVersion = DataType.unsignedShortToInt(byteBuffer.getShort());
|
||||
genericType = byteBuffer.getInt();
|
||||
productType = byteBuffer.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,612 @@
|
||||
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.MeteoDataType;
|
||||
import org.meteoinfo.ndarray.*;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PARadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo {
|
||||
|
||||
private GenericHeader genericHeader;
|
||||
private SiteConfig siteConfig;
|
||||
private TaskConfig taskConfig;
|
||||
private List<BeamConfig> beamConfigs;
|
||||
private List<CutConfig> cutConfigs;
|
||||
private List<RadialHeader> radialHeaders;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public PARadarDataInfo() {
|
||||
this.meteoDataType = MeteoDataType.RADAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radar data type
|
||||
* @return Radar data type
|
||||
*/
|
||||
@Override
|
||||
public RadarDataType getRadarDataType() {
|
||||
return RadarDataType.PA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generic header
|
||||
* @return Generic header
|
||||
*/
|
||||
public GenericHeader getGenericHeader() {
|
||||
return this.genericHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site config
|
||||
* @return Site config
|
||||
*/
|
||||
public SiteConfig getSiteConfig() {
|
||||
return this.siteConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task config
|
||||
* @return Task config
|
||||
*/
|
||||
public TaskConfig getTaskConfig() {
|
||||
return this.taskConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cut config list
|
||||
* @return Cut config list
|
||||
*/
|
||||
public List<CutConfig> getCutConfigs() {
|
||||
return this.cutConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radial header list
|
||||
* @return Radial header list
|
||||
*/
|
||||
public List<RadialHeader> getRadialHeaders() {
|
||||
return this.radialHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidFile(RandomAccessFile raf) {
|
||||
try {
|
||||
raf.seek(0);
|
||||
byte[] bytes = new byte[4];
|
||||
raf.read(bytes);
|
||||
int magic = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
if (magic == 1297371986) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the data file format
|
||||
* @param fileName Data file name
|
||||
* @return Boolean
|
||||
*/
|
||||
public static boolean canOpen(String fileName) {
|
||||
try {
|
||||
byte[] bytes = new byte[4];
|
||||
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();
|
||||
}
|
||||
int magic = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
if (magic == 1297371986) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void readDataInfo(InputStream raf) {
|
||||
try {
|
||||
genericHeader = new GenericHeader(raf);
|
||||
siteConfig = new SiteConfig(raf);
|
||||
this.antennaHeight = siteConfig.antennaHeight;
|
||||
|
||||
//Add global attributes
|
||||
this.addAttribute(new Attribute("StationCode", siteConfig.siteCode));
|
||||
this.addAttribute(new Attribute("StationName", siteConfig.siteName));
|
||||
this.addAttribute(new Attribute("StationLatitude", siteConfig.latitude));
|
||||
this.addAttribute(new Attribute("StationLongitude", siteConfig.longitude));
|
||||
this.addAttribute(new Attribute("AntennaHeight", siteConfig.antennaHeight));
|
||||
this.addAttribute(new Attribute("GroundHeight", siteConfig.groundHeight));
|
||||
this.addAttribute(new Attribute("RadarType", siteConfig.getRadarType()));
|
||||
this.addAttribute(new Attribute("featureType", "RADIAL"));
|
||||
this.addAttribute(new Attribute("DataType", "Radial"));
|
||||
this.addAttribute(new Attribute("RadarDataType", "PA"));
|
||||
|
||||
//Read task configuration
|
||||
taskConfig = new TaskConfig(raf);
|
||||
this.addAttribute(new Attribute("TaskName", taskConfig.taskName));
|
||||
this.addAttribute(new Attribute("TaskDescription", taskConfig.taskDescription));
|
||||
|
||||
//Read scan beam configuration
|
||||
beamConfigs = new ArrayList<>();
|
||||
BeamConfig beamConfig;
|
||||
for (int i = 0; i < taskConfig.beamNumber; i++) {
|
||||
beamConfig = new BeamConfig(raf);
|
||||
beamConfigs.add(beamConfig);
|
||||
if (i == 0) {
|
||||
this.beamWidthVert = beamConfig.txBeamWidthV;
|
||||
}
|
||||
}
|
||||
|
||||
//Read cut configuration
|
||||
cutConfigs = new ArrayList<>();
|
||||
CutConfig cutConfig;
|
||||
for (int i = 0; i < taskConfig.cutNumber; i++) {
|
||||
cutConfig = new CutConfig(raf);
|
||||
cutConfigs.add(cutConfig);
|
||||
if (i == 0) {
|
||||
this.logResolution = cutConfig.logResolution;
|
||||
this.dopplerResolution = cutConfig.dopplerResolution;
|
||||
}
|
||||
}
|
||||
|
||||
//Read radial data
|
||||
radialHeaders = new ArrayList<>();
|
||||
byte[] rhBytes = new byte[RadialHeader.length];
|
||||
while (raf.read(rhBytes) != -1) {
|
||||
RadialHeader radialHeader = new RadialHeader(rhBytes);
|
||||
for (int i = 0; i < radialHeader.momentNumber; i++) {
|
||||
MomentHeader momentHeader = new MomentHeader(raf);
|
||||
String product = this.productMap.get(momentHeader.dataType);
|
||||
RadialRecord record;
|
||||
if (this.recordMap.containsKey(product)) {
|
||||
record = this.recordMap.get(product);
|
||||
} else {
|
||||
record = new RadialRecord(product);
|
||||
record.setBinLength(momentHeader.binLength);
|
||||
record.scale = 1.f / momentHeader.scale;
|
||||
record.offset = -momentHeader.offset / (float) momentHeader.scale;
|
||||
this.recordMap.put(product, record);
|
||||
}
|
||||
if (radialHeader.radialNumber == 1) {
|
||||
record.fixedElevation.add(cutConfigs.get(radialHeader.elevationNumber - 1).elevation);
|
||||
record.elevation.add(new ArrayList<>());
|
||||
record.azimuth.add(new ArrayList<>());
|
||||
record.azimuthMinIndex.add(0);
|
||||
if (isVelocityGroup(record)) {
|
||||
record.disResolution.add(cutConfigs.get(radialHeader.elevationNumber - 1).dopplerResolution);
|
||||
record.distance.add(ArrayUtil.arrayRange1(0, momentHeader.dataLength / momentHeader.binLength,
|
||||
cutConfigs.get(radialHeader.elevationNumber - 1).dopplerResolution));
|
||||
} else {
|
||||
record.disResolution.add(cutConfigs.get(radialHeader.elevationNumber - 1).logResolution);
|
||||
record.distance.add(ArrayUtil.arrayRange1(0, momentHeader.dataLength / momentHeader.binLength,
|
||||
cutConfigs.get(radialHeader.elevationNumber - 1).logResolution));
|
||||
}
|
||||
record.newScanData();
|
||||
}
|
||||
record.elevation.get(record.elevation.size() - 1).add(radialHeader.elevation);
|
||||
record.addAzimuth(radialHeader.azimuth);
|
||||
byte[] bytes = new byte[momentHeader.dataLength];
|
||||
raf.read(bytes);
|
||||
record.addDataBytes(bytes);
|
||||
}
|
||||
radialHeaders.add(radialHeader);
|
||||
}
|
||||
raf.close();
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scan elevations
|
||||
* @return Scan elevations
|
||||
*/
|
||||
public List<Float> getElevations() {
|
||||
List<Float> elevations = new ArrayList<>();
|
||||
for (CutConfig cutConfig : this.cutConfigs) {
|
||||
if (!elevations.contains(cutConfig.elevation))
|
||||
elevations.add(cutConfig.elevation);
|
||||
}
|
||||
|
||||
return elevations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attribute> getGlobalAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Site configure inner class
|
||||
*/
|
||||
static class SiteConfig {
|
||||
public static int length = 128;
|
||||
public String siteCode;
|
||||
public String siteName;
|
||||
public float latitude;
|
||||
public float longitude;
|
||||
public int antennaHeight;
|
||||
public int groundHeight;
|
||||
public float frequency;
|
||||
public int antennaType;
|
||||
public int TRNumber;
|
||||
public int RADVersion;
|
||||
public short radarType;
|
||||
public byte[] reserved;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param raf InputStream object
|
||||
*/
|
||||
public SiteConfig(InputStream raf) throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
raf.read(bytes);
|
||||
siteCode = new String(bytes).trim();
|
||||
bytes = new byte[32];
|
||||
raf.read(bytes);
|
||||
siteName = new String(bytes).trim();
|
||||
bytes = new byte[4];
|
||||
raf.read(bytes);
|
||||
latitude = DataConvert.bytes2Float(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
longitude = DataConvert.bytes2Float(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
antennaHeight = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
groundHeight = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
frequency = DataConvert.bytes2Float(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
antennaType = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
TRNumber = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
RADVersion = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
bytes = new byte[2];
|
||||
raf.read(bytes);
|
||||
radarType = DataConvert.bytes2Short(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
reserved = new byte[54];
|
||||
raf.read(reserved);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radar type string
|
||||
* @return Radar type string
|
||||
*/
|
||||
public String getRadarType() {
|
||||
switch (radarType) {
|
||||
case 7:
|
||||
return "SPAR";
|
||||
case 8:
|
||||
return "SPARD";
|
||||
case 43:
|
||||
return "CPAR";
|
||||
case 44:
|
||||
return "CPARD";
|
||||
case 69:
|
||||
return "XPAR";
|
||||
case 70:
|
||||
return "XPARD";
|
||||
default:
|
||||
return "UNDEFINE";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Task configure inner class
|
||||
*/
|
||||
static class TaskConfig {
|
||||
public static int length = 256;
|
||||
public String taskName;
|
||||
public String taskDescription;
|
||||
public int polarizationType;
|
||||
public int scanType;
|
||||
public int beamNumber;
|
||||
public int cutNumber;
|
||||
public int rayOrder;
|
||||
public LocalDateTime scanStartTime;
|
||||
public byte[] reserves;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param raf InputStream object
|
||||
*/
|
||||
public TaskConfig(InputStream raf) throws IOException {
|
||||
byte[] bytes = new byte[32];
|
||||
raf.read(bytes);
|
||||
taskName = new String(bytes).trim();
|
||||
bytes = new byte[128];
|
||||
raf.read(bytes);
|
||||
taskDescription = new String(bytes).trim();
|
||||
bytes = new byte[4];
|
||||
raf.read(bytes);
|
||||
polarizationType = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
scanType = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
beamNumber = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
cutNumber = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
raf.read(bytes);
|
||||
rayOrder = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
bytes = new byte[8];
|
||||
raf.read(bytes);
|
||||
long seconds = DataConvert.bytes2Long(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
LocalDateTime dt = LocalDateTime.of(1970, 1, 1, 0, 0);
|
||||
scanStartTime = dt.plusSeconds(seconds);
|
||||
reserves = new byte[68];
|
||||
raf.read(reserves);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan beam configure inner class
|
||||
*/
|
||||
static class BeamConfig {
|
||||
public static int length = 640;
|
||||
public int beamIndex;
|
||||
public int beamType;
|
||||
public int subPulseNumber;
|
||||
public float txBeamDirection;
|
||||
public float txBeamWidthH;
|
||||
public float txBeamWidthV;
|
||||
public float txBeamGain;
|
||||
public byte[] reserves1; //100 bytes
|
||||
|
||||
public int subPulseStrategy;
|
||||
public int subPulseModulation;
|
||||
public float subPulseFrequency;
|
||||
public float subPulseBandWidth;
|
||||
public int subPulseWidth;
|
||||
public byte[] reserves2; //492 bytes
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param inputStream The input steam
|
||||
*/
|
||||
public BeamConfig(InputStream inputStream) throws IOException {
|
||||
byte[] inBytes = new byte[BeamConfig.length];
|
||||
inputStream.read(inBytes);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(inBytes);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
beamIndex = byteBuffer.getInt();
|
||||
beamType = byteBuffer.getInt();
|
||||
subPulseNumber = byteBuffer.getInt();
|
||||
txBeamDirection = byteBuffer.getFloat();
|
||||
txBeamWidthH = byteBuffer.getFloat();
|
||||
txBeamWidthV = byteBuffer.getFloat();
|
||||
txBeamGain = byteBuffer.getFloat();
|
||||
byteBuffer.position(byteBuffer.position() + 100);
|
||||
|
||||
subPulseStrategy = byteBuffer.getInt();
|
||||
subPulseModulation = byteBuffer.getInt();
|
||||
subPulseFrequency = byteBuffer.getFloat();
|
||||
subPulseBandWidth = byteBuffer.getFloat();
|
||||
subPulseWidth = byteBuffer.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut configuration inner class
|
||||
*/
|
||||
static class CutConfig {
|
||||
public static int length = 256;
|
||||
public short cutIndex;
|
||||
public short txBeamIndex;
|
||||
public float elevation;
|
||||
public float txBeamGain;
|
||||
public float rxBeamWidthH;
|
||||
public float rxBeamWidthV;
|
||||
public float rxBeamGain;
|
||||
public int processMode; // 1-PPP 2-FFT
|
||||
public int waveForm;
|
||||
public float N1_PRF_1;
|
||||
public float N1_PRF_2;
|
||||
public float N2_PRF_1;
|
||||
public float N2_PRF_2;
|
||||
public int unfoldMode;
|
||||
public float azimuth;
|
||||
public float startAngle;
|
||||
public float endAngle;
|
||||
public float angleResolution;
|
||||
public float scanSpeed;
|
||||
public float logResolution;
|
||||
public float dopplerResolution;
|
||||
public int maximumRange;
|
||||
public int maximumRange2;
|
||||
public int startRange;
|
||||
public int sample_1;
|
||||
public int sample_2;
|
||||
public int phaseMode;
|
||||
public float atmosphericLoss;
|
||||
public float nyquistSpeed;
|
||||
public long momentsMask;
|
||||
public long momentsSizeMask;
|
||||
public int miscFilterMask;
|
||||
public float SQIThreshold;
|
||||
public float SIGThreshold;
|
||||
public float CSRThreshold;
|
||||
public float LOGThreshold;
|
||||
public float CPAThreshold;
|
||||
public float PMIThreshold;
|
||||
public float DPLOGThreshold;
|
||||
public byte[] thresholdsReserved; // 4 bytes
|
||||
public int dBTMask;
|
||||
public int dBZMask;
|
||||
public int velocity;
|
||||
public int spectrumWidthMask;
|
||||
public int ZDRMask;
|
||||
public byte[] maskReserved; // 12 bytes
|
||||
public int scanSync;
|
||||
public int direction;
|
||||
public short groundClutterClassifierType;
|
||||
public short groundClutterFilterType;
|
||||
public short groundClutterFilterNotchWidth;
|
||||
public short groundClutterFilterWindow;
|
||||
public byte[] reserved; // 44 bytes
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param inputStream The input stream
|
||||
*/
|
||||
public CutConfig(InputStream inputStream) throws IOException {
|
||||
byte[] bytes = new byte[CutConfig.length];
|
||||
inputStream.read(bytes);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
cutIndex = byteBuffer.getShort();
|
||||
txBeamIndex = byteBuffer.getShort();
|
||||
elevation = byteBuffer.getFloat();
|
||||
txBeamGain = byteBuffer.getFloat();
|
||||
rxBeamWidthH = byteBuffer.getFloat();
|
||||
rxBeamWidthV = byteBuffer.getFloat();
|
||||
rxBeamGain = byteBuffer.getFloat();
|
||||
processMode = byteBuffer.getInt();
|
||||
waveForm = byteBuffer.getInt();
|
||||
N1_PRF_1 = byteBuffer.getFloat();
|
||||
N1_PRF_2 = byteBuffer.getFloat();
|
||||
N2_PRF_1 = byteBuffer.getFloat();
|
||||
N2_PRF_2 = byteBuffer.getFloat();
|
||||
unfoldMode = byteBuffer.getInt();
|
||||
azimuth = byteBuffer.getFloat();
|
||||
startAngle = byteBuffer.getFloat();
|
||||
endAngle = byteBuffer.getFloat();
|
||||
angleResolution = byteBuffer.getFloat();
|
||||
scanSpeed = byteBuffer.getFloat();
|
||||
logResolution = byteBuffer.getFloat();
|
||||
dopplerResolution = byteBuffer.getFloat();
|
||||
maximumRange = byteBuffer.getInt();
|
||||
maximumRange2 = byteBuffer.getInt();
|
||||
startRange = byteBuffer.getInt();
|
||||
sample_1 = byteBuffer.getInt();
|
||||
sample_2 = byteBuffer.getInt();
|
||||
phaseMode = byteBuffer.getInt();
|
||||
atmosphericLoss = byteBuffer.getFloat();
|
||||
nyquistSpeed = byteBuffer.getFloat();
|
||||
momentsMask = byteBuffer.getLong();
|
||||
momentsSizeMask = byteBuffer.getLong();
|
||||
miscFilterMask = byteBuffer.getInt();
|
||||
SQIThreshold = byteBuffer.getFloat();
|
||||
SIGThreshold = byteBuffer.getFloat();
|
||||
CSRThreshold = byteBuffer.getFloat();
|
||||
LOGThreshold = byteBuffer.getFloat();
|
||||
CPAThreshold = byteBuffer.getFloat();
|
||||
PMIThreshold = byteBuffer.getFloat();
|
||||
DPLOGThreshold = byteBuffer.getFloat();
|
||||
byteBuffer.getInt();
|
||||
|
||||
dBTMask = byteBuffer.getInt();
|
||||
dBZMask = byteBuffer.getInt();
|
||||
velocity = byteBuffer.getInt();
|
||||
spectrumWidthMask = byteBuffer.getInt();
|
||||
ZDRMask = byteBuffer.getInt();
|
||||
byteBuffer.position(byteBuffer.position() + 12);
|
||||
|
||||
scanSync = byteBuffer.getInt();
|
||||
direction = byteBuffer.getInt();
|
||||
groundClutterClassifierType = byteBuffer.getShort();
|
||||
groundClutterFilterType = byteBuffer.getShort();
|
||||
groundClutterFilterNotchWidth = byteBuffer.getShort();
|
||||
groundClutterFilterWindow = byteBuffer.getShort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radial header inner class
|
||||
*/
|
||||
static class RadialHeader {
|
||||
public static int length = 128;
|
||||
public int radialState;
|
||||
public int spotBlank;
|
||||
public int sequenceNumber;
|
||||
public int radialNumber;
|
||||
public int elevationNumber;
|
||||
public float azimuth;
|
||||
public float elevation;
|
||||
public long seconds;
|
||||
public int microSeconds;
|
||||
public int lengthOfData;
|
||||
public int momentNumber;
|
||||
public short scanBeamIndex;
|
||||
public short horizontalEstimatedNoise;
|
||||
public short verticalEstimatedNoise;
|
||||
public int PRFFLAG;
|
||||
public byte[] reserved04; //70 bytes
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param inputStream The input stream
|
||||
*/
|
||||
public RadialHeader(byte[] bytes) {
|
||||
ByteBuffer bf = ByteBuffer.wrap(bytes);
|
||||
bf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
radialState = bf.getInt();
|
||||
spotBlank = bf.getInt();
|
||||
sequenceNumber = bf.getInt();
|
||||
radialNumber = bf.getInt();
|
||||
elevationNumber = bf.getInt();
|
||||
azimuth = bf.getFloat();
|
||||
elevation = bf.getFloat();
|
||||
seconds = bf.getLong();
|
||||
microSeconds = bf.getInt();
|
||||
lengthOfData = bf.getInt();
|
||||
momentNumber = bf.getInt();
|
||||
scanBeamIndex = bf.getShort();
|
||||
horizontalEstimatedNoise = bf.getShort();
|
||||
verticalEstimatedNoise = bf.getShort();
|
||||
PRFFLAG = bf.getInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,19 @@
|
||||
package org.meteoinfo.data.meteodata.radar;
|
||||
|
||||
import org.apache.commons.compress.compressors.FileNameUtil;
|
||||
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.meteoinfo.common.DataConvert;
|
||||
import org.meteoinfo.data.meteodata.DataInfo;
|
||||
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.*;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class RadarDataUtil {
|
||||
|
||||
@ -53,6 +58,25 @@ public class RadarDataUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get InputStream from file name.
|
||||
*
|
||||
* @param fileName The file name
|
||||
* @return The InputStream
|
||||
* @throws IOException
|
||||
*/
|
||||
public static InputStream getInputStream(String fileName) throws IOException {
|
||||
String fileExtent = FilenameUtils.getExtension(fileName).toLowerCase();
|
||||
switch (fileExtent) {
|
||||
case "bz2":
|
||||
return new BZip2CompressorInputStream(Files.newInputStream(Paths.get(fileName)));
|
||||
case "gz":
|
||||
return new GzipCompressorInputStream(new FileInputStream(fileName));
|
||||
default:
|
||||
return new BufferedInputStream(Files.newInputStream(Paths.get(fileName)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radar data type
|
||||
* @param fileName Data file name
|
||||
@ -60,21 +84,24 @@ public class RadarDataUtil {
|
||||
*/
|
||||
public static RadarDataType getRadarDataType(String fileName) {
|
||||
try {
|
||||
InputStream inputStream = getInputStream(fileName);
|
||||
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) {
|
||||
byte[] inBytes = Arrays.copyOf(bytes, GenericHeader.length);
|
||||
GenericHeader genericHeader = new GenericHeader(inBytes);
|
||||
switch (genericHeader.genericType) {
|
||||
case 1:
|
||||
return RadarDataType.STANDARD;
|
||||
case 16:
|
||||
return RadarDataType.PA;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String radarT = new String(bytes);
|
||||
@ -86,14 +113,17 @@ public class RadarDataUtil {
|
||||
return RadarDataType.CC20;
|
||||
}
|
||||
|
||||
magicBytes = Arrays.copyOfRange(bytes, 14, 16);
|
||||
if (Arrays.equals(magicBytes, new byte[]{1, 0})) {
|
||||
return RadarDataType.SAB;
|
||||
magicBytes = Arrays.copyOf(bytes, 128);
|
||||
SABRadarDataInfo.RadialHeader radialHeader = new SABRadarDataInfo.RadialHeader(magicBytes);
|
||||
if (radialHeader.messageType != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
magicBytes = Arrays.copyOfRange(bytes, 8, 12);
|
||||
if (Arrays.equals(magicBytes, new byte[]{16, 0, 0, 0})) {
|
||||
return RadarDataType.PA;
|
||||
if (radialHeader.mSecond > 86400000) {
|
||||
return null;
|
||||
}
|
||||
LocalDateTime dateTime = radialHeader.getDateTime();
|
||||
if (dateTime.getYear() >= 1990 && dateTime.getYear() <= LocalDateTime.now().getYear()) {
|
||||
return RadarDataType.SAB;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@ -114,11 +144,13 @@ public class RadarDataUtil {
|
||||
} else {
|
||||
switch (radarDataType) {
|
||||
case STANDARD:
|
||||
return new CMARadarBaseDataInfo();
|
||||
return new StandardRadarDataInfo();
|
||||
case PA:
|
||||
return new PARadarDataInfo();
|
||||
case SAB:
|
||||
return new SABRadarDataInfo();
|
||||
/*case CC:
|
||||
return new CCRadarDataInfo();*/
|
||||
case CC:
|
||||
return new CCRadarDataInfo();
|
||||
case SC:
|
||||
return new SCRadarDataInfo();
|
||||
default:
|
||||
|
||||
@ -10,12 +10,14 @@ import org.meteoinfo.ndarray.DataType;
|
||||
import org.meteoinfo.ndarray.Index;
|
||||
import org.meteoinfo.ndarray.math.ArrayUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RadialRecord {
|
||||
public String product;
|
||||
private RadarDataType radarDataType = RadarDataType.STANDARD;
|
||||
private int binLength;
|
||||
private DataType dataType;
|
||||
private int fillValue;
|
||||
@ -26,7 +28,7 @@ public class RadialRecord {
|
||||
public List<List<Float>> azimuth = new ArrayList<>();
|
||||
public List<Integer> azimuthMinIndex = new ArrayList<>();
|
||||
public List<Array> distance = new ArrayList<>();
|
||||
public List<Integer> disResolution = new ArrayList<>();
|
||||
public List<Float> disResolution = new ArrayList<>();
|
||||
private final List<List<Array>> data = new ArrayList<>();
|
||||
|
||||
/**
|
||||
@ -37,6 +39,22 @@ public class RadialRecord {
|
||||
this.product = product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radar data type
|
||||
* @return Radar data type
|
||||
*/
|
||||
public RadarDataType getRadarDataType() {
|
||||
return this.radarDataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set radar data type
|
||||
* @param radarDataType Radar data type
|
||||
*/
|
||||
public void setRadarDataType(RadarDataType radarDataType) {
|
||||
this.radarDataType = radarDataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bin length and update DataType
|
||||
* @param value Bin length
|
||||
@ -55,6 +73,14 @@ public class RadialRecord {
|
||||
return this.dataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data type
|
||||
* @param dataType Data type
|
||||
*/
|
||||
public void setDataType(DataType dataType) {
|
||||
this.dataType = dataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an azimuth value
|
||||
* @param a Azimuth value
|
||||
@ -265,6 +291,9 @@ public class RadialRecord {
|
||||
}
|
||||
variable.addAttribute(new Attribute("scale_factor", this.scale));
|
||||
variable.addAttribute(new Attribute("add_offset", this.offset));
|
||||
if (this.radarDataType == RadarDataType.CC) {
|
||||
variable.addAttribute(new Attribute("missing_value", -32768));
|
||||
}
|
||||
dataInfo.addVariable(variable);
|
||||
}
|
||||
|
||||
@ -299,6 +328,9 @@ public class RadialRecord {
|
||||
variable.addDimension(disDim);
|
||||
variable.addAttribute(new Attribute("scale_factor", this.scale));
|
||||
variable.addAttribute(new Attribute("add_offset", this.offset));
|
||||
if (this.radarDataType == RadarDataType.CC) {
|
||||
variable.addAttribute(new Attribute("missing_value", -32768));
|
||||
}
|
||||
dataInfo.addVariable(variable);
|
||||
|
||||
variable = new Variable();
|
||||
@ -493,7 +525,7 @@ public class RadialRecord {
|
||||
public float getValue(int ei, float a, float r) {
|
||||
List<Array> sData = this.data.get(ei);
|
||||
int aziIdx = getAzimuthIndex(ei, a);
|
||||
int disRes = this.disResolution.get(ei);
|
||||
float disRes = this.disResolution.get(ei);
|
||||
int disIdx = (int) (r / disRes);
|
||||
Array rData = sData.get(aziIdx);
|
||||
float v;
|
||||
@ -522,7 +554,7 @@ public class RadialRecord {
|
||||
*/
|
||||
public float interpolateValue(int ei, int ai, float r) {
|
||||
List<Array> sData = this.data.get(ei);
|
||||
int disRes = this.disResolution.get(ei);
|
||||
float disRes = this.disResolution.get(ei);
|
||||
float v;
|
||||
Array rData = sData.get(ai);
|
||||
float disIdx = r / disRes;
|
||||
|
||||
@ -17,6 +17,9 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -51,25 +54,6 @@ public class SABRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInf
|
||||
}
|
||||
|
||||
@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;
|
||||
@ -99,11 +83,11 @@ public class SABRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInf
|
||||
record.azimuth.add(new ArrayList<>());
|
||||
record.azimuthMinIndex.add(0);
|
||||
if (isVelocityGroup(record)) {
|
||||
record.disResolution.add(radialHeader.gateSizeOfDoppler);
|
||||
record.disResolution.add((float) radialHeader.gateSizeOfDoppler);
|
||||
record.distance.add(ArrayUtil.arrayRange1(radialHeader.rangeToFirstGateOfDop,
|
||||
radialHeader.gatesNumberOfDoppler, radialHeader.gateSizeOfDoppler));
|
||||
} else {
|
||||
record.disResolution.add(radialHeader.gateSizeOfReflectivity);
|
||||
record.disResolution.add((float) radialHeader.gateSizeOfReflectivity);
|
||||
record.distance.add(ArrayUtil.arrayRange1(radialHeader.rangeToFirstGateOfRef,
|
||||
radialHeader.gatesNumberOfReflectivity, radialHeader.gateSizeOfReflectivity));
|
||||
}
|
||||
@ -135,6 +119,7 @@ public class SABRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInf
|
||||
|
||||
this.addAttribute(new Attribute("featureType", "RADIAL"));
|
||||
this.addAttribute(new Attribute("DataType", "Radial"));
|
||||
this.addAttribute(new Attribute("RadarDataType", "SA/SB"));
|
||||
|
||||
//Add dimensions and variables
|
||||
RadialRecord refRadialRecord = this.recordMap.get("dBZ");
|
||||
@ -172,6 +157,7 @@ public class SABRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInf
|
||||
|
||||
static class RadialHeader {
|
||||
public static int length = 128;
|
||||
public short messageType;
|
||||
public int mSecond; // collection time for this radial, msecs since midnight
|
||||
public short julianDate; // prob "collection time"
|
||||
public short uRange; // unambiguous range
|
||||
@ -202,6 +188,8 @@ public class SABRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInf
|
||||
public RadialHeader(byte[] inBytes) throws IOException {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(inBytes);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
byteBuffer.position(14);
|
||||
messageType = byteBuffer.getShort();
|
||||
byteBuffer.position(28);
|
||||
mSecond = byteBuffer.getInt();
|
||||
julianDate = byteBuffer.getShort();
|
||||
@ -228,6 +216,15 @@ public class SABRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInf
|
||||
nyquist = byteBuffer.getShort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date time
|
||||
* @return Date time
|
||||
*/
|
||||
public LocalDateTime getDateTime() {
|
||||
long total = ((long) (julianDate - 1)) * 24 * 3600 * 1000 + mSecond;
|
||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(total), ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Has reflectivity data or not
|
||||
* @return Has reflectivity data
|
||||
|
||||
@ -44,25 +44,6 @@ public class SCRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo
|
||||
}
|
||||
|
||||
@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 {
|
||||
byte[] bytes = new byte[SCRadarDataInfo.RadarHeader.length];
|
||||
@ -94,7 +75,7 @@ public class SCRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo
|
||||
record.elevation.add(new ArrayList<>());
|
||||
record.azimuth.add(new ArrayList<>());
|
||||
record.azimuthMinIndex.add(0);
|
||||
record.disResolution.add(layerParam.binWidth / 10);
|
||||
record.disResolution.add(layerParam.binWidth / 10.f);
|
||||
record.distance.add(ArrayUtil.arrayRange1(0,
|
||||
gateNum, layerParam.binWidth / 10));
|
||||
record.newScanData();
|
||||
@ -121,6 +102,7 @@ public class SCRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo
|
||||
this.addAttribute(new Attribute("AntennaHeight", radarHeader.getHeight()));
|
||||
this.addAttribute(new Attribute("featureType", "RADIAL"));
|
||||
this.addAttribute(new Attribute("DataType", "Radial"));
|
||||
this.addAttribute(new Attribute("RadarDataType", "SC"));
|
||||
|
||||
//Add dimensions and variables
|
||||
RadialRecord refRadialRecord = this.recordMap.get("dBZ");
|
||||
|
||||
@ -0,0 +1,256 @@
|
||||
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.*;
|
||||
import org.meteoinfo.ndarray.*;
|
||||
import org.meteoinfo.ndarray.math.ArrayUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
public class StandardRadarDataInfo extends BaseRadarDataInfo implements IRadarDataInfo {
|
||||
|
||||
private GenericHeader genericHeader;
|
||||
private SiteConfig siteConfig;
|
||||
private TaskConfig taskConfig;
|
||||
private List<CutConfig> cutConfigs;
|
||||
private List<RadialHeader> radialHeaders;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public StandardRadarDataInfo() {
|
||||
this.meteoDataType = MeteoDataType.RADAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radar data type
|
||||
* @return Radar data type
|
||||
*/
|
||||
@Override
|
||||
public RadarDataType getRadarDataType() {
|
||||
return RadarDataType.STANDARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generic header
|
||||
* @return Generic header
|
||||
*/
|
||||
public GenericHeader getGenericHeader() {
|
||||
return this.genericHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site config
|
||||
* @return Site config
|
||||
*/
|
||||
public SiteConfig getSiteConfig() {
|
||||
return this.siteConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task config
|
||||
* @return Task config
|
||||
*/
|
||||
public TaskConfig getTaskConfig() {
|
||||
return this.taskConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cut config list
|
||||
* @return Cut config list
|
||||
*/
|
||||
public List<CutConfig> getCutConfigs() {
|
||||
return this.cutConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radial header list
|
||||
* @return Radial header list
|
||||
*/
|
||||
public List<RadialHeader> getRadialHeaders() {
|
||||
return this.radialHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidFile(RandomAccessFile raf) {
|
||||
try {
|
||||
raf.seek(0);
|
||||
byte[] bytes = new byte[4];
|
||||
raf.read(bytes);
|
||||
int magic = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
if (magic == 1297371986) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the data file format
|
||||
* @param fileName Data file name
|
||||
* @return Boolean
|
||||
*/
|
||||
public static boolean canOpen(String fileName) {
|
||||
try {
|
||||
byte[] bytes = new byte[4];
|
||||
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();
|
||||
}
|
||||
int magic = DataConvert.bytes2Int(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||
if (magic == 1297371986) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void readDataInfo(InputStream raf) {
|
||||
try {
|
||||
genericHeader = new GenericHeader(raf);
|
||||
siteConfig = new SiteConfig(raf);
|
||||
this.antennaHeight = siteConfig.antennaHeight;
|
||||
this.beamWidthVert = siteConfig.beamWidthVert;
|
||||
|
||||
//Add global attributes
|
||||
this.addAttribute(new Attribute("StationCode", siteConfig.siteCode));
|
||||
this.addAttribute(new Attribute("StationName", siteConfig.siteName));
|
||||
this.addAttribute(new Attribute("StationLatitude", siteConfig.latitude));
|
||||
this.addAttribute(new Attribute("StationLongitude", siteConfig.longitude));
|
||||
this.addAttribute(new Attribute("AntennaHeight", siteConfig.antennaHeight));
|
||||
this.addAttribute(new Attribute("GroundHeight", siteConfig.groundHeight));
|
||||
this.addAttribute(new Attribute("RadarType", siteConfig.getRadarType()));
|
||||
this.addAttribute(new Attribute("featureType", "RADIAL"));
|
||||
this.addAttribute(new Attribute("DataType", "Radial"));
|
||||
this.addAttribute(new Attribute("RadarDataType", "CMA Standard"));
|
||||
|
||||
//Read task configuration
|
||||
taskConfig = new TaskConfig(raf);
|
||||
this.addAttribute(new Attribute("TaskName", taskConfig.taskName));
|
||||
this.addAttribute(new Attribute("TaskDescription", taskConfig.taskDescription));
|
||||
|
||||
//Read radial data
|
||||
cutConfigs = new ArrayList<>();
|
||||
CutConfig cutConfig;
|
||||
for (int i = 0; i < taskConfig.cutNumber; i++) {
|
||||
cutConfig = new CutConfig(raf);
|
||||
cutConfigs.add(cutConfig);
|
||||
if (i == 0) {
|
||||
this.logResolution = cutConfig.logResolution;
|
||||
this.dopplerResolution = cutConfig.dopplerResolution;
|
||||
}
|
||||
}
|
||||
radialHeaders = new ArrayList<>();
|
||||
byte[] rhBytes = new byte[RadialHeader.length];
|
||||
while (raf.read(rhBytes) != -1) {
|
||||
RadialHeader radialHeader = new RadialHeader(rhBytes);
|
||||
for (int i = 0; i < radialHeader.momentNumber; i++) {
|
||||
MomentHeader momentHeader = new MomentHeader(raf);
|
||||
String product = this.productMap.get(momentHeader.dataType);
|
||||
RadialRecord record;
|
||||
if (this.recordMap.containsKey(product)) {
|
||||
record = this.recordMap.get(product);
|
||||
} else {
|
||||
record = new RadialRecord(product);
|
||||
record.setBinLength(momentHeader.binLength);
|
||||
record.scale = 1.f / momentHeader.scale;
|
||||
record.offset = -momentHeader.offset / (float) momentHeader.scale;
|
||||
this.recordMap.put(product, record);
|
||||
}
|
||||
if (radialHeader.radialNumber == 1) {
|
||||
record.fixedElevation.add(cutConfigs.get(radialHeader.elevationNumber - 1).elevation);
|
||||
record.elevation.add(new ArrayList<>());
|
||||
record.azimuth.add(new ArrayList<>());
|
||||
record.azimuthMinIndex.add(0);
|
||||
if (isVelocityGroup(record)) {
|
||||
record.disResolution.add((float) cutConfigs.get(radialHeader.elevationNumber - 1).dopplerResolution);
|
||||
record.distance.add(ArrayUtil.arrayRange1(0, momentHeader.dataLength / momentHeader.binLength,
|
||||
cutConfigs.get(radialHeader.elevationNumber - 1).dopplerResolution));
|
||||
} else {
|
||||
record.disResolution.add((float) cutConfigs.get(radialHeader.elevationNumber - 1).logResolution);
|
||||
record.distance.add(ArrayUtil.arrayRange1(0, momentHeader.dataLength / momentHeader.binLength,
|
||||
cutConfigs.get(radialHeader.elevationNumber - 1).logResolution));
|
||||
}
|
||||
record.newScanData();
|
||||
}
|
||||
record.elevation.get(record.elevation.size() - 1).add(radialHeader.elevation);
|
||||
record.addAzimuth(radialHeader.azimuth);
|
||||
byte[] bytes = new byte[momentHeader.dataLength];
|
||||
raf.read(bytes);
|
||||
record.addDataBytes(bytes);
|
||||
}
|
||||
radialHeaders.add(radialHeader);
|
||||
}
|
||||
raf.close();
|
||||
|
||||
//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);
|
||||
|
||||
/*Dimension xyzDim = new Dimension(DimensionType.OTHER);
|
||||
xyzDim.setShortName("xyz");
|
||||
xyzDim.setDimValue(Array.factory(DataType.INT, new int[]{3}, new int[]{1,2,3}));
|
||||
this.addDimension(xyzDim);
|
||||
for (String product : this.recordMap.keySet()) {
|
||||
this.recordMap.get(product).makeVariables(this, xyzDim);
|
||||
}*/
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scan elevations
|
||||
* @return Scan elevations
|
||||
*/
|
||||
public List<Float> getElevations() {
|
||||
List<Float> elevations = new ArrayList<>();
|
||||
for (CutConfig cutConfig : this.cutConfigs) {
|
||||
if (!elevations.contains(cutConfig.elevation))
|
||||
elevations.add(cutConfig.elevation);
|
||||
}
|
||||
|
||||
return elevations;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,13 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<MeteoInfo File="milconfig.xml" Type="configurefile">
|
||||
<Path OpenPath="D:\Working\MIScript\Jython\mis\plot_types\plot">
|
||||
<Path OpenPath="D:\Working\MIScript\Jython\mis\io\radar">
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe\series"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\contour"/>
|
||||
<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\image"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo"/>
|
||||
@ -16,17 +14,19 @@
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\gridshow"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\plot"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar\cinrad"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar"/>
|
||||
</Path>
|
||||
<File>
|
||||
<OpenedFiles>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\test_read_SC.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_pa_1.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_cc_1.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\plot\plot_2darray_1.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\radar\radar_cma_base_vcs.py"/>
|
||||
</OpenedFiles>
|
||||
<RecentFiles>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\test_read_SC.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_pa_1.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\cinrad\radar_cc_1.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\plot\plot_2darray_1.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\io\radar\radar_cma_base_vcs.py"/>
|
||||
</RecentFiles>
|
||||
</File>
|
||||
<Font>
|
||||
@ -34,5 +34,5 @@
|
||||
</Font>
|
||||
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
|
||||
<Figure DoubleBuffering="true"/>
|
||||
<Startup MainFormLocation="-7,0" MainFormSize="1415,864"/>
|
||||
<Startup MainFormLocation="-7,0" MainFormSize="1340,820"/>
|
||||
</MeteoInfo>
|
||||
|
||||
2
pom.xml
2
pom.xml
@ -35,7 +35,7 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<revision>3.8.9</revision>
|
||||
<revision>3.8.10</revision>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<maven.compiler.release>8</maven.compiler.release>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user