Issue #1149 Refactored watches and warnings.

Former-commit-id: 3e4148ce433d9f8810f383f56c8ca9b24802de93
This commit is contained in:
Jonathan Sanchez 2012-10-01 12:19:32 -05:00
parent 01a13dee33
commit e859dd05e9
7 changed files with 1087 additions and 1652 deletions

View file

@ -2,19 +2,59 @@ package com.raytheon.viz.warnings.rsc;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.swt.graphics.RGB;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord;
import com.raytheon.uf.common.dataplugin.warning.PracticeWarningRecord;
import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.common.time.TimeRange;
import com.raytheon.uf.viz.core.DrawableString;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment;
import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle;
import com.raytheon.uf.viz.core.IGraphicsTarget.TextStyle;
import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment;
import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.uf.viz.core.catalog.LayerProperty;
import com.raytheon.uf.viz.core.datastructure.DataCubeContainer;
import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo;
import com.raytheon.uf.viz.core.drawables.IFont;
import com.raytheon.uf.viz.core.drawables.IShadedShape;
import com.raytheon.uf.viz.core.drawables.IWireframeShape;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.MapDescriptor;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IResourceDataChanged;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceType;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability;
import com.raytheon.viz.core.mode.CAVEMode;
import com.raytheon.viz.warnings.DateUtil;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
/**
*
@ -33,6 +73,7 @@ import com.raytheon.viz.warnings.DateUtil;
* May 31, 2012 DR14992 mgamazaychikov Changed the order of strings in the
* String array returned from getText method
* Jun 04, 2012 DR14992 mgamazaychikov Reversed the previous changes
* Sep 26, 2012 jsanchez Refactored AbstractWarningResource and AbstractWatchesResource into this class.
*
* </pre>
*
@ -42,6 +83,60 @@ import com.raytheon.viz.warnings.DateUtil;
public abstract class AbstractWWAResource extends
AbstractVizResource<WWAResourceData, MapDescriptor> implements
IResourceDataChanged {
protected static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(AbstractWWAResource.class);
protected class WarningEntry {
protected AbstractWarningRecord record;
protected IWireframeShape wireframeShape;
protected IShadedShape shadedShape;
/**
* whether or not the warning has been altered, ie CON, CAN, EXP. a
* warning can only be altered once with the exception of a partial
* cancel.
**/
protected boolean altered = false;
protected Date timeAltered;
protected Date frameAltered;
/**
* was the alter a partial cancel? if it was then a matching CON should
* be processed and added
*/
protected boolean partialCancel = false;
/**
* set to true if paint needs to re-init the shape
*/
protected boolean project = false;
}
protected static GeometryFactory gf = new GeometryFactory();
protected static PreparedGeometryFactory pgf = new PreparedGeometryFactory();
/** one hour ahead, entirely arbitrary/magic **/
private static final long LAST_FRAME_ADJ = (60 * 60 * 1000);
protected String resourceName;
/** map of dataURI to a warning entry **/
protected Map<String, WarningEntry> entryMap;
protected IFont warningsFont;
protected RGB color;
protected DataTime earliestRequested;
protected final Object paintLock = new Object();
private static final DataTime[] dataTimes = AbstractVizResource.TIME_AGNOSTIC
.toArray(new DataTime[0]);
@ -57,13 +152,23 @@ public abstract class AbstractWWAResource extends
protected List<AbstractWarningRecord> recordsToLoad;
protected WarningRecordComparator comparator = new WarningRecordComparator();
protected abstract void updateDisplay(IGraphicsTarget target)
throws VizException;
protected abstract void initShape(IGraphicsTarget target,
AbstractWarningRecord record) throws VizException;
public AbstractWWAResource(WWAResourceData data, LoadProperties props) {
super(data, props);
this.recordsToLoad = new ArrayList<AbstractWarningRecord>();
resourceData.addChangeListener(this);
getCapability(OutlineCapability.class).setOutlineWidth(2);
color = getCapability((ColorableCapability.class)).getColor();
this.entryMap = new ConcurrentHashMap<String, WarningEntry>();
}
protected final WarningRecordComparator comparator = new WarningRecordComparator();
/*
* (non-Javadoc)
*
@ -79,6 +184,316 @@ public abstract class AbstractWWAResource extends
return true;
}
@Override
public String inspect(ReferencedCoordinate coord) throws VizException {
if (resourceData.hideSampling) {
return "";
}
// check if we are in the last frame
boolean lastFrame = false;
FramesInfo framesInfo = this.descriptor.getFramesInfo();
int frameIdx = framesInfo.getFrameIndex();
DataTime[] frameTimes = framesInfo.getFrameTimes();
if (frameIdx < 0 || frameIdx >= frameTimes.length)
return "NO DATA";
DataTime time = frameTimes[frameIdx];
TimeRange framePeriod = null;
if (frameIdx + 1 < frameTimes.length) {
framePeriod = new TimeRange(time.getRefTime(),
frameTimes[frameIdx + 1].getRefTime());
} else {
framePeriod = new TimeRange(time.getRefTime(), LAST_FRAME_ADJ);
lastFrame = true;
}
if (time != null) {
try {
Point point = gf.createPoint(coord.asLatLon());
for (String key : entryMap.keySet()) {
WarningEntry entry = entryMap.get(key);
AbstractWarningRecord record = entry.record;
if (matchesFrame(entry, time, framePeriod, lastFrame)) {
Geometry recordGeom = record.getGeometry();
for (int i = 0; i < recordGeom.getNumGeometries(); i++) {
PreparedGeometry prepGeom = pgf.create(recordGeom
.getGeometryN(i));
if (prepGeom.contains(point)) {
StringBuffer sb = new StringBuffer();
String[] textToPrint = getText(record, 0);
for (String text : textToPrint) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append(text);
}
return sb.toString();
}
}
}
}
} catch (Exception e) {
throw new VizException("Error inspecting resource", e);
}
}
return "NO DATA";
}
protected void disposeEntry(final WarningEntry entry) {
if (entry.wireframeShape != null || entry.shadedShape != null) {
VizApp.runAsync(new Runnable() {
@Override
public void run() {
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
}
if (entry.wireframeShape != null) {
entry.wireframeShape.dispose();
}
}
});
}
}
@Override
public void project(CoordinateReferenceSystem crs) throws VizException {
synchronized (paintLock) {
for (Map.Entry<String, WarningEntry> entry : entryMap.entrySet()) {
WarningEntry warning = entry.getValue();
// dispose and set to null just to be safe
if (warning.shadedShape != null) {
warning.shadedShape.dispose();
warning.shadedShape = null;
}
if (warning.wireframeShape != null) {
warning.wireframeShape.dispose();
warning.wireframeShape = null;
}
warning.project = true;
}
}
}
@Override
protected void paintInternal(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
FramesInfo info = paintProps.getFramesInfo();
DataTime[] frames = info.getFrameTimes();
int frameToRequestedCompare = frames[0].compareTo(earliestRequested);
if (frameToRequestedCompare < 0) {
// we haven't requested data this far back
this.requestData(frames[0]);
} else if (frameToRequestedCompare > 0) {
// the previous earliest frame was removed as updates came in, so
// the warnings need to be disposed and we need to update the
// earliestRequested so if they ever went back in time to that
// again, it would be re-requested
earliestRequested = frames[0];
if (paintProps.getDataTime() != null) {
cleanupData(paintProps.getDataTime(), frames);
}
}
int index = info.getFrameIndex();
if (!this.recordsToLoad.isEmpty()) {
this.updateDisplay(target);
}
DataTime thisFrameTime = null;
if (index > -1 && index < frames.length) {
thisFrameTime = frames[index];
}
if (thisFrameTime == null) {
return;
}
TimeRange framePeriod = null;
boolean lastFrame = false;
if (index + 1 < frames.length) {
framePeriod = new TimeRange(thisFrameTime.getRefTime(),
frames[index + 1].getRefTime());
} else {
framePeriod = new TimeRange(thisFrameTime.getRefTime(),
LAST_FRAME_ADJ);
lastFrame = true;
}
synchronized (paintLock) {
HashMap<String, WarningEntry> candidates = new HashMap<String, WarningEntry>();
for (WarningEntry entry : entryMap.values()) {
if (matchesFrame(entry, paintProps.getDataTime(), framePeriod,
lastFrame)) {
String key = getEventKey(entry);
WarningEntry current = candidates.get(key);
if (current == null
|| current.record.getIssueTime().before(
entry.record.getIssueTime())
|| (current.record.getIssueTime().equals(
entry.record.getIssueTime()) && current.record
.getInsertTime().before(
entry.record.getInsertTime())))
candidates.put(key, entry);
}
}
for (WarningEntry entry : candidates.values()) {
AbstractWarningRecord record = entry.record;
// check shapes
if (entry.project) {
initShape(target, entry.record);
entry.project = false;
}
if (entry != null && entry.wireframeShape != null) {
LineStyle lineStyle = (record.getProductClass() != null && record
.getProductClass().equals("T")) ? LineStyle.DASHED
: LineStyle.SOLID;
target.drawWireframeShape(
entry.wireframeShape,
getCapability(ColorableCapability.class).getColor(),
getCapability(OutlineCapability.class)
.getOutlineWidth(), lineStyle);
} else if (entry != null && entry.shadedShape != null) {
target.drawShadedShape(entry.shadedShape, 1);
}
if (record != null && record.getGeometry() != null) {
// Calculate the upper left portion of the polygon
Coordinate upperLeft = new Coordinate(180, -90);
for (Coordinate c : record.getGeometry().getCoordinates()) {
if (c.y - c.x > upperLeft.y - upperLeft.x) {
upperLeft = c;
}
}
double[] d = descriptor.worldToPixel(new double[] {
upperLeft.x, upperLeft.y });
d[0] -= paintProps.getZoomLevel() * 100;
double mapWidth = descriptor.getMapWidth()
* paintProps.getZoomLevel() / 1000;
String[] textToPrint = getText(record, mapWidth);
if (warningsFont == null) {
warningsFont = target.getDefaultFont().deriveWithSize(
11);
}
// DR14992: reverse the textToPrint array to plot the
// strings in correct order
String[] textToPrintReversed = new String[textToPrint.length];
for (int i = 0; i < textToPrint.length; i++) {
textToPrintReversed[i] = textToPrint[textToPrint.length
- i - 1];
}
DrawableString params = new DrawableString(
textToPrintReversed, color);
params.font = warningsFont;
params.setCoordinates(d[0], d[1]);
params.textStyle = TextStyle.NORMAL;
params.horizontalAlignment = HorizontalAlignment.RIGHT;
params.verticallAlignment = VerticalAlignment.BOTTOM;
params.magnification = getCapability(
MagnificationCapability.class).getMagnification();
target.drawStrings(params);
}
}
}
}
abstract protected String getEventKey(WarningEntry entry);
protected boolean matchesFrame(WarningEntry entry, DataTime paintTime,
TimeRange framePeriod, boolean lastFrame) {
TimeRange recordPeriod = new TimeRange(entry.record.getStartTime()
.getTimeInMillis(), entry.record.getEndTime().getTimeInMillis());
long diff = entry.record.getEndTime().getTimeInMillis()
- entry.record.getStartTime().getTimeInMillis();
Date centerTime = new Date(entry.record.getStartTime()
.getTimeInMillis() + (diff / 2));
Date frameTime = framePeriod.getStart();
Date frameStart = framePeriod.getStart();
Date refTime = entry.record.getDataTime().getRefTime();
if (lastFrame) {
// use current system time to determine what to display
Date timeToDisplay = SimulatedTime.getSystemTime().getTime();
// change frame time
frameTime = timeToDisplay;
// point paint time to different time
paintTime = new DataTime(timeToDisplay);
// point framePeriod to new frame
framePeriod = new TimeRange(frameTime, LAST_FRAME_ADJ);
}
// check if the warning is cancelled
WarningAction action = WarningAction.valueOf(entry.record.getAct());
if (action == WarningAction.CAN && refTime.equals(paintTime)) {
return false;
// If this entry has been altered/updated, display its pre-altered
// version
// only in the frames prior to the time it was altered
} else if (entry.altered) {
if (frameStart.getTime() >= refTime.getTime()
&& frameStart.getTime() < (entry.timeAltered.getTime())
&& frameStart.getTime() < entry.frameAltered.getTime())
return true;
if (frameStart.getTime() >= (entry.timeAltered.getTime()))
return false;
} else if (refTime.equals(paintTime)
|| recordPeriod.contains(frameTime)
|| (framePeriod.contains(centerTime) && (!lastFrame || !entry.altered))) {
return true;
}
return false;
}
protected void cleanupData(DataTime paintTime, DataTime[] descFrameTimes) {
System.out.println("entryMap size " + entryMap.size());
List<TimeRange> framePeriods = new ArrayList<TimeRange>(
descFrameTimes.length);
for (int i = 0; i < descFrameTimes.length; i++) {
if (i == descFrameTimes.length - 1) {
framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(),
LAST_FRAME_ADJ));
} else {
framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(),
descFrameTimes[i + 1].getRefTime()));
}
}
int size = framePeriods.size();
List<String> toRemove = new ArrayList<String>();
for (String key : entryMap.keySet()) {
WarningEntry entry = entryMap.get(key);
boolean found = false;
for (int i = 0; i < size; i++) {
TimeRange tr = framePeriods.get(i);
if (matchesFrame(entry, paintTime, tr, (i == size - 1))) {
found = true;
break;
}
}
if (!found) {
toRemove.add(key);
}
}
for (String key : toRemove) {
WarningEntry entry = entryMap.remove(key);
System.out.println("removing " + entry.record.getDataURI());
disposeEntry(entry);
}
}
public synchronized void addRecord(PluginDataObject[] pdos)
throws VizException {
for (PluginDataObject pdo : pdos) {
@ -94,6 +509,59 @@ public abstract class AbstractWWAResource extends
}
}
protected PluginDataObject[] sort(PluginDataObject[] pdos) {
ArrayList<AbstractWarningRecord> sortedWarnings = new ArrayList<AbstractWarningRecord>();
for (Object o : pdos) {
if (((PluginDataObject) o) instanceof AbstractWarningRecord) {
AbstractWarningRecord record = (AbstractWarningRecord) o;
sortedWarnings.add(record);
}
}
/* Sorts by phensig, etn, starttime (descending), act */
Collections.sort(sortedWarnings, comparator);
return sortedWarnings.toArray(new AbstractWarningRecord[sortedWarnings
.size()]);
}
@SuppressWarnings("unchecked")
protected void requestData(DataTime earliest) throws VizException {
System.out.println("requesting data");
Map<String, RequestConstraint> map = (Map<String, RequestConstraint>) resourceData
.getMetadataMap().clone();
if (earliestRequested != null) {
// don't request data we've already requested
String[] times = new String[] { earliest.toString(),
earliestRequested.toString() };
RequestConstraint constraint = new RequestConstraint();
constraint.setConstraintType(ConstraintType.BETWEEN);
constraint.setBetweenValueList(times);
map.put("endTime", constraint);
} else {
RequestConstraint endConstraint = new RequestConstraint(
earliest.toString(), ConstraintType.GREATER_THAN_EQUALS);
map.put("endTime", endConstraint);
}
earliestRequested = earliest;
LayerProperty property = new LayerProperty();
property.setDesiredProduct(ResourceType.PLAN_VIEW);
property.setEntryQueryParameters(map, false);
property.setNumberOfImages(9999);
Object[] resp = null;
resp = DataCubeContainer.getData(property, 60000).toArray(
new Object[] {});
PluginDataObject[] arr = new PluginDataObject[resp.length];
int i = 0;
for (Object o : resp) {
arr[i] = (PluginDataObject) o;
i++;
}
addRecord(sort(arr));
}
protected String[] getText(AbstractWarningRecord record, double mapWidth) {
String vid = record.getPhensig();
String phen = record.getPhen();
@ -147,4 +615,33 @@ public abstract class AbstractWWAResource extends
}
}
@Override
public String getName() {
String name = resourceData.name != null ? resourceData.name
: resourceName;
DataTime[] times = this.descriptor.getFramesInfo().getFrameTimes();
int timeIdx = this.descriptor.getFramesInfo().getFrameIndex();
// handle last frame differently, it should always be the latest time
boolean lastFrame = false;
if (timeIdx == times.length - 1) {
lastFrame = true;
}
DataTime time = null;
// get time to display
if (lastFrame) {
time = new DataTime(SimulatedTime.getSystemTime().getTime());
} else if (timeIdx > -1 && timeIdx < times.length) {
time = times[timeIdx];
}
// add time to legend
if (time != null) {
name += " " + time.getLegendString();
}
return name;
}
}

