//
// Vis5DVerticalSystem.java
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad.data.vis5d;
import visad.CommonUnit;
import visad.CoordinateSystem;
import visad.ErrorEstimate;
import visad.Gridded1DSet;
import visad.IdentityCoordinateSystem;
import visad.Linear1DSet;
import visad.RealTupleType;
import visad.RealType;
import visad.SampledSet;
import visad.Set;
import visad.Unit;
import visad.VisADException;
import visad.data.units.ParseException;
import visad.data.units.Parser;
/**
* Class for encapsulating the Vis5D vertical system as VisAD
* MathTypes and Data objects
*/
public class Vis5DVerticalSystem
{
private static int counter = 0;
/** Unit used for the vertical system */
Unit vert_unit = null;
/** Sampled Set of values */
SampledSet vertSet;
/** RealType of the vertical system parameter */
RealType vert_type;
/** CoordinateSystem to transform to Reference values */
CoordinateSystem vert_cs;
/** Reference RealTupleType for the CoordinateSystem */
RealTupleType reference;
/**
* Construct the VisAD MathTypes and Data objects that relate
* to the Vis5D vertical system parameters.
* @param vert_sys Vis5D vertical System
* @param n_levels number of levels in vert_args
* @param vert_args array of vertical level values
* @throws VisADException unknown vert_sys or problem creating VisAD
* objects.
* @see visad.data.vis5d.V5DStruct#VertArgs for explanation of vertical
* coordinates.
*/
public Vis5DVerticalSystem( int vert_sys,
int n_levels,
double[] vert_args)
throws VisADException
{
switch ( vert_sys )
{
case (0):
vert_unit = CommonUnit.promiscuous;
vert_type = makeRealType("Height", vert_unit);
reference = new RealTupleType(RealType.Generic);
vert_cs = new IdentityCoordinateSystem(reference);
break;
case (1):
case (2):
try {
vert_unit = Parser.parse("km");
}
catch (ParseException e) {
}
vert_type = makeRealType("Height", vert_unit);
reference = new RealTupleType(RealType.Altitude);
vert_cs = new IdentityCoordinateSystem(reference);
break;
case (3):
try {
vert_unit = Parser.parse("mbar");
}
catch (ParseException e) {
}
vert_type = makeRealType("Pressure", vert_unit);
reference = new RealTupleType(RealType.Altitude);
vert_cs = new Vis5DVerticalCoordinateSystem();
break;
default:
throw new VisADException("vert_sys unknown");
}
switch ( vert_sys )
{
case (0):
case (1):
double first = vert_args[0];
double last = first + vert_args[1]*(n_levels-1);
vertSet = new Linear1DSet(vert_type, first, last, n_levels,
(CoordinateSystem) null, new Unit[] {vert_unit},
(ErrorEstimate[]) null);
break;
case (2): // Altitude in km - non-linear
double[][] values = new double[1][n_levels];
System.arraycopy(vert_args, 0, values[0], 0, n_levels);
vertSet =
new Gridded1DSet(vert_type, Set.doubleToFloat(values), n_levels,
(CoordinateSystem) null, new Unit[] {vert_unit},
(ErrorEstimate[]) null);
break;
case (3): // heights of pressure surfaces in km - non-linear
double[][] pressures = new double[1][n_levels];
System.arraycopy(vert_args, 0, pressures[0], 0, n_levels);
for (int i = 0; i < n_levels; i++) pressures[0][i] *=1000; // km->m
pressures = vert_cs.fromReference(pressures); // convert to pressures
vertSet =
new Gridded1DSet(vert_type, Set.doubleToFloat(pressures), n_levels,
(CoordinateSystem) null, new Unit[] {vert_unit},
(ErrorEstimate[]) null);
break;
default:
throw new VisADException("vert_sys unknown");
}
}
/** create a unique RealType for the specified name and unit */
private RealType makeRealType(String name, Unit unit)
throws VisADException {
RealType rt = null;
rt = RealType.getRealType(name, unit);
if (rt == null) {
rt = RealType.getRealType(name+"_"+counter++, unit);
if (rt == null) {
throw new VisADException(
"Unable to create a unique RealType named " + name +
" with unit " + unit);
}
}
return rt;
}
/**
* Vis5DVerticalCoordinateSystem is the VisAD class for coordinate
* systems for transforming pressure in millibars to Altitude
* in m. It uses the standard Vis5D climate formulas:
* <pre>
* P = 1012.5 * e^( H / -7.2 ) (^ denotes exponentiation)
*
* H = -7.2 * Ln( P / 1012.5 ) (Ln denotes natural log)
* </pre>
* for the transformations (in this case H is in km).
* <P>
*/
public static class Vis5DVerticalCoordinateSystem extends CoordinateSystem
{
/** Default scale value for logarithmic vertical coordinate system */
private static final double DEFAULT_LOG_SCALE = 1012.5;
/** Default exponent value for logarithmic vertical coordinate system */
private static final double DEFAULT_LOG_EXP = -7.2;
private static Unit[] csUnits;
static {
try {
csUnits = new Unit[] {Parser.parse("mbar")};
}
catch (ParseException pe) {;} // can't happen?
}
/**
* Construct a new vertical transformation system
*/
public Vis5DVerticalCoordinateSystem()
throws VisADException
{
super( new RealTupleType(RealType.Altitude), csUnits );
}
/**
* Converts pressures in millibars to altitude in meters.
* @param pressures array of pressures
* @return array of corresponding altitudes
* @throws VisADException illegal input
*/
public double[][] toReference(double[][] pressures)
throws VisADException
{
int length = pressures[0].length;
double[][] alts = new double[1][length];
for (int kk = 0; kk < length; kk++) {
alts[0][kk] = pressureToAltitude(pressures[0][kk]);
}
return alts;
}
/**
* Converts altitudes in m to pressure in millibars.
* @param alts array of altitudes
* @return array of corresponding pressures
* @throws VisADException illegal input
*/
public double[][] fromReference(double[][] alts)
throws VisADException
{
int length = alts[0].length;
double[][] pressures = new double[1][length];
for (int kk = 0; kk < length; kk++) {
pressures[0][kk] = altitudeToPressure(alts[0][kk]);
}
return pressures;
}
/**
* Converts pressures in millibars to altitude in meters.
* @param pressures array of pressures
* @return array of corresponding altitudes
* @throws VisADException illegal input
*/
public float[][] toReference(float[][] pressures)
throws VisADException
{
int length = pressures[0].length;
float[][] alts = new float[1][length];
for (int kk = 0; kk < length; kk++) {
alts[0][kk] = (float) pressureToAltitude(pressures[0][kk]);
}
return alts;
}
/**
* Converts altitudes in m to pressure in millibars.
* @param alts array of altitudes
* @return array of corresponding pressures
* @throws VisADException illegal input
*/
public float[][] fromReference(float[][] alts)
throws VisADException
{
int length = alts[0].length;
float[][] pressures = new float[1][length];
for (int kk = 0; kk < length; kk++) {
pressures[0][kk] = (float) altitudeToPressure(alts[0][kk]);
}
return pressures;
}
/**
* Checks the equality of o against this coordinate system
* @param o object in question
* @return true if o is a Vis5DVerticalCoordinateSystem
*/
public boolean equals(Object o) {
return (o instanceof Vis5DVerticalCoordinateSystem);
}
/**
* Converts an altitude value in meters to a pressure value in
* millibars. It uses the standard Vis5D climate formula:
* <pre>
* P = 1012.5 * e^( H / -7.2 ) (^ denotes exponentiation)
*
* (H is in km in this formula, but input value is meters)
* </pre>
* @param alt value to convert
* @return corresponding pressure value
*/
public static double altitudeToPressure(double alt) {
return (DEFAULT_LOG_SCALE * Math.exp((alt/1000.) / DEFAULT_LOG_EXP));
}
/**
* Converts a pressure value in millibars to an altitude in
* meters. It uses the standard Vis5D climate formula:
* <pre>
* H = -7.2 * Ln( P / 1012.5 ) (Ln denotes natural log)
*
* (H is in km in this formula, but returned value is meters)
* </pre>
* @param pressure value to convert
* @return corresponding altitude value
*/
public static double pressureToAltitude(double pressure) {
return (DEFAULT_LOG_EXP *
Math.log( pressure / DEFAULT_LOG_SCALE)) * 1000.;
}
}
}