awips2/edexOsgi/com.raytheon.uf.common.sounding/src/com/raytheon/uf/common/sounding/VerticalSounding.java
Matt Nash 82300ccdcf Moving 12.1.1 into ss_sync
Former-commit-id: 580e2938d7 [formerly 66be3bec40] [formerly c83e5ff474 [formerly 2a9569942c48542cf708b6c0b9189146fd954c11]]
Former-commit-id: c83e5ff474
Former-commit-id: 1faae42484
2012-01-19 11:53:12 -06:00

1126 lines
No EOL
34 KiB
Java

/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.sounding;
import static com.raytheon.uf.common.sounding.SoundingLayer.MISSING;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.raytheon.uf.common.pointdata.spatial.SurfaceObsLocation;
import com.raytheon.uf.common.sounding.SoundingLayer.DATA_TYPE;
import com.raytheon.uf.common.time.DataTime;
import com.vividsolutions.jts.geom.Geometry;
/**
* VerticalSounding is an ordered collection of SoundingLayers sorted by
* pressure, where the highest pressure is at the beginning of the collection.
*
* <pre>
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* 06 Nov 2006 jkorman Initial Coding
* 20071127 382 jkorman Moved from Cave graphing.
* 15Jan2008 682 ebabin Updated to remove non calculated parameters.
* 16Jan2008 682 ebabin Updates for grib model traps on multiple loads.
* 04Oct2008 dhladky Many changes.
* </pre>
*
* @author jkorman
* @version 1.0
*/
public class VerticalSounding implements Iterable<SoundingLayer>, Cloneable {
private static final float FREEZING = 273.15f;
/**
* Is the value not null and above the missing/invalid values.
*
* @param value
* A value to check.
* @return Is the value valid.
*/
protected static boolean isValid(double value) {
return value < MISSING;
}
// *************************************************
private SurfaceObsLocation spatialInfo;
private Calendar obsTime = null;
private DataTime dataTime = null;
private List<SoundingLayer> layerData = new ArrayList<SoundingLayer>();
private Map<Float, SoundingLayer> layerMap = new HashMap<Float, SoundingLayer>();
private List<SoundingLayer> maxWinds = new ArrayList<SoundingLayer>();
private SoundingLayer sfcLayer = null;
private String displayFormat;
// *************************************************
// * Computed data.
// *************************************************
private SoundingLayer maxTemperatureLayer = null;
private SoundingLayer minTemperatureLayer = null;
private boolean initialLoad = true;
private String name = "";
/**
* Create an empty sounding.
*/
public VerticalSounding() {
}
/**
* @return the displayFormat
*/
public String getDisplayFormat() {
return displayFormat;
}
/**
* @param displayFormat
* the displayFormat to set
*/
public void setDisplayFormat(String displayFormat) {
this.displayFormat = displayFormat;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#clone()
*/
@Override
public Object clone() throws CloneNotSupportedException {
VerticalSounding clone = (VerticalSounding) super.clone();
clone.layerData = new ArrayList<SoundingLayer>();
clone.layerMap = new HashMap<Float, SoundingLayer>();
clone.maxWinds = new ArrayList<SoundingLayer>();
clone.spatialInfo = (SurfaceObsLocation) spatialInfo.clone();
clone.setInitialLoad(true);
for (SoundingLayer layer : layerData) {
clone.addLayer((SoundingLayer) layer.clone());
}
clone.invalidate();
return clone;
}
/**
* Check to see if a surface layer exists. If it does not then find the
* lowest layer and set this as the surface.
*/
public void checkSfcLayer() {
// Did the data declare a surface layer?
if (sfcLayer == null) {
// Did we set Elevation? If so use that to calculate the surface
// layer.
if (this.getElevation() != null) {
int press = (int) WxMath.heightToPressure(this.getElevation());
sfcLayer = this.getLayerNearest(press);
if (press != sfcLayer.getPressure()) {
SoundingLayer sLayer = new SoundingLayer();
sLayer.setGeoHeight(this.getElevation());
sLayer.setPressure(press);
sLayer.setTemperature((float) (Math.pow(
press / sfcLayer.getPressure(), 0.286) * sfcLayer
.getTemperature()));
sLayer.setWindU(sfcLayer.getWindU());
sLayer.setWindV(sfcLayer.getWindV());
sLayer.setWindDirection(sfcLayer.getWindDirection());
sLayer.setWindSpeed(sfcLayer.getWindSpeed());
sfcLayer = sLayer;
addLayer(sfcLayer);
}
} else {
SoundingLayer lowestLayer = new SoundingLayer();
// Start at 1 hPa. We won't consider anything higher that that.
lowestLayer.setPressure(1.0f);
for (SoundingLayer layer : layerData) {
if (layer.isLowerThan(lowestLayer)) {
lowestLayer = layer;
}
}
sfcLayer = lowestLayer;
}
}
}
/**
* Get the surface layer if defined.
*/
public SoundingLayer getSfcLayer() {
return sfcLayer;
}
/**
* @return the layerData
*/
public List<SoundingLayer> getLayerData() {
return layerData;
}
/**
*/
public void removeBelowSfcLayers() {
if (layerData != null) {
if (sfcLayer != null) {
Iterator<SoundingLayer> layers = layerData.iterator();
while (layers.hasNext()) {
SoundingLayer layer = layers.next();
if (layer.isLowerThan(sfcLayer)) {
layers.remove();
}
}
}
}
}
private void rebuildMap() {
layerMap.clear();
for (SoundingLayer layer : layerData) {
layerMap.put(new Float(layer.getPressure()), layer);
}
}
/**
* Add a layer to this sounding.
*
* @param layer
* A sounding layer to add.
*/
/**
* Add a layer to this sounding.
*
* @param layer
* A sounding layer to add.
*/
public void addLayer(SoundingLayer layer) {
if (okToAdd(layer)) {
sortLayerIntoSounding(layer);
if (layer.getPressure() != MISSING) {
// update values
SoundingLayer currLayer = layerMap.get(layer.getPressure());
if (currLayer != null) {
if (layer.getTemperature() != MISSING) {
currLayer.setTemperature(layer.getTemperature());
}
if (layer.getDewpoint() != MISSING) {
currLayer.setDewpoint(layer.getDewpoint());
}
if (layer.getWindDirection() != MISSING) {
currLayer.setWindDirection(layer.getWindDirection());
}
if (layer.getWindSpeed() != MISSING) {
currLayer.setWindSpeed(layer.getWindSpeed());
}
} else {
layerMap.put(new Float(layer.getPressure()), layer);
}
}
invalidate();
}
}
/**
*
* @param layers
*/
public void addLayers(List<SoundingLayer> layers) {
initialLoad = true;
layerData.clear();
layerMap.clear();
for (SoundingLayer layer : layers) {
addLayer(layer);
}
}
/**
*
*/
public void invalidate() {
if (!isInitialLoad()) {
sortByPressure();
rebuildMap();
updateMaxMinTemp();
}
}
/**
* Checks a layer to be inserted to determine if a layer with the same
* pressure already exists.
*
* @param layer
* The layer to be added.
*/
private boolean okToAdd(SoundingLayer layer) {
boolean shouldAdd = false;
if (layer != null) {
float p = layer.getPressure();
if ((p != MISSING) && (layer.getLayerType() != null)) {
SoundingLayer currLayer = layerMap.get(p);
if (currLayer != null) {
switch (layer.getLayerType()) {
case SURFACE: {
if (LayerType.MAN_PRESSURE.equals(currLayer
.getLayerType())) {
int i = layerData.indexOf(currLayer);
layerData.remove(i);
layerMap.remove(currLayer.getPressure());
shouldAdd = true;
} else if (LayerType.SIG_PRESSURE.equals(currLayer
.getLayerType())) {
int i = layerData.indexOf(currLayer);
layerData.remove(i);
layerMap.remove(currLayer.getPressure());
shouldAdd = true;
}
break;
}
case MAN_PRESSURE: {
if (LayerType.SIG_PRESSURE.equals(currLayer
.getLayerType())) {
int i = layerData.indexOf(currLayer);
layerData.remove(i);
layerMap.remove(currLayer.getPressure());
shouldAdd = true;
}
break;
}
case SIG_PRESSURE: {
// TODO : Make sure to check other layer types.
break;
}
default: {
shouldAdd = true;
}
} // end switch
} else {
shouldAdd = true;
}
} else {
layerMap.put(new Float(layer.getPressure()), layer);
}
}
return shouldAdd;
}
/**
* Remove Data layer
*
* @param layer
*/
public void removeLayer(SoundingLayer layer) {
if (layerMap.containsKey(layer.getPressure())) {
layerMap.remove(layer.getPressure());
}
layerData.remove(layer);
invalidate();
}
/**
* Gets the layer at the specified pressure level
*
* @param level
* @return the layer
*/
public SoundingLayer getLayer(float level) {
return layerMap.get(level);
}
/**
* Get the layer at the specified index
*
* @param index
* @return the layer
*/
public SoundingLayer get(int index) {
return layerData.get(index);
}
/**
* Sort a layer into this sounding. The primary sort key is the layer
* pressure. If pressure is not available, the geopotential height is used.
*
* @param layer
* The layer that is being added.
*/
private void sortLayerIntoSounding(SoundingLayer layer) {
// If empty, just add the layer.
if (LayerType.MAX_WIND.equals(layer.getLayerType())) {
maxWinds.add(layer);
return;
}
if (LayerType.SURFACE.equals(layer.getLayerType())) {
sfcLayer = layer;
}
if (layerData.size() == 0) {
layerData.add(layer);
} else {
boolean entered = false;
for (int i = 0; i < layerData.size(); i++) {
SoundingLayer cLayer = layerData.get(i);
if (layer.isLowerThan(cLayer)) {
layerData.add(i, layer);
entered = true;
break;
}
}
if (!entered) {
layerData.add(layer);
}
}
}
/**
* Gets the initialLoad boolean
*
* @return
*/
public boolean isInitialLoad() {
return initialLoad;
}
/**
* Sets the initialLoad param
*
* @param initalLoad
*/
public void setInitialLoad(boolean initialLoad) {
this.initialLoad = initialLoad;
}
/**
* Return the layer closest to a given pressure.
*
* @param pressure
* The target pressure.
* @return The SoundingLayer instance nearest to the target.
*/
public SoundingLayer getLayerNearest(float pressure) {
SoundingLayer retLayer = new SoundingLayer(pressure, MISSING, MISSING,
MISSING, MISSING, MISSING, MISSING);
if (layerData.size() > 0) {
retLayer = layerData.get(0);
if (layerData.size() == 1) {
return retLayer;
}
SoundingLayer loLayer = null;
SoundingLayer hiLayer = null;
SoundingLayer sLayer = new SoundingLayer(pressure, MISSING,
MISSING, MISSING, MISSING, MISSING, MISSING);
int hiIndex = 0;
for (int i = 0; i < layerData.size(); i++) {
SoundingLayer layer = layerData.get(i);
if (layer.getPressure() == pressure) {
return layer;
} else if (layer.isHigherThan(sLayer)) {
// Make sure we have a lower pressure level.
if (layer.getPressure() < MISSING) {
hiLayer = layer;
hiIndex = i;
break;
}
}
}
if (hiIndex > 0) {
SoundingLayer layer = layerData.get(0);
loLayer = layer;
for (int i = hiIndex - 1; i > 0; i--) {
layer = layerData.get(i);
if (layer.isLowerThan(sLayer)) {
// Make sure we have a lower pressure level.
if (layer.getPressure() < MISSING) {
loLayer = layer;
break;
}
}
}
} else {
if (pressure > layerData.get(0).getPressure()) {
retLayer = layerData.get(0);
} else {
retLayer = layerData.get(layerData.size() - 1);
}
}
if ((loLayer != null) && (hiLayer != null)) {
double hPoint = (loLayer.getPressure() + hiLayer.getPressure()) / 2;
if (pressure < hPoint) {
retLayer = hiLayer;
} else {
retLayer = loLayer;
}
}
}
return retLayer;
}
/**
* Return the layer closest to a given windspeed and winddir.
*
* @param windspeed
* , winddir The target pressure.
* @return The SoundingLayer instance nearest to the target.
*/
public SoundingLayer getLayerNearest(float windspeed, float winddir) {
SoundingLayer retLayer = null;
SoundingLayer loLayer = null;
SoundingLayer hiLayer = null;
SoundingLayer sLayer = new SoundingLayer(MISSING, MISSING, MISSING,
MISSING, windspeed, winddir, MISSING);
int hiIndex = 0;
// Check and find closest wind speed
for (int i = 0; i < layerData.size(); i++) {
SoundingLayer layer = layerData.get(i);
if (layer.getWindSpeed() == sLayer.getWindSpeed()
&& layer.getWindDirection() == sLayer.getWindDirection()) {
return layer;
} else if ((layer.getWindSpeed() > sLayer.getWindSpeed())
&& layer.getWindSpeed() != 99999.0) {
hiLayer = layer;
hiIndex = i;
break;
}
}
if (hiIndex > 0) {
for (int i = hiIndex + 1; i < layerData.size(); i++) {
SoundingLayer layer = layerData.get(i);
if (layer.getWindSpeed() < sLayer.getWindSpeed()) {
loLayer = layer;
break;
}
}
} else {
if (windspeed > layerData.get(0).getPressure()) {
retLayer = layerData.get(0);
} else {
retLayer = layerData.get(layerData.size() - 1);
}
}
if ((loLayer != null) && (hiLayer != null)) {
double hPoint = (loLayer.getWindSpeed() + hiLayer.getWindSpeed()) / 2;
if (windspeed < hPoint) {
retLayer = hiLayer;
} else {
retLayer = loLayer;
}
}
return retLayer;
}
/**
* Get an iterator to the internal layer data.
*
* @return The layer data iterator.
*/
public Iterator<SoundingLayer> iterator() {
return layerData.iterator();
}
/**
* Get this observation's geometry.
*
* @return The geometry for this observation.
*/
public Geometry getObsGeometry() {
Geometry obsGeometry = null;
if (spatialInfo != null) {
obsGeometry = spatialInfo.getGeometry();
}
return obsGeometry;
}
/**
* Get the geometry latitude.
*
* @return The geometry latitude.
*/
public double getLatitude() {
return spatialInfo.getLatitude();
}
/**
* Get the geometry longitude.
*
* @return The geometry longitude.
*/
public double getLongitude() {
return spatialInfo.getLongitude();
}
/**
* Set the geometry latitude.
*
*/
public void setLatitude(double latitude) {
spatialInfo.setLatitude(latitude);
}
/**
* Set the geometry longitude.
*
*/
public void setLongitude(double longitude) {
spatialInfo.setLongitude(longitude);
}
/**
* Get the station identifier for this observation.
*
* @return the stationId
*/
public String getStationId() {
String stationId = null;
if (spatialInfo != null) {
stationId = (spatialInfo).getStationId();
}
return stationId;
}
/**
* Set the station identifier for this observation.
*
* @param stationId
* the stationId to set
*/
public void setStationId(String stationId) {
if (spatialInfo == null) {
spatialInfo = new SurfaceObsLocation();
}
(spatialInfo).setStationId(stationId);
}
/**
* Get the elevation, in meters, of the observing platform or location.
*
* @return The observation elevation, in meters.
*/
public Integer getElevation() {
Integer elevation = null;
if (spatialInfo != null) {
elevation = (spatialInfo).getElevation();
}
return elevation;
}
/**
* Set the elevation, in meters, of the observing platform or location.
*
* @param elevation
* The elevation to set
*/
public void setElevation(Integer elevation) {
if (spatialInfo == null) {
spatialInfo = new SurfaceObsLocation();
}
(spatialInfo).setElevation(elevation);
}
/**
* @return the timeObs
*/
public Calendar getObsTime() {
return obsTime;
}
/**
* @param timeObs
* the timeObs to set
*/
public void setObsTime(Calendar obsTime) {
this.obsTime = obsTime;
}
/**
* @return the dataTime
*/
public DataTime getDataTime() {
return dataTime;
}
/**
* @param dataTime
* the dataTime to set
*/
public void setDataTime(DataTime dataTime) {
this.dataTime = dataTime;
}
/**
* Get the three char ICAO climo station name associated with this sounding.
*
* @return The ICAO climo station name.
*/
public String getName() {
return name;
}
/**
* Set the three char ICAO climo station name associated with this sounding.
*
* @param name
* The ICAO climo station name.
*/
public void setName(String name) {
this.name = name;
}
// *************************************************
/**
* Gets the maximum pressure layer, always first in array unless added to.
*
* @return
*/
public SoundingLayer getMaxPressurelayer() {
int i = 0;
while (layerData.get(i).getPressure() >= MISSING) {
i++;
}
return layerData.get(i);
}
/**
* Returns the top of the sounding layer
*
* @return SoundingLayer
*/
public SoundingLayer getMinPressureLayer() {
int i = layerData.size();
while (layerData.get(--i).getPressure() >= MISSING) {
}
return layerData.get(i);
}
/**
* Get the layer containing the maximum temperature in the sounding.
*
* @return The layer containing the maximum temperature in the sounding.
*/
public SoundingLayer getMaxTempLayer() {
return maxTemperatureLayer;
}
/**
* Get the layer containing the minimum temperature in the sounding.
*
* @return The layer containing the minimum temperature in the sounding.
*/
public SoundingLayer getMinTempLayer() {
return minTemperatureLayer;
}
/**
* Determine the pressure level of the first occurrence of a specified
* temperature.
*
* @param temperature
* The temperature to find.
* @return The pressure of the first occurrence. Returns a null value if the
* temperature does not exist.
*/
// public Double tempLevel(double temperature) {
// Double temperatureLevel = null;
// updateMaxMinTemp();
// // If either of the following layers are null don't bother checking
// // further.
// if ((maxTemperatureLayer != null) && (minTemperatureLayer != null)) {
// // Check if the temperature is in this sounding's range.
// if (isValid(maxTemperatureLayer.getTemperature())
// && isValid(minTemperatureLayer.getTemperature())) {
// if (temperature < maxTemperatureLayer.getTemperature()
// && temperature > minTemperatureLayer.getTemperature()) {
// SoundingLayer layerBelow = null;
// SoundingLayer layerAbove = null;
//
// // find the first level that contains a valid temperature.
// for (SoundingLayer layer : layerData) {
// if (isValid(layer.getTemperature())) {
// // we're looking for a base layer
// if (layerBelow == null) {
// layerBelow = layer;
// } else {
// // we have a base layer, now find the next layer
// // with a temperature.
// layerAbove = layer;
//
// // now we have two layers with temperature.
// // Find out if the temperature is here.
// Double p = isContained(layerBelow, layerAbove,
// temperature);
// // we found it.
// if (isValid(p)) {
// temperatureLevel = p;
// break;
// }
// }
// }
// }
// }
// }
// }
// return temperatureLevel;
// }
/**
*
* @param type
* @return
*/
public float[] getValues(DATA_TYPE type) {
float f[] = new float[layerData.size()];
for (int i = 0; i < layerData.size(); i++) {
f[i] = layerData.get(i).getValue(type);
}
return f;
}
public String printableSoundingData() {
StringBuffer buffer = new StringBuffer();
buffer.append("START******" + this.getDataTime().getLegendString()
+ "*******/n");
for (SoundingLayer l : layerData) {
buffer.append(l + "\n");
}
buffer.append("END ******" + this.getDataTime().getLegendString()
+ "*******/n");
return buffer.toString();
}
public SurfaceObsLocation getSpatialInfo() {
return spatialInfo;
}
public void setSpatialInfo(SurfaceObsLocation spatialInfo) {
this.spatialInfo = spatialInfo;
}
/**
* Iterate the sounding to recompute the max and min data. This method must
* be used when edits have been performed.
*/
private void updateMaxMinTemp() {
SoundingLayer maxTempLayer = null;
SoundingLayer minTempLayer = null;
double minTemp = Double.MAX_VALUE;
double maxTemp = -Double.MAX_VALUE;
for (SoundingLayer layer : layerData) {
double t = layer.getTemperature();
if (isValid(t)) {
if (t > maxTemp) {
maxTemp = t;
maxTempLayer = layer;
}
if (t < minTemp) {
minTemp = t;
minTempLayer = layer;
}
}
}
maxTemperatureLayer = maxTempLayer;
minTemperatureLayer = minTempLayer;
}
public int size() {
return layerData.size();
}
/**
* Interpolate the desired value at specified pressure
*
* @param pressure
* @param dataType
* @return the interpolated value
*/
public float interpolateValue(float pressure, DATA_TYPE dataType) {
// find the bracketing layers
SoundingLayer layer, prevLayer;
layer = prevLayer = null;
float press = (float) Math.floor(pressure);
for (SoundingLayer l : layerData) {
if (l.getPressure() < MISSING && l.getValue(dataType) < MISSING) {
if (Math.floor(l.getPressure()) <= press) {
layer = l;
break;
}
prevLayer = l;
}
}
// if bracketing layers not found
if (layer == null || prevLayer == null) {
return MISSING;
}
// compute weighting factors
double w1 = Math.log(prevLayer.getPressure() / layer.getPressure());
double w2 = Math.log(prevLayer.getPressure() / pressure) / w1;
w1 = Math.log(pressure / layer.getPressure()) / w1;
// interpolate desired value
double interpVal = w1 * prevLayer.getValue(dataType) + w2
* layer.getValue(dataType);
return (float) interpVal;
}
public void sortByPressure() {
Collections.sort(layerData, SoundingLayer.getPressureComparator());
}
public float totalTotals() {
float totals = -9999.0f;
// TT = (T850 - T500) + (Td850 - T500)
SoundingLayer lyr850 = null;
SoundingLayer lyr500 = null;
Float val = new Float(850.0f);
if (layerMap.containsKey(val)) {
lyr850 = layerMap.get(val);
}
val = new Float(500.0f);
if (layerMap.containsKey(val)) {
lyr500 = layerMap.get(val);
}
if ((lyr850 != null) && (lyr500 != null)) {
// 500 temperature is required for cross and vertical totals.
float t500 = lyr500.getTemperature();
float t850 = lyr850.getTemperature();
float td850 = lyr850.getTemperature();
if (t500 > -9999.0f) {
if (t850 > -9999.0f) {
if (td850 > -9999.0f) {
totals = (t850 - t500) + (td850 - t500);
}
}
}
}
return totals;
}
public float getWindSpeed700() {
float ws = -9999.0f;
// TT = (T850 - T500) + (Td850 - T500)
SoundingLayer lyr700 = null;
Float val = new Float(700.0f);
if (layerMap.containsKey(val)) {
lyr700 = layerMap.get(val);
}
if (lyr700 != null) {
ws = lyr700.getWindSpeed();
}
return ws;
}
public float getWindUComp500() {
float uComp = -9999.0f;
SoundingLayer lyr500 = null;
Float val = new Float(500.0f);
if (layerMap.containsKey(val)) {
lyr500 = layerMap.get(val);
}
if (lyr500 != null) {
float ws = lyr500.getWindSpeed();
float wd = lyr500.getWindDirection();
if ((ws >= 0) && (wd > 0) && (wd < MISSING)) {
uComp = -ws * (float) Math.sin(Math.toRadians(wd));
}
}
return uComp;
}
/**
* Find the height in meters of the freezing level. 1. Surface is the
* freezing level.
*
*
*
*
* @return
*/
public float firstFreezingLevel() {
float fzlLevel = 0;
// Create a list of layers that have both temperature and
// height data.
List<SoundingLayer> data = new ArrayList<SoundingLayer>();
for (SoundingLayer layer : layerData) {
if ((layer.getGeoHeight() != MISSING)
&& (layer.getTemperature() != MISSING)) {
data.add(layer);
}
}
if (data.size() > 0) {
// Scenerio 1 - Freezing level at the surface.
if (data.get(0).getTemperature() == FREEZING) {
fzlLevel = getElevation().floatValue();
} else {
if (data.size() > 2) {
for (int i = 1; i < data.size(); i++) {
float ta = data.get(i).getTemperature();
float tb = data.get(i - 1).getTemperature();
float ha = data.get(i).getGeoHeight();
float hb = data.get(i - 1).getGeoHeight();
if ((ta < FREEZING) && (tb > FREEZING)) {
fzlLevel = interp(hb, tb, ha, ta);
} else if ((ta > FREEZING) && (tb < FREEZING)) {
fzlLevel = interp(hb, tb, ha, ta);
}
}
}
}
}
return fzlLevel;
}
/**
* Interpolate the temperature between tlo and thi at heights hlo and hhi
* respectively.
*
* @param hlo
* @param tlo
* @param hhi
* @param thi
* @return
*/
private static final float interp(float hlo, float tlo, float hhi, float thi) {
return hlo + ((FREEZING - tlo) * (hhi - hlo) / (thi - tlo));
}
public static final void main(String[] args) {
VerticalSounding sounding = new VerticalSounding();
sounding.setElevation(350);
sounding.setInitialLoad(true);
sounding.addLayer(new SoundingLayer(1000.0f, 76f, -9999.0f, -9999.0f,
-9999.0f, -9999.0f, -9999.0f));
sounding.addLayer(new SoundingLayer(969.0f, 350f, 294.3f, 290.73f,
6.18f, 80f, -9999.0f));
sounding.addLayer(new SoundingLayer(925.0f, 757f, 294.1f, 292.7f,
14.93f, 105f, -9999.0f));
sounding.addLayer(new SoundingLayer(850.0f, 1490f, 293.7f, 293.6f,
11.33f, 195f, -9999.0f));
sounding.addLayer(new SoundingLayer(700.0f, 3155f, 285.1f, 277.1f,
16.99f, 245f, -9999.0f));
sounding.addLayer(new SoundingLayer(639.0f, 3910f, 278.3f, 276.3f,
16.99f, 245f, -9999.0f));
sounding.addLayer(new SoundingLayer(611.5f, 4267f, 275.3f, 273.7f,
16.99f, 245f, -9999.0f));
sounding.addLayer(new SoundingLayer(591.0f, 4543f, 273.0f, 271.6f,
16.99f, 245f, -9999.0f));
sounding.addLayer(new SoundingLayer(566.8f, 4877f, 270.4f, 265.9f,
16.99f, 245f, -9999.0f));
sounding.addLayer(new SoundingLayer(500.0f, 5860f, 265.6f, 254.6f,
11.8f, 295f, -9999.0f));
sounding.addLayer(new SoundingLayer(493.0f, 5970f, 265.4f, 252.4f, 38f,
260f, -9999.0f));
sounding.setInitialLoad(false);
sounding.invalidate();
for (SoundingLayer layer : sounding) {
System.out.println(layer);
}
System.out.println("Total totals = " + sounding.totalTotals());
System.out.println("700mb Wind Speed = " + sounding.getWindSpeed700());
System.out.println("500mb u comp = " + sounding.getWindUComp500());
System.out.println("Freezing level 1 = "
+ sounding.firstFreezingLevel());
sounding = new VerticalSounding();
sounding.setElevation(350);
sounding.setInitialLoad(true);
sounding.addLayer(new SoundingLayer(969.0f, 350f, 273.15f, MISSING,
MISSING, MISSING, MISSING));
sounding.addLayer(new SoundingLayer(925.0f, 757f, 275f, MISSING,
MISSING, MISSING, MISSING));
sounding.addLayer(new SoundingLayer(925.0f, 757f, 278f, MISSING,
MISSING, MISSING, MISSING));
sounding.setInitialLoad(false);
sounding.invalidate();
System.out.println("Freezing level 2 = "
+ sounding.firstFreezingLevel());
sounding = new VerticalSounding();
sounding.setElevation(350);
sounding.setInitialLoad(true);
sounding.addLayer(new SoundingLayer(969.0f, 350f, 263.15f, MISSING,
MISSING, MISSING, MISSING));
sounding.addLayer(new SoundingLayer(925.0f, 757f, 271f, MISSING,
MISSING, MISSING, MISSING));
sounding.addLayer(new SoundingLayer(925.0f, 1500f, 275f, MISSING,
MISSING, MISSING, MISSING));
sounding.setInitialLoad(false);
sounding.invalidate();
System.out.println("Freezing level 3 = "
+ sounding.firstFreezingLevel());
}
}