View file

@ -1,763 +0,0 @@
package com.raytheon.viz.warnings.rsc;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.swt.graphics.RGB;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord;
import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.common.time.TimeRange;
import com.raytheon.uf.viz.core.DrawableString;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment;
import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle;
import com.raytheon.uf.viz.core.IGraphicsTarget.TextStyle;
import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment;
import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo;
import com.raytheon.uf.viz.core.drawables.IFont;
import com.raytheon.uf.viz.core.drawables.IRenderableDisplay;
import com.raytheon.uf.viz.core.drawables.IShadedShape;
import com.raytheon.uf.viz.core.drawables.IWireframeShape;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IResourceDataChanged;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
/**
*
* TODO njensen: This was a refactored version of AbstractWWAResource. The
* WatchesResource was not refactored to work with it. Ideally this class and
* AbstractWWAResource should be combined so it supports both watches and
* warnings.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* May 3, 2011 jsanchez Initial creation
* Aug 5, 2011 njensen Refactored maps
* Aug 22, 2011 10631 njensen Major refactor
* 2012-04-16 DR 14866 D. Friedman Fix sampling error
* May 3, 2012 DR 14741 porricel Updated matchesFrame function
* to make SVS warning updates and
* original warning display properly
* in a given display frame
* Jun 04, 2012 DR14992 mgamazaychikov Reversed the textToPrint array to
* plot the strings in correct order
* Aug 09, 2012 DR 15166 D.Friedman Plot only the most recent record for
* an event.
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public abstract class AbstractWarningResource extends AbstractWWAResource
implements IResourceDataChanged {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(AbstractWarningResource.class);
private static GeometryFactory gf = new GeometryFactory();
private static PreparedGeometryFactory pgf = new PreparedGeometryFactory();
protected static class RepaintHeartbeat extends TimerTask {
private HashSet<AbstractVizResource<?, ?>> resourceSet = new HashSet<AbstractVizResource<?, ?>>();
private boolean cancelled = false;
public RepaintHeartbeat() {
}
/**
* copy resources from old task, just in case some were added after it
* should have been replaced (threads are fun)
**/
public void copyResourceSet(RepaintHeartbeat oldTask) {
// copy resources, in case one was added after a cancel
Set<AbstractVizResource<?, ?>> oldResourceSet = oldTask
.getResourceSet();
synchronized (oldResourceSet) {
for (AbstractVizResource<?, ?> rsc : oldResourceSet) {
this.addResource(rsc);
}
}
}
public Set<AbstractVizResource<?, ?>> getResourceSet() {
return resourceSet;
}
@Override
public void run() {
// get the unique displays from all the added resources
ArrayList<IRenderableDisplay> displaysToRefresh = new ArrayList<IRenderableDisplay>(
1);
synchronized (resourceSet) {
for (AbstractVizResource<?, ?> rsc : resourceSet) {
try {
IRenderableDisplay disp = rsc.getDescriptor()
.getRenderableDisplay();
if (!displaysToRefresh.contains(disp)) {
displaysToRefresh.add(disp);
}
} catch (Exception e) {
statusHandler
.handle(Priority.PROBLEM,
"Encountered error during Warnings Heartbeat, continuing with other Warnings ",
e);
}
}
}
// create an array with final modifier
final IRenderableDisplay[] refreshList = displaysToRefresh
.toArray(new IRenderableDisplay[displaysToRefresh.size()]);
// execute refersh in UI thread
VizApp.runAsync(new Runnable() {
@Override
public void run() {
for (IRenderableDisplay disp : refreshList) {
disp.refresh();
}
}
});
// cancel the task if there are no more resources
boolean cancel = false;
synchronized (resourceSet) {
if (resourceSet.size() < 1) {
cancel = true;
}
}
if (cancel) {
doCancel();
}
}
public void addResource(AbstractVizResource<?, ?> rsc) {
// if task has no resources then it needs to be started when the
// first is added
boolean start = false;
synchronized (resourceSet) {
// if this is the first resource added to an empty set start the
// timer
if (resourceSet.size() < 1) {
start = true;
}
resourceSet.add(rsc);
}
if (start) {
AbstractWarningResource.scheduleHeartBeat();
}
}
public void removeResource(AbstractVizResource<?, ?> rsc) {
synchronized (resourceSet) {
resourceSet.remove(rsc);
// cancel the task if there are no more resources
if (resourceSet.size() < 1) {
doCancel();
}
}
}
private void doCancel() {
synchronized (heartBeatChangeLock) {
if (cancelled == false) {
cancelled = true;
heartBeatTimer.cancel();
heartBeatTask = new RepaintHeartbeat();
heartBeatTimer = new Timer();
heartBeatTask.copyResourceSet(this);
}
}
}
}
/** lock when changing heartBeatTask **/
protected static final Object heartBeatChangeLock = new Object();
protected static RepaintHeartbeat heartBeatTask = null;
protected static Timer heartBeatTimer = null;
/** one hour ahead, entirely arbitrary/magic **/
private static final long LAST_FRAME_ADJ = (60 * 60 * 1000);
protected class WarningEntry {
protected AbstractWarningRecord record;
protected IWireframeShape wireframeShape;
protected IShadedShape shadedShape;
/**
* whether or not the warning has been altered, ie CON, CAN, EXP. a
* warning can only be altered once with the exception of a partial
* cancel.
**/
protected boolean altered = false;
protected Date timeAltered;
protected Date frameAltered;
/**
* was the alter a partial cancel? if it was then a matching CON should
* be processed and added
*/
protected boolean partialCancel = false;
/**
* set to true if paint needs to re-init the shape
*/
protected boolean project = false;
}
/** map of dataURI to a warning entry **/
protected Map<String, WarningEntry> entryMap;
protected IFont warningsFont;
protected RGB color;
protected DataTime earliestRequested;
protected final Object paintLock = new Object();
/**
* Constructor
*/
public AbstractWarningResource(WWAResourceData data, LoadProperties props) {
super(data, props);
resourceData.addChangeListener(this);
getCapability(OutlineCapability.class).setOutlineWidth(2);
color = getCapability((ColorableCapability.class)).getColor();
this.entryMap = new ConcurrentHashMap<String, WarningEntry>();
}
/**
* schedule the heart beat for the next minute
*/
protected static void scheduleHeartBeat() {
// get simulated time
Date currentTime = SimulatedTime.getSystemTime().getTime();
// get a calendar
Calendar now = Calendar.getInstance();
// set calendar time to simulated time
now.setTime(currentTime);
// add one to the minutes field
now.add(Calendar.MINUTE, 1);
// reset second and milisecond to 0
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
// schedule task to fire every minute
synchronized (heartBeatChangeLock) {
try {
if (heartBeatTimer == null) {
heartBeatTimer = new Timer();
}
// schedule on the minute every minute
heartBeatTimer.schedule(heartBeatTask, now.getTime(),
1 * 60 * 1000);
} catch (Exception e) {
try {
heartBeatTimer.cancel();
} catch (Exception e2) {
// ignore, we just want to make sure the timer is cancelled
} finally {
// create a new task if there was an error when scheduling
heartBeatTask = new RepaintHeartbeat();
heartBeatTimer = new Timer();
}
statusHandler.handle(Priority.SIGNIFICANT,
"Error scheduling warnings heart beat ", e);
}
}
}
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
synchronized (heartBeatChangeLock) {
if (heartBeatTask == null) {
heartBeatTask = new RepaintHeartbeat();
}
heartBeatTask.addResource(this);
}
synchronized (this) {
try {
addRecord(getWarningRecordArray());
} catch (VizException e) {
e.printStackTrace();
}
}
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.core.rsc.IVizResource#dispose()
*/
@Override
protected void disposeInternal() {
synchronized (heartBeatChangeLock) {
heartBeatTask.removeResource(this);
}
for (WarningEntry entry : entryMap.values()) {
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
}
if (entry.wireframeShape != null) {
entry.wireframeShape.dispose();
}
}
entryMap.clear();
if (warningsFont != null) {
warningsFont.dispose();
}
}
@Override
public String inspect(ReferencedCoordinate coord) throws VizException {
if (resourceData.hideSampling) {
return "";
}
// check if we are in the last frame
boolean lastFrame = false;
FramesInfo framesInfo = this.descriptor.getFramesInfo();
int frameIdx = framesInfo.getFrameIndex();
DataTime[] frameTimes = framesInfo.getFrameTimes();
if (frameIdx < 0 || frameIdx >= frameTimes.length)
return "NO DATA";
DataTime time = frameTimes[frameIdx];
TimeRange framePeriod = null;
if (frameIdx + 1 < frameTimes.length) {
framePeriod = new TimeRange(time.getRefTime(),
frameTimes[frameIdx + 1].getRefTime());
} else {
framePeriod = new TimeRange(time.getRefTime(), LAST_FRAME_ADJ);
lastFrame = true;
}
if (time != null) {
try {
Point point = gf.createPoint(coord.asLatLon());
for (String key : entryMap.keySet()) {
WarningEntry entry = entryMap.get(key);
AbstractWarningRecord record = entry.record;
if (matchesFrame(entry, time, framePeriod, lastFrame)) {
Geometry recordGeom = record.getGeometry();
for (int i = 0; i < recordGeom.getNumGeometries(); i++) {
PreparedGeometry prepGeom = pgf.create(recordGeom
.getGeometryN(i));
if (prepGeom.contains(point)) {
StringBuffer sb = new StringBuffer();
String[] textToPrint = getText(record, 0);
for (String text : textToPrint) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append(text);
}
return sb.toString();
}
}
}
}
} catch (Exception e) {
throw new VizException("Error inspecting Warning Resource", e);
}
}
return "NO DATA";
}
protected void disposeEntry(final WarningEntry entry) {
if (entry.wireframeShape != null || entry.shadedShape != null) {
VizApp.runAsync(new Runnable() {
@Override
public void run() {
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
}
if (entry.wireframeShape != null) {
entry.wireframeShape.dispose();
}
}
});
}
}
@Override
public void project(CoordinateReferenceSystem crs) throws VizException {
synchronized (paintLock) {
for (Map.Entry<String, WarningEntry> entry : entryMap.entrySet()) {
WarningEntry warning = entry.getValue();
// dispose and set to null just to be safe
if (warning.shadedShape != null) {
warning.shadedShape.dispose();
warning.shadedShape = null;
}
if (warning.wireframeShape != null) {
warning.wireframeShape.dispose();
warning.wireframeShape = null;
}
warning.project = true;
}
}
}
@Override
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.DATA_UPDATE) {
PluginDataObject[] pdo = (PluginDataObject[]) object;
synchronized (AbstractWarningResource.this) {
{
try {
addRecord(pdo);
} catch (VizException e) {
statusHandler.handle(Priority.SIGNIFICANT,
e.getLocalizedMessage(), e);
}
}
}
} else if (type == ChangeType.CAPABILITY) {
if (color != null
&& color.equals(getCapability((ColorableCapability.class))
.getColor()) == false) {
color = getCapability((ColorableCapability.class)).getColor();
// TODO this needs to be fixed to work with watches which are
// shaded
// for (String dataUri : entryMap.keySet()) {
// WarningEntry entry = entryMap.get(dataUri);
// TODO init a shape somewhere else
// if (entry.shadedShape != null) {
// entry.shadedShape.dispose();
// try {
// initShape(entry.record);
// } catch (VizException e) {
// statusHandler.handle(Priority.PROBLEM,
// e.getLocalizedMessage(), e);
// }
// }
// }
}
}
issueRefresh();
}
protected void paintInternal(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
FramesInfo info = paintProps.getFramesInfo();
DataTime[] frames = info.getFrameTimes();
int frameToRequestedCompare = frames[0].compareTo(earliestRequested);
if (frameToRequestedCompare < 0) {
// we haven't requested data this far back
this.requestData(frames[0]);
} else if (frameToRequestedCompare > 0) {
// the previous earliest frame was removed as updates came in, so
// the warnings need to be disposed and we need to update the
// earliestRequested so if they ever went back in time to that
// again, it would be re-requested
earliestRequested = frames[0];
if (paintProps.getDataTime() != null) {
cleanupData(paintProps.getDataTime(), frames);
}
}
int index = info.getFrameIndex();
if (!this.recordsToLoad.isEmpty()) {
this.updateDisplay(target);
}
DataTime thisFrameTime = null;
if (index > -1 && index < frames.length) {
thisFrameTime = frames[index];
}
if (thisFrameTime == null) {
return;
}
TimeRange framePeriod = null;
boolean lastFrame = false;
if (index + 1 < frames.length) {
framePeriod = new TimeRange(thisFrameTime.getRefTime(),
frames[index + 1].getRefTime());
} else {
framePeriod = new TimeRange(thisFrameTime.getRefTime(),
LAST_FRAME_ADJ);
lastFrame = true;
}
synchronized (paintLock) {
HashMap<String, WarningEntry> candidates =
new HashMap<String, AbstractWarningResource.WarningEntry>();
for (WarningEntry entry : entryMap.values()) {
if (matchesFrame(entry, paintProps.getDataTime(), framePeriod,
lastFrame)) {
String key = getEventKey(entry);
WarningEntry current = candidates.get(key);
if (current == null || current.record.getIssueTime().
before(entry.record.getIssueTime()) ||
(current.record.getIssueTime().equals(entry.record.getIssueTime()) &&
current.record.getInsertTime().before(entry.record.getInsertTime()))
)
candidates.put(key, entry);
}
}
for (WarningEntry entry : candidates.values()) {
AbstractWarningRecord record = entry.record;
// check shapes
if (entry.project) {
initShape(target, entry.record);
entry.project = false;
}
if (entry != null && entry.wireframeShape != null) {
LineStyle lineStyle = (record.getProductClass() != null && record
.getProductClass().equals("T")) ? LineStyle.DASHED
: LineStyle.SOLID;
target.drawWireframeShape(entry.wireframeShape,
getCapability(ColorableCapability.class)
.getColor(),
getCapability(OutlineCapability.class)
.getOutlineWidth(), lineStyle);
} else if (entry != null && entry.shadedShape != null) {
target.drawShadedShape(entry.shadedShape, 1);
}
if (record != null && record.getGeometry() != null) {
// Calculate the upper left portion of the polygon
Coordinate upperLeft = new Coordinate(180, -90);
for (Coordinate c : record.getGeometry()
.getCoordinates()) {
if (c.y - c.x > upperLeft.y - upperLeft.x) {
upperLeft = c;
}
}
double[] d = descriptor.worldToPixel(new double[] {
upperLeft.x, upperLeft.y });
d[0] -= paintProps.getZoomLevel() * 100;
double mapWidth = descriptor.getMapWidth()
* paintProps.getZoomLevel() / 1000;
String[] textToPrint = getText(record, mapWidth);
if (warningsFont == null) {
warningsFont = target.getDefaultFont()
.deriveWithSize(11);
}
// DR14992: reverse the textToPrint array to plot the strings in correct order
String [] textToPrintReversed = new String[textToPrint.length];
for(int i = 0; i < textToPrint.length; i++) {
textToPrintReversed[i] = textToPrint[textToPrint.length
- i - 1];
}
DrawableString params = new DrawableString(textToPrintReversed,
color);
params.font = warningsFont;
params.setCoordinates(d[0], d[1]);
params.textStyle = TextStyle.NORMAL;
params.horizontalAlignment = HorizontalAlignment.RIGHT;
params.verticallAlignment = VerticalAlignment.BOTTOM;
params.magnification = getCapability(
MagnificationCapability.class)
.getMagnification();
target.drawStrings(params);
}
}
}
}
private static String getEventKey(WarningEntry entry) {
AbstractWarningRecord r = entry.record;
return r.getOfficeid() + '.' + r.getPhensig() + '.' + r.getEtn();
}
protected boolean matchesFrame(WarningEntry entry, DataTime paintTime,
TimeRange framePeriod, boolean lastFrame) {
TimeRange recordPeriod = new TimeRange(entry.record.getStartTime()
.getTimeInMillis(), entry.record.getEndTime().getTimeInMillis());
long diff = entry.record.getEndTime().getTimeInMillis()
- entry.record.getStartTime().getTimeInMillis();
Date centerTime = new Date(entry.record.getStartTime()
.getTimeInMillis() + (diff / 2));
Date frameTime = framePeriod.getStart();
Date frameStart = framePeriod.getStart();
Date refTime = entry.record.getDataTime().getRefTime();
if (lastFrame) {
// use current system time to determine what to display
Date timeToDisplay = SimulatedTime.getSystemTime().getTime();
// change frame time
frameTime = timeToDisplay;
// point paint time to different time
paintTime = new DataTime(timeToDisplay);
// point framePeriod to new frame
framePeriod = new TimeRange(frameTime, LAST_FRAME_ADJ);
}
// check if the warning is cancelled
WarningAction action = WarningAction.valueOf(entry.record.getAct());
if (action == WarningAction.CAN && refTime.equals(paintTime)) {
return false;
// If this entry has been altered/updated, display its pre-altered
// version
// only in the frames prior to the time it was altered
} else if (entry.altered) {
if (frameStart.getTime() >= refTime.getTime()
&& frameStart.getTime() < (entry.timeAltered.getTime())
&& frameStart.getTime() < entry.frameAltered.getTime())
return true;
if (frameStart.getTime() >= (entry.timeAltered.getTime()))
return false;
} else if (refTime.equals(paintTime)
|| recordPeriod.contains(frameTime)
|| (framePeriod.contains(centerTime) && (!lastFrame || !entry.altered))) {
return true;
}
return false;
}
protected void cleanupData(DataTime paintTime, DataTime[] descFrameTimes) {
System.out.println("entryMap size " + entryMap.size());
List<TimeRange> framePeriods = new ArrayList<TimeRange>(
descFrameTimes.length);
for (int i = 0; i < descFrameTimes.length; i++) {
if (i == descFrameTimes.length - 1) {
framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(),
LAST_FRAME_ADJ));
} else {
framePeriods.add(new TimeRange(descFrameTimes[i].getRefTime(),
descFrameTimes[i + 1].getRefTime()));
}
}
int size = framePeriods.size();
List<String> toRemove = new ArrayList<String>();
for (String key : entryMap.keySet()) {
WarningEntry entry = entryMap.get(key);
boolean found = false;
for (int i = 0; i < size; i++) {
TimeRange tr = framePeriods.get(i);
if (matchesFrame(entry, paintTime, tr, (i == size - 1))) {
found = true;
break;
}
}
if (!found) {
toRemove.add(key);
}
}
for (String key : toRemove) {
WarningEntry entry = entryMap.remove(key);
System.out.println("removing " + entry.record.getDataURI());
disposeEntry(entry);
}
}
protected abstract void updateDisplay(IGraphicsTarget target);
protected abstract void requestData(DataTime earliest) throws VizException;
protected abstract void initShape(IGraphicsTarget target,
AbstractWarningRecord record);
public synchronized void addRecord(PluginDataObject[] pdos)
throws VizException {
for (PluginDataObject pdo : pdos) {
if (pdo instanceof AbstractWarningRecord) {
AbstractWarningRecord record = (AbstractWarningRecord) pdo;
String officeid = record.getOfficeid();
if (!resourceData.getMetadataMap().containsKey("officeid")
|| resourceData.getMetadataMap().get("officeid")
.getConstraintValue().contains(officeid)) {
this.recordsToLoad.add((AbstractWarningRecord) pdo);
}
}
}
}
@Override
public String getName() {
String name = resourceData.name != null ? resourceData.name
: "Warnings";
DataTime[] times = this.descriptor.getFramesInfo().getFrameTimes();
int timeIdx = this.descriptor.getFramesInfo().getFrameIndex();
// handle last frame differently, it should always be the latest time
boolean lastFrame = false;
if (timeIdx == times.length - 1) {
lastFrame = true;
}
DataTime time = null;
// get time to display
if (lastFrame) {
time = new DataTime(SimulatedTime.getSystemTime().getTime());
} else if (timeIdx > -1 && timeIdx < times.length) {
time = times[timeIdx];
}
// add time to legend
if (time != null) {
name += " " + time.getLegendString();
}
return name;
}
}

