Omaha #5149 Fix interpolation of hurricane tracks.

Former-commit-id: 60a54772b861a66f887a7ba1f6962f252c3b3ad4
This commit is contained in:
Ben Steffensmeier 2015-11-24 15:43:43 -06:00
parent 38c6961187
commit 1cd13731c0
3 changed files with 88 additions and 105 deletions

View file

@ -38,7 +38,7 @@ import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.tcs.Radius;
import com.raytheon.uf.common.dataplugin.tcs.TropicalCycloneSummary;
import com.raytheon.uf.common.dataplugin.tcs.util.TCSConstants;
import com.raytheon.uf.common.dataplugin.tcs.util.Util;
import com.raytheon.uf.common.dataplugin.tcs.util.TcsUtil;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
import com.raytheon.uf.common.pointdata.PointDataContainer;
@ -69,18 +69,19 @@ import com.raytheon.viz.pointdata.PointDataRequest;
import com.vividsolutions.jts.geom.Coordinate;
/**
* Tropical Cyclone summary resource
* Resource for displaying data from a {@link TropicalCycloneSummary}.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 22, 2010 jsanchez Initial creation
* Jul 29, 2014 #3465 mapeters Updated deprecated drawString() calls.
* Sep 17, 2014 3632 bclement fixed index out of bounds
* Nov 05, 2015 5070 randerso Adjust font sizes for dpi scaling
* Date Ticket# Engineer Description
* ------------- -------- --------- ---------------------------------------
* Oct 22, 2010 jsanchez Initial creation
* Jul 29, 2014 3465 mapeters Updated deprecated drawString() calls.
* Sep 17, 2014 3632 bclement fixed index out of bounds
* Nov 05, 2015 5070 randerso Adjust font sizes for dpi scaling
* Nov 30, 2015 5149 bsteffen Rename TcsUtil, update class javadoc
*
* </pre>
*
@ -177,7 +178,7 @@ public class TCSResource extends
PaintProperties paintProps, PointDataView pdv, double scale)
throws VizException {
TropicalCycloneSummary storm = Util.interplateStorm(pdv,
TropicalCycloneSummary storm = TcsUtil.interplateStorm(pdv,
paintProps.getDataTime());
displayOneStorm(target, paintProps, (float) storm.getLongitude(),
(float) storm.getLatitude(), storm.getPressure(),
@ -299,14 +300,12 @@ public class TCSResource extends
if (paintProps.getZoomLevel() * descriptor.getMapWidth() / 1000 < ZOOM_LEVEL) {
drawLegends(target, paintProps, scale);
drawRadius(target, name, descriptor.pixelToWorld(loc), scale,
radiusList);
drawRadius(target, descriptor.pixelToWorld(loc), radiusList);
}
}
private void drawRadius(IGraphicsTarget target, String name,
double[] latLon, double scale, ArrayList<Radius> radiusList)
throws VizException {
private void drawRadius(IGraphicsTarget target, double[] latLon,
ArrayList<Radius> radiusList) throws VizException {
Coordinate[] coordinates = null;
// Draw the XX KT... lines.

View file

@ -28,26 +28,28 @@ import com.raytheon.uf.common.dataplugin.tcs.Radius;
import com.raytheon.uf.common.dataplugin.tcs.TropicalCycloneSummary;
import com.raytheon.uf.common.pointdata.PointDataView;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.util.TimeUtil;
/**
* TODO Add Description
* Static Utility methods that are useful for parsing and displaying
* {@link TropicalCycloneSummary}s.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 30, 2010 jsanchez Initial creation
* Jul 23, 2014 3410 bclement location changed to floats
* Date Ticket# Engineer Description
* ------------- -------- --------- -------------------------------------------
* Oct 30, 2010 jsanchez Initial creation
* Jul 23, 2014 3410 bclement location changed to floats
* Nov 24, 2015 5149 bsteffen Handle case of forecast spanning midnight.
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class Util implements TCSConstants {
public class TcsUtil implements TCSConstants {
private static final Pattern tcmWindRadius = Pattern
.compile(".*((\\d{2}) KT...|12 FT SEAS.)\\W{0,}\\s{0,}(\\d{1,3})NE\\s{1,}(\\d{1,3})SE\\s{1,}(\\d{1,3})SW\\s{1,}(\\d{1,3})NW.");
@ -59,16 +61,6 @@ public class Util implements TCSConstants {
public static TropicalCycloneSummary interplateStorm(PointDataView pdv,
DataTime dataTime) {
TropicalCycloneSummary tcs = null;
float t1 = 0.0f, t2 = 0.0f, tf = 0.0f, dt = 0.0f, tc = 0.0f;
Calendar calendar = dataTime.getRefTimeAsCalendar();
int day = dataTime.getValidTime().get(Calendar.DAY_OF_MONTH);
int hour = dataTime.getValidTime().get(Calendar.HOUR_OF_DAY);
boolean tropical = true;
int wind = 0;
float lat = 29.0f; // This came from TCMplotter.C
float lon = -89.6f; // This came from TCMplotter.C
String dTime = "Dissipated";
@ -92,52 +84,46 @@ public class Util implements TCSConstants {
isTropical[size - 1].intValue() == 1 ? true : false);
}
// Searching two storms with the time, s1 and s2 in the list.
// The time is between s1.time and s2.time. We need not do the
// interplattion
// if time == s1.time or time == s2.time.
// Convert the given forecast time to hours.
tf = dataTime.getValidTime().get(Calendar.HOUR_OF_DAY);
t1 = Integer.parseInt(displayTime[0].split("\\.")[1]);
// fore cast time is 0
if (dataTime.getValidTime().getTimeInMillis() == dataTime.getRefTime()
.getTime()) {
tcs = new TropicalCycloneSummary(name, pressure,
longitude[0].floatValue(), latitude[0].floatValue(),
displayTime[0], windSpeed[0].intValue(),
if (dataTime.getFcstTime() == 0) {
TropicalCycloneSummary tcs = new TropicalCycloneSummary(name,
pressure, longitude[0].floatValue(),
latitude[0].floatValue(), displayTime[0],
windSpeed[0].intValue(),
isTropical[0].intValue() == 1 ? true : false);
tcs.setRadiusList(loadRadiusList(pdv, 0));
return tcs;
}
/*
* Search for an index i such that the validTime falls between the time
* for displayTime[i - 1] and displayTime[i].
*/
int i = 1;
/*
* tc is a weight value(between 0 and 1) for the distance from i. So as
* validTime moves further from displayTime[i](and closer to
* displayTime[i - 1]) tc will get larger.
*/
float tc = 0.0f;
Calendar refTime = dataTime.getRefTimeAsCalendar();
Calendar validTime = dataTime.getValidTime();
Calendar prevTime = parseTime(refTime, displayTime[0]);
while (i < size) {
dt = Integer.parseInt(displayTime[i - 1].split("\\.")[1]);
t2 = Integer.parseInt(displayTime[i].split("\\.")[1]);
dt = t2 - dt; // dt is the delta between dt and t2
if (dt <= 0) {
dt += 24;
}
t2 = t1 + dt;
tc = Math.abs(t2 - tf);
if (tc < 0.01) {
tcs = new TropicalCycloneSummary(name, 0,
longitude[i].floatValue(), latitude[i].floatValue(),
Calendar nextTime = parseTime(refTime, displayTime[i]);
if (nextTime.equals(validTime)) {
TropicalCycloneSummary tcs = new TropicalCycloneSummary(name,
0, longitude[i].floatValue(), latitude[i].floatValue(),
displayTime[i], windSpeed[i].intValue(),
isTropical[i].intValue() == 1 ? true : false);
tcs.setRadiusList(loadRadiusList(pdv, i));
return tcs;
} else if (t2 < tf) {
t1 = t2; // shifting the base or t1 time
} else if (nextTime.before(validTime)) {
prevTime = nextTime;
} else {
long t2 = nextTime.getTimeInMillis();
long t1 = prevTime.getTimeInMillis();
long tf = validTime.getTimeInMillis();
tc = (float) ((t2 - tf) / (double) (t2 - t1));
break;
}
i++;
@ -155,32 +141,11 @@ public class Util implements TCSConstants {
isTropical[size - 1].intValue() == 1 ? true : false);
}
tc = (t2 - tf) / (t2 - t1);
if (tc > 1 && t2 == 24) {
tc = -tf / (t2 - t1);
}
// Create the "dd.hh" string of the given time
dt = tf - t1;
Number isTrop = tc <= 0.5 ? isTropical[i] : isTropical[i - 1];
boolean tropical = (isTrop.intValue() == 1);
int isTrop = (dt / 2.0 + t1) >= tf ? isTropical[i].intValue()
: isTropical[i - 1].intValue();
tropical = isTrop == 1 ? true : false;
t1 = Integer.parseInt(displayTime[i - 1].split("\\.")[1]);
day = Integer.parseInt(displayTime[i - 1].split("\\.")[0]);
hour = (int) (t1 + dt + 0.5);
if (hour > 24) {
hour = hour - 24;
calendar.set(Calendar.DAY_OF_MONTH,
Integer.parseInt(displayTime[i].split("\\.")[0]));
calendar.set(Calendar.HOUR_OF_DAY,
Integer.parseInt(displayTime[i].split("\\.")[1]));
day++;
}
dTime = day + "." + (hour < 10 ? "0" + hour : hour);
// Interplating with the t1, t2 and tm.
dTime = String.format("%d.%02d", validTime.get(Calendar.DAY_OF_MONTH),
validTime.get(Calendar.HOUR_OF_DAY));
// Location
lat = latitude[i].floatValue() - tc
@ -189,7 +154,7 @@ public class Util implements TCSConstants {
* (longitude[i].floatValue() - longitude[i - 1].floatValue());
// Wind speed
wind = (int) (windSpeed[i].intValue() - tc
int wind = (int) (windSpeed[i].intValue() - tc
* (windSpeed[i].intValue() - windSpeed[i - 1].intValue()) + 0.6);
// Loading the radius
@ -201,7 +166,7 @@ public class Util implements TCSConstants {
// Wind radius, we use longer one of the _list[i-1].radiusList(t1) and
// _list[i].radiusList(t2) to loop. The distance is "0" if a wind radius
// is not exsiting in another list. How about it is "-1"?, we will
// is not existing in another list. How about it is "-1"?, we will
// consider
// it latter.
int j = 0, rx1, rx2;
@ -273,12 +238,26 @@ public class Util implements TCSConstants {
j++;
}
}
tcs = new TropicalCycloneSummary(name, 0, lon, lat, dTime, wind,
tropical);
TropicalCycloneSummary tcs = new TropicalCycloneSummary(name, 0, lon,
lat, dTime, wind, tropical);
tcs.setRadiusList(radiusList);
return tcs;
}
private static Calendar parseTime(Calendar refTime, String displayTime) {
String[] halves = displayTime.split("\\.");
int year = refTime.get(Calendar.YEAR);
int month = refTime.get(Calendar.MONTH);
int day = Integer.parseInt(halves[0]);
int hour = Integer.parseInt(halves[1]);
Calendar c = TimeUtil.newGmtCalendar(year, month, day);
c.set(Calendar.HOUR_OF_DAY, hour);
if (c.before(refTime)) {
c.add(Calendar.MONTH, 1);
}
return c;
}
private static ArrayList<Radius> loadRadiusList(PointDataView pdv, int index) {
// Loading the radius
int COLUMN_WIDTH = 4;

View file

@ -32,7 +32,7 @@ import java.util.regex.Pattern;
import com.raytheon.uf.common.dataplugin.tcs.Radius;
import com.raytheon.uf.common.dataplugin.tcs.TropicalCycloneSummary;
import com.raytheon.uf.common.dataplugin.tcs.util.Util;
import com.raytheon.uf.common.dataplugin.tcs.util.TcsUtil;
import com.raytheon.uf.common.pointdata.PointDataDescription;
import com.raytheon.uf.common.pointdata.spatial.SurfaceObsLocation;
import com.raytheon.uf.common.time.DataTime;
@ -41,20 +41,25 @@ import com.raytheon.uf.common.wmo.WMOTimeParser;
import com.raytheon.uf.edex.plugin.tcs.TropicalCycloneSummaryDao;
/**
* TODO Add Description
* Adapter for parsing a Tropical Cyclone Forecast/Advisory. TCM is the NNN of
* the AFOS PIL for Tropical Cyclone Forecast/Advisories. More information on
* the format of this product can be found at
* http://www.nhc.noaa.gov/aboutnhcprod.shtml#TCM.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 20, 2010 jsanchez Initial creation
* Apr 19, 2012 457 dgilling Use TimeTools.findDataTime() to
* calculate times.
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract
* May 14, 2014 2536 bclement moved WMO Header to common, removed TimeTools usage
* Jul 23, 2014 3410 bclement location changed to floats
* Date Ticket# Engineer Description
* ------------- -------- --------- --------------------------------------------
* Oct 20, 2010 jsanchez Initial creation
* Apr 19, 2012 457 dgilling Use TimeTools.findDataTime() to calculate
* times.
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract
* May 14, 2014 2536 bclement moved WMO Header to common, removed
* TimeTools usage
* Jul 23, 2014 3410 bclement location changed to floats
* Nov 30, 2015 5149 bsteffen Rename TcsUtil, add class javadoc
*
* </pre>
*
@ -209,7 +214,7 @@ public class TCMData extends TCSDataAdapter {
}
String mask = line;
// Searching for the wind radius
if (Util.isWindRadius(type, mask, radius)) {
if (TcsUtil.isWindRadius(type, mask, radius)) {
if ((radius.getKFUnit() != 'x') && (radius.getKT_FT() != -1)
&& (radius.getNE() != -1) && (radius.getSE() != -1)
&& (radius.getSW() != -1) && (radius.getNW() != -1)) {