View file

@ -1,539 +0,0 @@
package com.raytheon.viz.warnings.rsc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.swt.graphics.RGB;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.DrawableString;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment;
import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle;
import com.raytheon.uf.viz.core.IGraphicsTarget.TextStyle;
import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment;
import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo;
import com.raytheon.uf.viz.core.drawables.IFont;
import com.raytheon.uf.viz.core.drawables.IShadedShape;
import com.raytheon.uf.viz.core.drawables.IWireframeShape;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.IResourceDataChanged;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
/**
*
* TODO njensen: Ideally this should be refactored to be more in line with the
* AbstractWarningsResource.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* May 3, 2011 jsanchez Initial creation
* Aug 5, 2011 njensen Refactored maps
* Aug 22, 2011 10631 njensen Major refactor
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public abstract class AbstractWatchesResource extends AbstractWWAResource
implements IResourceDataChanged {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(AbstractWatchesResource.class);
private static PreparedGeometryFactory pgf = new PreparedGeometryFactory();
private static GeometryFactory gf = new GeometryFactory();
/**
*
* this task calls redoTimeMatching on the resource, it should be scheduled
* to run for when a warning is set to expire
*
* @author ekladstrup
* @version 1.0
*/
protected class WarningExpirationTask extends TimerTask {
private AbstractWatchesResource rsc = null;
public WarningExpirationTask(AbstractWatchesResource rsc) {
this.rsc = rsc;
}
@Override
public void run() {
// System.err.println("warning expired");
// some warning has expired
rsc.redoTimeMatching(this.scheduledExecutionTime());
rsc.issueRefresh();
}
}
protected class WarningEntry {
protected AbstractWarningRecord record;
protected IWireframeShape wireframeShape;
protected IShadedShape shadedShape;
protected List<DataTime> times;
}
protected IGraphicsTarget target;
/** map of dataURI to a warning entry **/
protected Map<String, WarningEntry> entryMap;
protected Map<DataTime, List<AbstractWarningRecord>> frames;
protected DataTime displayedDate;
protected IFont warningsFont;
protected RGB color;
private Timer timer;
private Set<Long> expTaskSet;
/**
* Constructor
*/
public AbstractWatchesResource(WWAResourceData data, LoadProperties props) {
super(data, props);
resourceData.addChangeListener(this);
getCapability(OutlineCapability.class).setOutlineWidth(2);
color = getCapability((ColorableCapability.class)).getColor();
this.entryMap = new HashMap<String, WarningEntry>();
this.frames = Collections
.synchronizedMap(new HashMap<DataTime, List<AbstractWarningRecord>>());
timer = new Timer();
expTaskSet = new HashSet<Long>();
}
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
if (this.target == null) {
this.target = target;
synchronized (this) {
try {
addRecord(getWarningRecordArray());
} catch (VizException e) {
e.printStackTrace();
}
}
}
// force creation of a frame for any currently active warnings, this
// frame might get displayed in place of the last frame.
initNewFrame(new DataTime(SimulatedTime.getSystemTime().getTime()));
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.core.rsc.IVizResource#dispose()
*/
@Override
protected void disposeInternal() {
timer.cancel();
for (WarningEntry entry : entryMap.values()) {
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
}
if (entry.wireframeShape != null) {
entry.wireframeShape.dispose();
}
if (entry.times != null) {
entry.times.clear();
}
}
entryMap.clear();
frames.clear();
if (warningsFont != null) {
warningsFont.dispose();
}
}
@Override
public String inspect(ReferencedCoordinate coord) throws VizException {
if (resourceData.hideSampling) {
return "";
}
if (this.displayedDate != null
&& this.frames.containsKey(displayedDate)) {
try {
Point point = gf.createPoint(coord.asLatLon());
synchronized (frames) {
for (AbstractWarningRecord record : this.frames
.get(displayedDate)) {
Date entryIssue = record.getStartTime().getTime();
if (entryIssue == null) {
entryIssue = record.getIssueTime().getTime();
}
Date entryEnd = record.getEndTime().getTime();
Date ref = displayedDate.getRefTime();
if (entryIssue.compareTo(ref) <= 0
&& entryEnd.compareTo(ref) >= 0) {
Geometry recordGeom = record.getGeometry();
for (int i = 0; i < recordGeom.getNumGeometries(); i++) {
PreparedGeometry prepGeom = pgf
.create(recordGeom.getGeometryN(i));
if (prepGeom.contains(point)) {
StringBuffer sb = new StringBuffer();
String[] textToPrint = getText(record, 0);
for (String text : textToPrint) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append(text);
}
return sb.toString();
}
}
}
}
}
} catch (Exception e) {
throw new VizException("Error inspecting Warning Resource", e);
}
}
return "NO DATA";
}
protected void removeFrame(DataTime dataTime) {
synchronized (frames) {
DataTime[] frameTimes = descriptor.getFramesInfo().getFrameTimes();
DataTime lastTime = frameTimes[frameTimes.length - 1];
if (lastTime != null
&& lastTime.getMatchValid() < dataTime.getMatchValid()) {
// Don't remove warnings that are newer than the last frame.
// This allows us to display the latest warnings even if they
// don't time match. These warnings will be cleaned up again on
// the next redoTimeMatching. Hopefully we aren't keeping too
// many around.
return;
}
List<AbstractWarningRecord> warningRecordList = frames
.remove(dataTime);
// remove times can effect stepping through frames
for (AbstractWarningRecord w : warningRecordList) {
final WarningEntry entry = entryMap.get(w.getDataURI());
if (entry != null) {
List<DataTime> list = entry.times;
if (list != null && list.remove(dataTime)) {
if (list.isEmpty()) {
disposeEntry(entry);
}
}
if (list == null || list.isEmpty()) {
entryMap.remove(w.getDataURI());
}
}
}
}
}
protected void disposeEntry(final WarningEntry entry) {
if (entry.wireframeShape != null || entry.shadedShape != null) {
VizApp.runAsync(new Runnable() {
@Override
public void run() {
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
}
if (entry.wireframeShape != null) {
entry.wireframeShape.dispose();
}
}
});
}
}
@Override
public void project(CoordinateReferenceSystem crs) throws VizException {
this.frames.clear();
}
@Override
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.DATA_UPDATE) {
PluginDataObject[] pdo = (PluginDataObject[]) object;
synchronized (AbstractWatchesResource.this) {
{
try {
addRecord(pdo);
} catch (VizException e) {
statusHandler.handle(Priority.SIGNIFICANT,
e.getLocalizedMessage(), e);
}
}
}
} else if (type == ChangeType.CAPABILITY) {
if (color != null
&& color.equals(getCapability((ColorableCapability.class))
.getColor()) == false) {
color = getCapability((ColorableCapability.class)).getColor();
for (String dataUri : entryMap.keySet()) {
WarningEntry entry = entryMap.get(dataUri);
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
try {
initShape(entry.record);
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
}
}
}
}
issueRefresh();
}
protected void paintInternal(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
FramesInfo info = paintProps.getFramesInfo();
DataTime[] frames = info.getFrameTimes();
cleanupData(frames);
int index = info.getFrameIndex();
if (index > -1 && info.getFrameCount() > 0
&& index < info.getFrameCount()) {
displayedDate = frames[index];
if (index == info.getFrameCount() - 1) {
// When we are on the last frame, if there exists a frame with
// data that is beyond the last frame but within a reasonable
// amount of time(1 hour) then we will display that frame
// instead of the last frame. We do this so when a new warning
// is issued it will display immediately over radar or satellite
// data, even if the latest frame is a few minutes behind the
// current time.
long displayValid = displayedDate.getMatchValid();
long latestValid = displayValid;
synchronized (frames) {
for (Entry<DataTime, List<AbstractWarningRecord>> entry : this.frames
.entrySet()) {
if (entry.getValue().isEmpty()) {
// do not override the frame time for a time with no
// records.
continue;
}
long valid = entry.getKey().getMatchValid();
// 3600000 == 1 hour == a magicly random amount of time
if (valid > latestValid
&& latestValid - displayValid < 3600000) {
displayedDate = entry.getKey();
latestValid = valid;
}
}
}
}
} else {
displayedDate = paintProps.getDataTime();
}
if (displayedDate == null) {
return;
}
if (!this.recordsToLoad.isEmpty()) {
this.updateFrames();
}
DataTime thisFrameTime = displayedDate;
if (!this.frames.containsKey(thisFrameTime)) {
this.initNewFrame(thisFrameTime);
}
List<AbstractWarningRecord> records = this.frames.get(thisFrameTime);
for (AbstractWarningRecord record : records) {
WarningEntry entry = entryMap.get(record.getDataURI());
if (entry != null && entry.wireframeShape != null) {
LineStyle lineStyle = (record.getProductClass() != null && record
.getProductClass().equals("T")) ? LineStyle.DASHED
: LineStyle.SOLID;
target.drawWireframeShape(entry.wireframeShape,
getCapability(ColorableCapability.class).getColor(),
getCapability(OutlineCapability.class)
.getOutlineWidth(), lineStyle);
} else if (entry != null && entry.shadedShape != null) {
target.drawShadedShape(entry.shadedShape, 1);
}
if (record != null && record.getGeometry() != null) {
// Calculate the upper left portion of the polygon
Coordinate upperLeft = new Coordinate(180, -90);
for (Coordinate c : record.getGeometry().getCoordinates()) {
if (c.y - c.x > upperLeft.y - upperLeft.x) {
upperLeft = c;
}
}
double[] d = descriptor.worldToPixel(new double[] {
upperLeft.x, upperLeft.y });
d[0] -= paintProps.getZoomLevel() * 100;
double mapWidth = descriptor.getMapWidth()
* paintProps.getZoomLevel() / 1000;
String[] textToPrint = getText(record, mapWidth);
if (warningsFont == null) {
warningsFont = target.getDefaultFont().deriveWithSize(11);
}
DrawableString params = new DrawableString(textToPrint, color);
params.font = warningsFont;
params.setCoordinates(d[0], d[1]);
params.textStyle = TextStyle.NORMAL;
params.horizontalAlignment = HorizontalAlignment.RIGHT;
params.verticallAlignment = VerticalAlignment.BOTTOM;
params.magnification = getCapability(
MagnificationCapability.class).getMagnification();
target.drawStrings(params);
}
}
}
protected void cleanupData(DataTime[] descFrameTimes) {
List<DataTime> oldFrames = null;
synchronized (frames) {
for (DataTime dt : frames.keySet()) {
boolean found = false;
for (DataTime descTime : descFrameTimes) {
if (descTime.equals(dt)) {
found = true;
break;
}
}
if (!found) {
if (oldFrames == null) {
oldFrames = new ArrayList<DataTime>();
}
oldFrames.add(dt);
}
}
}
if (oldFrames != null) {
for (DataTime old : oldFrames) {
this.removeFrame(old);
}
}
}
protected synchronized void initNewFrame(DataTime thisFrameTime)
throws VizException {
// subclasses should override
}
protected synchronized void updateFrames() throws VizException {
// subclasses should override
}
protected void initShape(AbstractWarningRecord record) throws VizException {
// subclasses should override
}
@Override
public String getName() {
return null;
}
/**
* Schedules a WarningExpirationTask for the end time of the passing in
* record
*
* @param rec
* a WarningRecord
*/
protected void scheduleTimer(AbstractWarningRecord rec) {
// only schedule if record has not expired already
long now = SimulatedTime.getSystemTime().getTime().getTime();
long endTime = rec.getEndTime().getTimeInMillis();
synchronized (expTaskSet) {
if (endTime > now && !expTaskSet.contains(new Long(endTime))) {
WarningExpirationTask task = new WarningExpirationTask(this);
timer.schedule(task, rec.getEndTime().getTime());
expTaskSet.add(new Long(endTime));
}
}
}
/**
* Redo time matching and remove the passed in time from the map of
* scheduled times
*
* @param triggerTime
*/
public void redoTimeMatching(long triggerTime) {
redoTimeMatching();
Long time = new Long(triggerTime);
// remove the instance of the trigger time from the map
synchronized (expTaskSet) {
if (expTaskSet != null && expTaskSet.contains(time)) {
expTaskSet.remove(time);
}
}
}
/**
* Redo the time matching
*/
public void redoTimeMatching() {
try {
this.getDescriptor().getTimeMatcher().redoTimeMatching(this);
this.getDescriptor().getTimeMatcher()
.redoTimeMatching(this.getDescriptor());
} catch (VizException e) {
// TODO Auto-generated catch block. Please revise as appropriate.
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
}
}
}

View file

@ -27,6 +27,7 @@ import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKBReader;
import com.vividsolutions.jts.io.WKTReader;
public class CWASPSResource extends WarningsResource {
private static final transient IUFStatusHandler statusHandler = UFStatus
@ -230,14 +231,14 @@ public class CWASPSResource extends WarningsResource {
textToPrint[2] = record.getPil();
SimpleDateFormat startFormat = AbstractWarningResource.DEFAULT_FORMAT;
SimpleDateFormat endFormat = AbstractWarningResource.DEFAULT_FORMAT;
SimpleDateFormat startFormat = AbstractWWAResource.DEFAULT_FORMAT;
SimpleDateFormat endFormat = AbstractWWAResource.DEFAULT_FORMAT;
if (mapWidth == 0) {
startFormat = AbstractWarningResource.LONG_FORMAT;
endFormat = AbstractWarningResource.DAY_FORMAT;
startFormat = AbstractWWAResource.LONG_FORMAT;
endFormat = AbstractWWAResource.DAY_FORMAT;
} else if (mapWidth <= 200) {
startFormat = AbstractWarningResource.DAY_FORMAT;
endFormat = AbstractWarningResource.DAY_FORMAT;
startFormat = AbstractWWAResource.DAY_FORMAT;
endFormat = AbstractWWAResource.DAY_FORMAT;
}
synchronized (startFormat) {

View file

@ -17,7 +17,7 @@ import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* May 12, 2011 jsanchez Initial creation
*
* Sep 27, 2012 1149 jsanchez Updated order of actions. Put CON before CAN,EXP
* </pre>
*
* @author jsanchez
@ -30,6 +30,7 @@ public class WarningRecordComparator implements
* Compares the WarningRecords by phenSig, ETN, action, then starttime
* (descending order)
*/
@Override
public int compare(AbstractWarningRecord wr1, AbstractWarningRecord wr2) {
int rval = 0;
@ -39,19 +40,30 @@ public class WarningRecordComparator implements
rval = Double.compare(Double.parseDouble(wr1.getEtn()),
Double.parseDouble(wr2.getEtn()));
if (rval == 0) {
if (wr1.getAct().equals(wr2.getAct())) {
WarningAction act1 = WarningAction.valueOf(wr1.getAct());
WarningAction act2 = WarningAction.valueOf(wr2.getAct());
if (act1 == act2) {
rval = 0;
} else if (wr1.getAct().equals(WarningAction.NEW.toString())) {
} else if (act1 == WarningAction.NEW) {
rval = -1;
} else if (wr2.getAct().equals(WarningAction.NEW.toString())) {
} else if (act2 == WarningAction.NEW) {
rval = 1;
} else if (act1 == WarningAction.CON
&& (act2 == WarningAction.CAN || act2 == WarningAction.EXP)) {
return -1;
} else if (act2 == WarningAction.CON
&& (act1 == WarningAction.CAN || act1 == WarningAction.EXP)) {
return 1;
} else {
rval = wr1.getAct().compareTo(wr2.getAct());
}
if (rval == 0) {
rval = -1
* wr1.getStartTime().compareTo(wr2.getStartTime());
rval = wr1.getStartTime().compareTo(wr2.getStartTime());
// sort warnings in descending order
if (!wr1.getSig().equals("A")) {
rval *= -1;
}
}
}
}

View file

@ -22,26 +22,27 @@ package com.raytheon.viz.warnings.rsc;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Map;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord;
import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.catalog.LayerProperty;
import com.raytheon.uf.viz.core.datastructure.DataCubeContainer;
import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo;
import com.raytheon.uf.viz.core.drawables.IRenderableDisplay;
import com.raytheon.uf.viz.core.drawables.IWireframeShape;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceType;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.viz.core.rsc.jts.JTSCompiler;
import com.raytheon.viz.texteditor.util.SiteAbbreviationUtil;
import com.vividsolutions.jts.geom.Geometry;
@ -55,31 +56,268 @@ import com.vividsolutions.jts.geom.Geometry;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 1, 2010 jsanchez Initial creation
* Aug 22, 2011 10631 njensen Major refactor
* Aug 22, 2011 10631 njensen Major refactor
* May 3, 2012 DR 14741 porricel Stop setting end time of orig.
* warning to start time of update.
* Jun 04, 2012 DR14992 mgamazaychikov Fix the problem with plotting expiration time for
* NEW warning when CAN warning is issued
*
* Sep 27, 2012 1149 jsanchez Refactored methods from AbstractWarningsResource into this class.
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class WarningsResource extends AbstractWarningResource {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(WarningsResource.class);
public class WarningsResource extends AbstractWWAResource {
protected static class RepaintHeartbeat extends TimerTask {
private final HashSet<AbstractVizResource<?, ?>> resourceSet = new HashSet<AbstractVizResource<?, ?>>();
private boolean cancelled = false;
public RepaintHeartbeat() {
}
/**
* copy resources from old task, just in case some were added after it
* should have been replaced (threads are fun)
**/
public void copyResourceSet(RepaintHeartbeat oldTask) {
// copy resources, in case one was added after a cancel
Set<AbstractVizResource<?, ?>> oldResourceSet = oldTask
.getResourceSet();
synchronized (oldResourceSet) {
for (AbstractVizResource<?, ?> rsc : oldResourceSet) {
this.addResource(rsc);
}
}
}
public Set<AbstractVizResource<?, ?>> getResourceSet() {
return resourceSet;
}
@Override
public void run() {
// get the unique displays from all the added resources
ArrayList<IRenderableDisplay> displaysToRefresh = new ArrayList<IRenderableDisplay>(
1);
synchronized (resourceSet) {
for (AbstractVizResource<?, ?> rsc : resourceSet) {
try {
IRenderableDisplay disp = rsc.getDescriptor()
.getRenderableDisplay();
if (!displaysToRefresh.contains(disp)) {
displaysToRefresh.add(disp);
}
} catch (Exception e) {
statusHandler
.handle(Priority.PROBLEM,
"Encountered error during Warnings Heartbeat, continuing with other Warnings ",
e);
}
}
}
// create an array with final modifier
final IRenderableDisplay[] refreshList = displaysToRefresh
.toArray(new IRenderableDisplay[displaysToRefresh.size()]);
// execute refersh in UI thread
VizApp.runAsync(new Runnable() {
@Override
public void run() {
for (IRenderableDisplay disp : refreshList) {
disp.refresh();
}
}
});
// cancel the task if there are no more resources
boolean cancel = false;
synchronized (resourceSet) {
if (resourceSet.size() < 1) {
cancel = true;
}
}
if (cancel) {
doCancel();
}
}
public void addResource(AbstractVizResource<?, ?> rsc) {
// if task has no resources then it needs to be started when the
// first is added
boolean start = false;
synchronized (resourceSet) {
// if this is the first resource added to an empty set start the
// timer
if (resourceSet.size() < 1) {
start = true;
}
resourceSet.add(rsc);
}
if (start) {
WarningsResource.scheduleHeartBeat();
}
}
public void removeResource(AbstractVizResource<?, ?> rsc) {
synchronized (resourceSet) {
resourceSet.remove(rsc);
// cancel the task if there are no more resources
if (resourceSet.size() < 1) {
doCancel();
}
}
}
private void doCancel() {
synchronized (heartBeatChangeLock) {
if (cancelled == false) {
cancelled = true;
heartBeatTimer.cancel();
heartBeatTask = new RepaintHeartbeat();
heartBeatTimer = new Timer();
heartBeatTask.copyResourceSet(this);
}
}
}
}
/** lock when changing heartBeatTask **/
protected static final Object heartBeatChangeLock = new Object();
protected static RepaintHeartbeat heartBeatTask = null;
protected static Timer heartBeatTimer = null;
/**
* Constructor
*/
public WarningsResource(WWAResourceData data, LoadProperties props) {
super(data, props);
resourceName = "Warnings";
}
@Override
protected synchronized void updateDisplay(IGraphicsTarget target) {
protected void initInternal(IGraphicsTarget target) throws VizException {
DataTime earliest = this.descriptor.getFramesInfo().getFrameTimes()[0];
requestData(earliest);
synchronized (heartBeatChangeLock) {
if (heartBeatTask == null) {
heartBeatTask = new RepaintHeartbeat();
}
heartBeatTask.addResource(this);
}
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.core.rsc.IVizResource#dispose()
*/
@Override
protected void disposeInternal() {
synchronized (heartBeatChangeLock) {
heartBeatTask.removeResource(this);
}
for (WarningEntry entry : entryMap.values()) {
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
}
if (entry.wireframeShape != null) {
entry.wireframeShape.dispose();
}
}
entryMap.clear();
if (warningsFont != null) {
warningsFont.dispose();
}
}
@Override
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.DATA_UPDATE) {
PluginDataObject[] pdo = (PluginDataObject[]) object;
synchronized (WarningsResource.this) {
{
try {
addRecord(pdo);
} catch (VizException e) {
statusHandler.handle(Priority.SIGNIFICANT,
e.getLocalizedMessage(), e);
}
}
}
} else if (type == ChangeType.CAPABILITY) {
if (color != null
&& color.equals(getCapability((ColorableCapability.class))
.getColor()) == false) {
color = getCapability((ColorableCapability.class)).getColor();
// TODO this needs to be fixed to work with watches which are
// shaded
// for (String dataUri : entryMap.keySet()) {
// WarningEntry entry = entryMap.get(dataUri);
// TODO init a shape somewhere else
// if (entry.shadedShape != null) {
// entry.shadedShape.dispose();
// try {
// initShape(entry.record);
// } catch (VizException e) {
// statusHandler.handle(Priority.PROBLEM,
// e.getLocalizedMessage(), e);
// }
// }
// }
}
}
issueRefresh();
}
@Override
protected void initShape(IGraphicsTarget target,
AbstractWarningRecord record) throws VizException {
Geometry geo;
if (record.getGeometry() != null) {
try {
WarningEntry entry = entryMap.get(record.getDataURI());
if (entry == null) {
entry = new WarningEntry();
entry.record = record;
entryMap.put(record.getDataURI(), entry);
}
IWireframeShape wfs = entry.wireframeShape;
if (wfs != null) {
wfs.dispose();
}
wfs = target.createWireframeShape(false, descriptor);
geo = (Geometry) record.getGeometry().clone();
JTSCompiler jtsCompiler = new JTSCompiler(null, wfs, descriptor);
jtsCompiler.handle(geo);
wfs.compile();
entry.wireframeShape = wfs;
} catch (Exception e) {
statusHandler.handle(Priority.ERROR,
"Error creating wireframe", e);
}
}
}
@Override
protected synchronized void updateDisplay(IGraphicsTarget target)
throws VizException {
if (!this.recordsToLoad.isEmpty()) {
FramesInfo info = getDescriptor().getFramesInfo();
DataTime[] frames = info.getFrameTimes();
@ -121,7 +359,7 @@ public class WarningsResource extends AbstractWarningResource {
entry.record.setEndTime((Calendar) warnrec
.getStartTime().clone());
}
if (!rec.getCountyheader().equals(
warnrec.getCountyheader())
&& act == WarningAction.CAN) {
@ -166,103 +404,50 @@ public class WarningsResource extends AbstractWarningResource {
}
@Override
protected void initShape(IGraphicsTarget target,
AbstractWarningRecord record) {
Geometry geo;
if (record.getGeometry() != null) {
try {
WarningEntry entry = entryMap.get(record.getDataURI());
if (entry == null) {
entry = new WarningEntry();
entry.record = record;
entryMap.put(record.getDataURI(), entry);
}
IWireframeShape wfs = entry.wireframeShape;
if (wfs != null) {
wfs.dispose();
}
wfs = target.createWireframeShape(false, descriptor);
geo = (Geometry) record.getGeometry().clone();
JTSCompiler jtsCompiler = new JTSCompiler(null, wfs, descriptor);
jtsCompiler.handle(geo);
wfs.compile();
entry.wireframeShape = wfs;
} catch (Exception e) {
statusHandler.handle(Priority.ERROR,
"Error creating wireframe", e);
}
}
}
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
DataTime earliest = this.descriptor.getFramesInfo().getFrameTimes()[0];
requestData(earliest);
/**
* schedule the heart beat for the next minute
*/
protected static void scheduleHeartBeat() {
// get simulated time
Date currentTime = SimulatedTime.getSystemTime().getTime();
// get a calendar
Calendar now = Calendar.getInstance();
// set calendar time to simulated time
now.setTime(currentTime);
// add one to the minutes field
now.add(Calendar.MINUTE, 1);
// reset second and milisecond to 0
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
// schedule task to fire every minute
synchronized (heartBeatChangeLock) {
if (heartBeatTask == null) {
heartBeatTask = new RepaintHeartbeat();
try {
if (heartBeatTimer == null) {
heartBeatTimer = new Timer();
}
// schedule on the minute every minute
heartBeatTimer.schedule(heartBeatTask, now.getTime(),
1 * 60 * 1000);
} catch (Exception e) {
try {
heartBeatTimer.cancel();
} catch (Exception e2) {
// ignore, we just want to make sure the timer is cancelled
} finally {
// create a new task if there was an error when scheduling
heartBeatTask = new RepaintHeartbeat();
heartBeatTimer = new Timer();
}
statusHandler.handle(Priority.SIGNIFICANT,
"Error scheduling warnings heart beat ", e);
}
heartBeatTask.addResource(this);
}
}
@SuppressWarnings("unchecked")
@Override
protected void requestData(DataTime earliest) throws VizException {
System.out.println("requesting data");
Map<String, RequestConstraint> map = (Map<String, RequestConstraint>) resourceData
.getMetadataMap().clone();
if (earliestRequested != null) {
// don't request data we've already requested
String[] times = new String[] { earliest.toString(),
earliestRequested.toString() };
RequestConstraint constraint = new RequestConstraint();
constraint.setConstraintType(ConstraintType.BETWEEN);
constraint.setBetweenValueList(times);
map.put("endTime", constraint);
} else {
RequestConstraint endConstraint = new RequestConstraint(
earliest.toString(), ConstraintType.GREATER_THAN_EQUALS);
map.put("endTime", endConstraint);
}
earliestRequested = earliest;
LayerProperty property = new LayerProperty();
property.setDesiredProduct(ResourceType.PLAN_VIEW);
property.setEntryQueryParameters(map, false);
property.setNumberOfImages(9999);
Object[] resp = null;
resp = DataCubeContainer.getData(property, 60000).toArray(
new Object[] {});
PluginDataObject[] arr = new PluginDataObject[resp.length];
int i = 0;
for (Object o : resp) {
arr[i] = (PluginDataObject) o;
i++;
}
addRecord(sort(arr));
}
private PluginDataObject[] sort(PluginDataObject[] pdos) {
ArrayList<AbstractWarningRecord> sortedWarnings = new ArrayList<AbstractWarningRecord>();
for (Object o : pdos) {
if (((PluginDataObject) o) instanceof AbstractWarningRecord) {
AbstractWarningRecord record = (AbstractWarningRecord) o;
sortedWarnings.add(record);
}
}
/* Sorts by phensig, etn, starttime (descending), act */
Collections.sort(sortedWarnings, comparator);
return sortedWarnings.toArray(new AbstractWarningRecord[sortedWarnings
.size()]);
protected String getEventKey(WarningEntry entry) {
AbstractWarningRecord r = entry.record;
return r.getOfficeid() + '.' + r.getPhensig() + '.' + r.getEtn();
}
}

View file

@ -1,12 +1,17 @@
package com.raytheon.viz.warnings.rsc;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord;
@ -17,250 +22,172 @@ import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintTyp
import com.raytheon.uf.common.geospatial.ISpatialQuery.SearchMode;
import com.raytheon.uf.common.geospatial.SpatialQueryFactory;
import com.raytheon.uf.common.geospatial.SpatialQueryResult;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.TimeRange;
import com.raytheon.uf.viz.core.catalog.LayerProperty;
import com.raytheon.uf.viz.core.datastructure.DataCubeContainer;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.drawables.FillPatterns;
import com.raytheon.uf.viz.core.drawables.IShadedShape;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceType;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.viz.core.rsc.jts.JTSCompiler;
import com.raytheon.viz.core.rsc.jts.JTSCompiler.PointStyle;
import com.raytheon.viz.texteditor.util.SiteAbbreviationUtil;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
public class WatchesResource extends AbstractWatchesResource {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(WatchesResource.class);
/**
*
* TODO Add Description
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 27, 2012 1149 jsanchez Refactored methods from AbstractWarningsResource into this class.
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class WatchesResource extends AbstractWWAResource {
private Map<String, WeakReference<Geometry>> geometryMap = new HashMap<String, WeakReference<Geometry>>();
/**
*
* this task calls redoTimeMatching on the resource, it should be scheduled
* to run for when a warning is set to expire
*
* @author ekladstrup
* @version 1.0
*/
protected class WarningExpirationTask extends TimerTask {
private WatchesResource rsc = null;
public WarningExpirationTask(WatchesResource rsc) {
this.rsc = rsc;
}
@Override
public void run() {
// System.err.println("warning expired");
// some warning has expired
rsc.redoTimeMatching(this.scheduledExecutionTime());
rsc.issueRefresh();
}
}
private final Map<String, WeakReference<Geometry>> geometryMap = new HashMap<String, WeakReference<Geometry>>();
private final Timer timer;
private final Set<Long> expTaskSet;
protected IGraphicsTarget target;
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
public WatchesResource(WWAResourceData data, LoadProperties props) {
super(data, props);
timer = new Timer();
expTaskSet = new HashSet<Long>();
resourceName = "Watches";
}
@Override
public String getName() {
DataTime time = displayedDate;
if (time == null) {
time = descriptor.getTimeForResource(this);
}
String name = resourceData.name != null ? resourceData.name : "Watches";
if (time != null) {
name += " " + time.getLegendString();
}
return name;
}
protected void initInternal(IGraphicsTarget target) throws VizException {
if (this.target == null) {
this.target = target;
@Override
protected synchronized void initNewFrame(DataTime thisFrameTime)
throws VizException {
long t0 = System.currentTimeMillis();
HashMap<String, RequestConstraint> map = (HashMap<String, RequestConstraint>) resourceData
.getMetadataMap().clone();
LayerProperty property = new LayerProperty();
property.setDesiredProduct(ResourceType.PLAN_VIEW);
property.setNumberOfImages(9999);
/*
* Retrieve all the watches that have a have a range containing the
* frame time
*/
map.put("startTime", new RequestConstraint(thisFrameTime.toString(),
ConstraintType.LESS_THAN_EQUALS));
map.put("endTime", new RequestConstraint(thisFrameTime.toString(),
ConstraintType.GREATER_THAN_EQUALS));
property.setEntryQueryParameters(map, false);
Object[] resp = DataCubeContainer.getData(property, 60000).toArray(
new Object[] {});
ArrayList<AbstractWarningRecord> sortedWatches = new ArrayList<AbstractWarningRecord>();
for (Object o : resp) {
if (((PluginDataObject) o) instanceof AbstractWarningRecord) {
sortedWatches.add((AbstractWarningRecord) o);
}
}
/* Sorts by phensig, etn, starttime (descending), act */
Collections.sort(sortedWatches, comparator);
ArrayList<AbstractWarningRecord> remove = new ArrayList<AbstractWarningRecord>();
AbstractWarningRecord conRecord = null;
AbstractWarningRecord expRecord = null;
for (AbstractWarningRecord w : sortedWatches) {
WarningAction act = WarningAction.valueOf(w.getAct());
// Do not plot watches that have been Cancelled or Expired
if ((act == WarningAction.CAN || act == WarningAction.EXP)
&& w.getStartTime().after(thisFrameTime.getValidTime()) == false
&& w.getUgcsString() != null
&& w.getUgcsString()[0] != null
&& w.getUgcsString()[0].endsWith("000")) {
expRecord = w;
} else if (expRecord != null
&& expRecord.getEtn().equals(w.getEtn())
&& expRecord.getPhensig().equals(w.getPhensig())
&& expRecord.getStartTime().after(w.getStartTime())) {
remove.add(w);
}
// Do not plot watches that out dated CONs
if (act == WarningAction.CON
&& (conRecord == null || conRecord.getEtn().equals(
w.getEtn()) == false)) {
conRecord = w;
} else if (conRecord != null
&& (act == WarningAction.CON || act == WarningAction.NEW)
&& conRecord.getEtn().equals(w.getEtn())
&& conRecord.getPhensig().equals(w.getPhensig())
&& conRecord.getStartTime().after(w.getStartTime())) {
remove.add(w);
}
}
for (AbstractWarningRecord w : remove) {
sortedWatches.remove(w);
}
Map<String, AbstractWarningRecord> watches = new HashMap<String, AbstractWarningRecord>();
for (AbstractWarningRecord watchRec : sortedWatches) {
TimeRange tr = new TimeRange(watchRec.getStartTime(),
watchRec.getEndTime());
if (tr.contains(thisFrameTime.getValidTime().getTime())) {
addWatch(watches, watchRec);
}
}
ArrayList<AbstractWarningRecord> watchList = new ArrayList<AbstractWarningRecord>();
if (watches.isEmpty() == false) {
for (String key : watches.keySet()) {
AbstractWarningRecord watch = watches.get(key);
watch.setPil(SiteAbbreviationUtil.getSiteNode(watch.getXxxid())
+ watch.getPil() + watch.getXxxid());
watchList.add(watch);
scheduleTimer(watch);
initShape(watch);
WarningEntry entry = entryMap.get(watch.getDataURI());
if (entry == null) {
entry = new WarningEntry();
entry.record = watch;
entryMap.put(watch.getDataURI(), entry);
synchronized (this) {
try {
addRecord(getWarningRecordArray());
} catch (VizException e) {
e.printStackTrace();
}
List<DataTime> list = entry.times;
if (list == null) {
list = new ArrayList<DataTime>();
}
list.add(thisFrameTime);
entry.times = list;
}
}
// force creation of a frame for any currently active warnings, this
// frame might get displayed in place of the last frame.
requestData(new DataTime(SimulatedTime.getSystemTime().getTime()));
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.core.rsc.IVizResource#dispose()
*/
@Override
protected void disposeInternal() {
timer.cancel();
for (WarningEntry entry : entryMap.values()) {
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
}
if (entry.wireframeShape != null) {
entry.wireframeShape.dispose();
}
}
this.frames.put(thisFrameTime, watchList);
System.out.println("Init Frame: " + (System.currentTimeMillis() - t0)
+ " ms");
entryMap.clear();
if (warningsFont != null) {
warningsFont.dispose();
}
}
@Override
protected synchronized void updateFrames() throws VizException {
if (!this.recordsToLoad.isEmpty()) {
for (AbstractWarningRecord watchRec : recordsToLoad) {
WarningAction watchAct = WarningAction.valueOf(watchRec
.getAct());
TimeRange tr = new TimeRange(watchRec.getStartTime(),
watchRec.getEndTime());
synchronized (frames) {
for (DataTime dt : frames.keySet()) {
if (tr.contains(dt.getValidTime().getTime())) {
boolean add = true;
ArrayList<AbstractWarningRecord> remove = new ArrayList<AbstractWarningRecord>();
List<AbstractWarningRecord> watchList = frames
.get(dt);
for (AbstractWarningRecord watch : watchList) {
// Do not plot watches that have been Cancelled
// or
// Expired
if ((watchAct == WarningAction.CAN || watchAct == WarningAction.EXP)
&& watchRec.getStartTime().after(dt) == false
&& watchRec.getEtn().equals(
watch.getEtn())
&& watchRec.getPhensig().equals(
watch.getPhensig())
&& watchRec.getUgcsString() != null
&& watchRec.getUgcsString()[0] != null
&& watchRec.getUgcsString()[0]
.endsWith("000")) {
remove.add(watch);
}
if (watchAct == WarningAction.CON
&& (WarningAction.valueOf(watch
.getAct()) == WarningAction.CON || WarningAction
.valueOf(watch.getAct()) == WarningAction.NEW)
&& watchRec.getEtn().equals(
watch.getEtn())
&& watchRec.getPhensig().equals(
watch.getPhensig())) {
if (watchRec.getStartTime().after(
watch.getStartTime())) {
remove.add(watch);
} else {
add = false;
}
}
}
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.DATA_UPDATE) {
PluginDataObject[] pdo = (PluginDataObject[]) object;
synchronized (WatchesResource.this) {
{
try {
addRecord(pdo);
} catch (VizException e) {
statusHandler.handle(Priority.SIGNIFICANT,
e.getLocalizedMessage(), e);
}
}
}
} else if (type == ChangeType.CAPABILITY) {
if (color != null
&& color.equals(getCapability((ColorableCapability.class))
.getColor()) == false) {
color = getCapability((ColorableCapability.class)).getColor();
for (AbstractWarningRecord w : remove) {
watchList.remove(w);
}
if (add) {
watchList.add(watchRec);
scheduleTimer(watchRec);
initShape(watchRec);
WarningEntry entry = entryMap.get(watchRec
.getDataURI());
if (entry == null) {
entry = new WarningEntry();
entry.record = watchRec;
entryMap.put(watchRec.getDataURI(), entry);
}
List<DataTime> list = entry.times;
if (list == null) {
list = new ArrayList<DataTime>();
}
list.add(dt);
entry.times = list;
}
for (String dataUri : entryMap.keySet()) {
WarningEntry entry = entryMap.get(dataUri);
if (entry.shadedShape != null) {
entry.shadedShape.dispose();
try {
initShape(target, entry.record);
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
}
}
if (frames.containsKey(new DataTime(watchRec.getStartTime())) == false) {
initNewFrame(new DataTime(watchRec.getStartTime()));
}
}
recordsToLoad.clear();
}
issueRefresh();
}
@Override
protected void initShape(AbstractWarningRecord record) throws VizException {
protected void initShape(IGraphicsTarget target,
AbstractWarningRecord record) throws VizException {
Geometry geo;
if (record.getUgczones().size() > 0) {
setGeometry(record);
if (record.getGeometry() != null) {
IShadedShape ss = target.createShadedShape(false, descriptor,
false);
IShadedShape ss = target.createShadedShape(false,
descriptor.getGridGeometry(), false);
geo = (Geometry) record.getGeometry().clone();
JTSCompiler jtsCompiler = new JTSCompiler(ss, null,
this.descriptor, PointStyle.CROSS);
@ -279,6 +206,66 @@ public class WatchesResource extends AbstractWatchesResource {
}
}
@Override
protected synchronized void updateDisplay(IGraphicsTarget target)
throws VizException {
if (!recordsToLoad.isEmpty()) {
// Merges all the zones for the same vtec and time
List<AbstractWarningRecord> mergedWatches = mergeWatches(recordsToLoad);
for (AbstractWarningRecord watchrec : mergedWatches) {
WarningAction watchact = WarningAction.valueOf(watchrec
.getAct());
int watchSize = watchrec.getUgczones().size();
if (watchact != WarningAction.NEW) {
AbstractWarningRecord createShape = null;
for (String entryKey : entryMap.keySet()) {
WarningEntry entry = entryMap.get(entryKey);
AbstractWarningRecord rec = entry.record;
if (rec.getPhensig().equals(watchrec.getPhensig())
&& rec.getOfficeid().equals(
watchrec.getOfficeid())
&& rec.getEtn().equals(watchrec.getEtn())) {
int recSize = rec.getUgczones().size();
if (!entry.partialCancel) {
if (watchact == WarningAction.EXP
|| watchact == WarningAction.CAN) {
entry.partialCancel = true;
entry.record.setEndTime((Calendar) watchrec
.getStartTime().clone());
} else if (watchact == WarningAction.CON
&& recSize > watchSize
&& watchrec.getStartTime().after(
rec.getStartTime())) {
entry.partialCancel = true;
entry.record.setEndTime((Calendar) watchrec
.getStartTime().clone());
createShape = watchrec;
}
}
}
}
if (createShape != null) {
WarningEntry entry = entryMap.get(createShape
.getDataURI());
if (entry != null) {
entry.shadedShape.dispose();
}
initShape(target, createShape);
}
} else {
initShape(target, watchrec);
}
}
recordsToLoad.clear();
}
}
private void setGeometry(AbstractWarningRecord record) {
List<String> county = new ArrayList<String>();
List<String> marinezone = new ArrayList<String>();
@ -365,35 +352,90 @@ public class WatchesResource extends AbstractWatchesResource {
record.setGeometry(geometry);
}
/*
* Combining watches with the same ETN (Watch No.) into a single record.
* This will also help performance to make a single DB query.
/**
* Groups all the ugc zones with the same 'product.act.phensig.etn'
*/
private void addWatch(Map<String, AbstractWarningRecord> watches,
AbstractWarningRecord watchrec) {
String key = watchrec.getProductClass() + "." + watchrec.getPhensig()
+ "." + watchrec.getEtn();
AbstractWarningRecord watch = null;
WarningAction act = WarningAction.valueOf(watchrec.getAct());
if (watches.containsKey(key)) {
watch = watches.get(key);
Set<UGCZone> ugcZones = watch.getUgczones();
for (UGCZone zone : watchrec.getUgczones()) {
if (act == WarningAction.CAN || act == WarningAction.EXP) {
ugcZones.remove(zone);
} else {
ugcZones.add(zone);
}
private List<AbstractWarningRecord> mergeWatches(
List<AbstractWarningRecord> watchrecs) {
Map<String, AbstractWarningRecord> watches = new HashMap<String, AbstractWarningRecord>();
for (AbstractWarningRecord watchrec : watchrecs) {
String key = watchrec.getAct() + '.' + watchrec.getPhensig() + '.'
+ watchrec.getEtn() + '.'
+ sdf.format(watchrec.getStartTime().getTime());
AbstractWarningRecord watch = watches.get(key);
if (watch == null) {
watch = watchrec;
} else if (watchrec.getUgczones() != null) {
Set<UGCZone> ugcZones = watch.getUgczones();
ugcZones.addAll(watchrec.getUgczones());
watch.setUgczones(ugcZones);
}
watch.setUgczones(ugcZones);
} else if (act != WarningAction.CAN && act != WarningAction.EXP) {
watch = watchrec;
}
if (watch != null) {
watches.put(key, watch);
}
ArrayList<AbstractWarningRecord> mergedWatches = new ArrayList<AbstractWarningRecord>(
watches.values());
Collections.sort(mergedWatches, comparator);
return mergedWatches;
}
/**
* Schedules a WarningExpirationTask for the end time of the passing in
* record
*
* @param rec
* a WarningRecord
*/
protected void scheduleTimer(AbstractWarningRecord rec) {
// only schedule if record has not expired already
long now = SimulatedTime.getSystemTime().getTime().getTime();
long endTime = rec.getEndTime().getTimeInMillis();
synchronized (expTaskSet) {
if (endTime > now && !expTaskSet.contains(new Long(endTime))) {
WarningExpirationTask task = new WarningExpirationTask(this);
timer.schedule(task, rec.getEndTime().getTime());
expTaskSet.add(new Long(endTime));
}
}
}
/**
* Redo time matching and remove the passed in time from the map of
* scheduled times
*
* @param triggerTime
*/
public void redoTimeMatching(long triggerTime) {
redoTimeMatching();
Long time = new Long(triggerTime);
// remove the instance of the trigger time from the map
synchronized (expTaskSet) {
if (expTaskSet != null && expTaskSet.contains(time)) {
expTaskSet.remove(time);
}
}
}
/**
* Redo the time matching
*/
public void redoTimeMatching() {
try {
this.getDescriptor().getTimeMatcher().redoTimeMatching(this);
this.getDescriptor().getTimeMatcher()
.redoTimeMatching(this.getDescriptor());
} catch (VizException e) {
// TODO Auto-generated catch block. Please revise as appropriate.
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
}
}
@Override
protected String getEventKey(WarningEntry entry) {
AbstractWarningRecord r = entry.record;
return r.getAct() + '.' + r.getPhensig() + '.' + r.getEtn() + '.'
+ sdf.format(entry.record.getStartTime().getTime());
}
}