Issue #2070 Add geospatial constraints to metar precip requests.
Change-Id: Ie7ce8ac0f3d2655c5e427aee0588b8546b1674d1 Former-commit-id:3a6af1b023
[formerly984410e172
[formerlyb94373379d
] [formerly3a6af1b023
[formerly fe7cfca3760374fdeac084158ddb6f1b1a4e5d54]]] Former-commit-id:984410e172
[formerlyb94373379d
] Former-commit-id:984410e172
Former-commit-id:c9b6c18cab
This commit is contained in:
parent
6632420d11
commit
2acab55876
2 changed files with 328 additions and 74 deletions
|
@ -37,21 +37,30 @@ import org.eclipse.core.runtime.IStatus;
|
|||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.swt.graphics.RGB;
|
||||
import org.geotools.coverage.grid.GridEnvelope2D;
|
||||
import org.geotools.coverage.grid.GridGeometry2D;
|
||||
import org.geotools.geometry.DirectPosition2D;
|
||||
import org.geotools.geometry.Envelope2D;
|
||||
import org.opengis.referencing.FactoryException;
|
||||
import org.opengis.referencing.crs.CoordinateReferenceSystem;
|
||||
import org.opengis.referencing.operation.MathTransform;
|
||||
import org.opengis.referencing.operation.TransformException;
|
||||
|
||||
import com.raytheon.uf.common.dataplugin.PluginDataObject;
|
||||
import com.raytheon.uf.common.dataplugin.annotations.DataURIUtil;
|
||||
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
|
||||
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
|
||||
import com.raytheon.uf.common.geospatial.MapUtil;
|
||||
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.viz.core.DrawableString;
|
||||
import com.raytheon.uf.viz.core.IExtent;
|
||||
import com.raytheon.uf.viz.core.IGraphicsTarget;
|
||||
import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment;
|
||||
import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment;
|
||||
import com.raytheon.uf.viz.core.RecordFactory;
|
||||
import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo;
|
||||
import com.raytheon.uf.viz.core.drawables.IFont;
|
||||
import com.raytheon.uf.viz.core.drawables.IFont.Style;
|
||||
|
@ -60,7 +69,7 @@ import com.raytheon.uf.viz.core.drawables.PaintProperties;
|
|||
import com.raytheon.uf.viz.core.exception.VizException;
|
||||
import com.raytheon.uf.viz.core.map.IMapDescriptor;
|
||||
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
|
||||
import com.raytheon.uf.viz.core.rsc.IResourceDataChanged;
|
||||
import com.raytheon.uf.viz.core.rsc.IResourceDataChanged.ChangeType;
|
||||
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.DensityCapability;
|
||||
|
@ -71,7 +80,10 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
|
||||
/**
|
||||
*
|
||||
* TODO Add Description
|
||||
* Resource for displaying Metar Precip values as strings on the map. Uses
|
||||
* custom data request so that it can use derived precip values. Uses custom
|
||||
* progressive disclosure so that sites with the most precip are always
|
||||
* disclosed first.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
|
@ -79,7 +91,9 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Aug 19, 2011 bsteffen Initial creation
|
||||
* Aug 19, 2011 bsteffen Initial creation
|
||||
* Jun 07, 2013 2070 bsteffen Add geospatial constraints to metar
|
||||
* precip requests.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -88,6 +102,8 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
*/
|
||||
public class MetarPrecipResource extends
|
||||
AbstractVizResource<MetarPrecipResourceData, IMapDescriptor> {
|
||||
private static final transient IUFStatusHandler statusHandler = UFStatus
|
||||
.getHandler(MetarPrecipResource.class);
|
||||
|
||||
private static final int PLOT_PIXEL_SIZE = 30;
|
||||
|
||||
|
@ -120,10 +136,6 @@ public class MetarPrecipResource extends
|
|||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
processRemoves();
|
||||
if (monitor.isCanceled()) {
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
processReproject();
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
|
||||
|
@ -195,7 +207,7 @@ public class MetarPrecipResource extends
|
|||
continue;
|
||||
}
|
||||
if (data.distValue >= threshold) {
|
||||
// This is easier then changing it when the capability cahnges.
|
||||
// This is easier then changing it when the capability changes.
|
||||
data.string.font = this.font;
|
||||
data.string.setText(data.string.getText(), color);
|
||||
strings.add(data.string);
|
||||
|
@ -220,32 +232,30 @@ public class MetarPrecipResource extends
|
|||
|
||||
@Override
|
||||
protected void initInternal(IGraphicsTarget target) throws VizException {
|
||||
|
||||
dataTimes = new ArrayList<DataTime>();
|
||||
dataProcessJob.schedule();
|
||||
resourceData.addChangeListener(new IResourceDataChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourceChanged(ChangeType type, Object object) {
|
||||
if (type == ChangeType.CAPABILITY) {
|
||||
if (object instanceof MagnificationCapability) {
|
||||
if (font != null) {
|
||||
font.dispose();
|
||||
font = null;
|
||||
}
|
||||
}
|
||||
issueRefresh();
|
||||
} else if (type == ChangeType.DATA_UPDATE) {
|
||||
if (object instanceof PluginDataObject[]) {
|
||||
PluginDataObject[] pdos = (PluginDataObject[]) object;
|
||||
for (PluginDataObject pdo : pdos) {
|
||||
updates.offer(pdo);
|
||||
dataProcessJob.schedule();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void resourceDataChanged(ChangeType type, Object object) {
|
||||
super.resourceDataChanged(type, object);
|
||||
if (type == ChangeType.CAPABILITY) {
|
||||
if (object instanceof MagnificationCapability) {
|
||||
if (font != null) {
|
||||
font.dispose();
|
||||
font = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
issueRefresh();
|
||||
} else if (type == ChangeType.DATA_UPDATE) {
|
||||
if (object instanceof PluginDataObject[]) {
|
||||
PluginDataObject[] pdos = (PluginDataObject[]) object;
|
||||
for (PluginDataObject pdo : pdos) {
|
||||
updates.offer(pdo);
|
||||
dataProcessJob.schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -255,12 +265,14 @@ public class MetarPrecipResource extends
|
|||
|
||||
@Override
|
||||
public void remove(DataTime dataTime) {
|
||||
// This will be handled asynchronously by the update job
|
||||
removes.offer(dataTime);
|
||||
dataProcessJob.schedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void project(CoordinateReferenceSystem crs) throws VizException {
|
||||
// This will be handled asynchronously by the update job
|
||||
reproject = true;
|
||||
dataProcessJob.schedule();
|
||||
}
|
||||
|
@ -313,21 +325,35 @@ public class MetarPrecipResource extends
|
|||
return "No Data";
|
||||
}
|
||||
|
||||
private void processReproject() {
|
||||
private boolean processReproject() {
|
||||
if (reproject) {
|
||||
reproject = false;
|
||||
// reproject all stations to the new crs and throw out any off the
|
||||
// screen
|
||||
GridEnvelope2D envelope = GridGeometry2D.wrap(
|
||||
descriptor.getGridGeometry()).getGridRange2D();
|
||||
synchronized (data) {
|
||||
for (List<RenderablePrecipData> dataList : data.values()) {
|
||||
for (RenderablePrecipData precip : dataList) {
|
||||
Iterator<RenderablePrecipData> it = dataList.iterator();
|
||||
while (it.hasNext()) {
|
||||
RenderablePrecipData precip = it.next();
|
||||
Coordinate latLon = precip.getLatLon();
|
||||
double[] px = descriptor.worldToPixel(new double[] {
|
||||
latLon.x, latLon.y });
|
||||
precip.string.setCoordinates(px[0], px[1], px[2]);
|
||||
if (envelope.contains(px[0], px[1])) {
|
||||
precip.string.setCoordinates(px[0], px[1], px[2]);
|
||||
} else {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// returning true will tell the caller to reload the frame in case
|
||||
// any data in the new area was outside the old area
|
||||
return true;
|
||||
}
|
||||
issueRefresh();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processRemoves() {
|
||||
|
@ -350,20 +376,48 @@ public class MetarPrecipResource extends
|
|||
RequestConstraint rc = new RequestConstraint(null, ConstraintType.IN);
|
||||
long earliestTime = Long.MAX_VALUE;
|
||||
Set<String> newStations = new HashSet<String>();
|
||||
// Get the envelope and math transform to ensure we only bother
|
||||
// processing updates on screen.
|
||||
MathTransform toDescriptor = null;
|
||||
try {
|
||||
toDescriptor = MapUtil.getTransformFromLatLon(descriptor.getCRS());
|
||||
} catch (FactoryException e) {
|
||||
statusHandler
|
||||
.handle(Priority.PROBLEM,
|
||||
"Error processing updates for MetarPrecip, Ignoring all updates.",
|
||||
e);
|
||||
updates.clear();
|
||||
return;
|
||||
}
|
||||
Envelope2D envelope = new Envelope2D(descriptor.getGridGeometry()
|
||||
.getEnvelope());
|
||||
while (!updates.isEmpty()) {
|
||||
PluginDataObject pdo = updates.poll();
|
||||
try {
|
||||
Map<String, Object> map = RecordFactory.getInstance()
|
||||
.loadMapFromUri(pdo.getDataURI());
|
||||
newStations.add(map.get("location.stationId").toString());
|
||||
} catch (VizException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
long validTime = pdo.getDataTime().getMatchValid();
|
||||
if (validTime < earliestTime) {
|
||||
earliestTime = validTime;
|
||||
Map<String, Object> map = DataURIUtil.createDataURIMap(pdo);
|
||||
double lon = ((Number) map.get("location.longitude"))
|
||||
.doubleValue();
|
||||
double lat = ((Number) map.get("location.latitude"))
|
||||
.doubleValue();
|
||||
DirectPosition2D dp = new DirectPosition2D(lon, lat);
|
||||
toDescriptor.transform(dp, dp);
|
||||
if (envelope.contains(dp)) {
|
||||
newStations.add(map.get("location.stationId").toString());
|
||||
long validTime = pdo.getDataTime().getMatchValid();
|
||||
if (validTime < earliestTime) {
|
||||
earliestTime = validTime;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
statusHandler
|
||||
.handle(Priority.PROBLEM,
|
||||
"Error processing updates for MetarPrecip, Ignoring an update.",
|
||||
e);
|
||||
}
|
||||
}
|
||||
if (newStations.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
rc.setConstraintValueList(newStations.toArray(new String[0]));
|
||||
rcMap.put("location.stationId", rc);
|
||||
MetarPrecipDataContainer container = new MetarPrecipDataContainer(
|
||||
|
@ -393,16 +447,25 @@ public class MetarPrecipResource extends
|
|||
}
|
||||
|
||||
private void processNewFrames(IProgressMonitor monitor) {
|
||||
|
||||
// load data in two steps, first load base data then any derived data.
|
||||
// Always try to load the current frame, then nearby frames.
|
||||
MetarPrecipDataContainer container = new MetarPrecipDataContainer(
|
||||
resourceData.getDuration(), resourceData.getMetadataMap());
|
||||
resourceData.getDuration(), resourceData.getMetadataMap(),
|
||||
descriptor.getGridGeometry().getEnvelope());
|
||||
Set<DataTime> reprojectedFrames = new HashSet<DataTime>();
|
||||
Set<DataTime> baseOnly = new HashSet<DataTime>();
|
||||
boolean modified = true;
|
||||
while (modified) {
|
||||
// don't want to mis a reproject if retrieval takes awhile.
|
||||
processReproject();
|
||||
// don't want to miss a reproject if retrieval takes awhile.
|
||||
if (processReproject()) {
|
||||
// We must create a new container and re request all the data
|
||||
// for the new area.
|
||||
reprojectedFrames = new HashSet<DataTime>(data.keySet());
|
||||
container = new MetarPrecipDataContainer(
|
||||
resourceData.getDuration(),
|
||||
resourceData.getMetadataMap(), descriptor
|
||||
.getGridGeometry().getEnvelope());
|
||||
}
|
||||
if (monitor.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
@ -417,26 +480,25 @@ public class MetarPrecipResource extends
|
|||
}
|
||||
int curIndex = frameInfo.getFrameIndex();
|
||||
int count = frameInfo.getFrameCount();
|
||||
if (times.length != count) {
|
||||
System.out.println("Uh oh");
|
||||
}
|
||||
// This will generate the number series 0, -1, 1, -2, 2, -3, 3...
|
||||
for (int i = 0; i < count / 2 + 1; i = i < 0 ? -i : -i - 1) {
|
||||
int index = (count + curIndex + i) % count;
|
||||
DataTime next = times[index];
|
||||
if (next != null) {
|
||||
if (!data.containsKey(next)) {
|
||||
if (!data.containsKey(next)
|
||||
|| reprojectedFrames.contains(next)) {
|
||||
List<PrecipData> baseData = container
|
||||
.getBasePrecipData(next);
|
||||
addData(next, baseData);
|
||||
baseOnly.add(next);
|
||||
reprojectedFrames.remove(next);
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
if (baseOnly.contains(next)) {
|
||||
List<PrecipData> baseData = container
|
||||
List<PrecipData> derivedData = container
|
||||
.getDerivedPrecipData(next);
|
||||
addData(next, baseData);
|
||||
addData(next, derivedData);
|
||||
baseOnly.remove(next);
|
||||
modified = true;
|
||||
break;
|
||||
|
@ -484,6 +546,9 @@ public class MetarPrecipResource extends
|
|||
|
||||
RGB color = getCapability(ColorableCapability.class).getColor();
|
||||
|
||||
GridEnvelope2D envelope = GridGeometry2D.wrap(
|
||||
descriptor.getGridGeometry()).getGridRange2D();
|
||||
|
||||
for (int i = 0; i < precips.size(); i++) {
|
||||
PrecipData precip = precips.get(i);
|
||||
RenderablePrecipData data = null;
|
||||
|
@ -493,6 +558,9 @@ public class MetarPrecipResource extends
|
|||
data = new RenderablePrecipData(precip);
|
||||
double[] px = descriptor.worldToPixel(new double[] {
|
||||
precip.getLatLon().x, precip.getLatLon().y });
|
||||
if (!envelope.contains(px[0], px[1])) {
|
||||
continue;
|
||||
}
|
||||
data.string = new DrawableString(formatPrecip(precips.get(i)
|
||||
.getPrecipAmt()), color);
|
||||
data.string.setCoordinates(px[0], px[1], px[2]);
|
||||
|
@ -509,7 +577,10 @@ public class MetarPrecipResource extends
|
|||
}
|
||||
}
|
||||
data.distValue = bestDist;
|
||||
newPrecips.add(data);
|
||||
// this checks removes duplicates
|
||||
if (bestDist > 0) {
|
||||
newPrecips.add(data);
|
||||
}
|
||||
}
|
||||
synchronized (data) {
|
||||
data.put(time, newPrecips);
|
||||
|
|
|
@ -29,8 +29,12 @@ import java.util.Map;
|
|||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.geotools.geometry.jts.ReferencedEnvelope;
|
||||
|
||||
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
|
||||
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
|
||||
import com.raytheon.uf.common.geospatial.MapUtil;
|
||||
import com.raytheon.uf.common.geospatial.util.EnvelopeIntersection;
|
||||
import com.raytheon.uf.common.pointdata.PointDataContainer;
|
||||
import com.raytheon.uf.common.pointdata.PointDataView;
|
||||
import com.raytheon.uf.common.status.IUFStatusHandler;
|
||||
|
@ -40,10 +44,18 @@ import com.raytheon.uf.common.time.DataTime;
|
|||
import com.raytheon.uf.viz.core.datastructure.DataCubeContainer;
|
||||
import com.raytheon.uf.viz.core.exception.VizException;
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
import com.vividsolutions.jts.geom.Envelope;
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
import com.vividsolutions.jts.geom.GeometryCollection;
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO Add Description
|
||||
* Container for requesting and caching metar precip data. This container can be
|
||||
* reused to request data for multiple times. Typically it is used in 2 stages,
|
||||
* first getBasePrecipData is used to quickly retrieve data for all metar
|
||||
* stations which have the data directly available, second getDerivedPrecipData
|
||||
* is used to get all the data taht is not directly available but has to be
|
||||
* derived by accumulating other reports over time.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
|
@ -51,8 +63,11 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Aug 19, 2011 bsteffen Initial creation
|
||||
* Jan 10, 2013 snaples updated getBasePrecipData to use correct data for 1 hour precip.
|
||||
* Aug 19, 2011 bsteffen Initial creation
|
||||
* Jan 10, 2013 snaples updated getBasePrecipData to use correct
|
||||
* data for 1 hour precip.
|
||||
* Jun 07, 2013 2070 bsteffen Add geospatial constraints to metar
|
||||
* precip requests.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -65,6 +80,13 @@ public class MetarPrecipDataContainer {
|
|||
private static final transient IUFStatusHandler statusHandler = UFStatus
|
||||
.getHandler(MetarPrecipDataContainer.class);
|
||||
|
||||
/*
|
||||
* Envelope which contains the whole world in LatLon projection. Used for
|
||||
* intersections/conversion since this is the valid area for metar records.
|
||||
*/
|
||||
private static final ReferencedEnvelope WORLD_LAT_LON_ENVELOPE = new ReferencedEnvelope(
|
||||
-180, 180, -90, 90, MapUtil.LATLON_PROJECTION);
|
||||
|
||||
public static class PrecipData {
|
||||
|
||||
private final long timeObs;
|
||||
|
@ -128,7 +150,11 @@ public class MetarPrecipDataContainer {
|
|||
|
||||
private final int duration;
|
||||
|
||||
private Map<String, RequestConstraint> rcMap = null;
|
||||
private final Map<String, RequestConstraint> rcMap;
|
||||
|
||||
private final org.opengis.geometry.Envelope descriptorEnvelope;
|
||||
|
||||
private List<Envelope> latLonEnvelopes;
|
||||
|
||||
private final Map<Long, Map<String, PrecipData>> cache3 = new HashMap<Long, Map<String, PrecipData>>();
|
||||
|
||||
|
@ -136,12 +162,45 @@ public class MetarPrecipDataContainer {
|
|||
|
||||
private final Map<DataTime, Set<String>> baseStations = new HashMap<DataTime, Set<String>>();
|
||||
|
||||
/**
|
||||
* Consturct a container with geospatially filtering to only request data in
|
||||
* the area of descriptorEnvelope
|
||||
*
|
||||
* @param duration
|
||||
* @param rcMap
|
||||
* @param descriptorEnvelope
|
||||
*/
|
||||
public MetarPrecipDataContainer(int duration,
|
||||
HashMap<String, RequestConstraint> rcMap,
|
||||
org.opengis.geometry.Envelope descriptorEnvelope) {
|
||||
this.duration = duration;
|
||||
this.rcMap = rcMap;
|
||||
this.descriptorEnvelope = descriptorEnvelope;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will construct a container with no geospatial constraints, use it
|
||||
* only for updates where the rcMap already has stationIds that are
|
||||
* geospatially filtered.
|
||||
*
|
||||
* @param duration
|
||||
* @param rcMap
|
||||
*/
|
||||
public MetarPrecipDataContainer(int duration,
|
||||
Map<String, RequestConstraint> rcMap) {
|
||||
this.duration = duration;
|
||||
this.rcMap = rcMap;
|
||||
this.descriptorEnvelope = null;
|
||||
this.latLonEnvelopes = Arrays.<Envelope> asList(WORLD_LAT_LON_ENVELOPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base precip data from all metar stations that have precip for the
|
||||
* specified duration directly encoded.
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public List<PrecipData> getBasePrecipData(DataTime time) {
|
||||
Map<String, PrecipData> precipMap = new HashMap<String, PrecipData>();
|
||||
long validTime = time.getMatchValid();
|
||||
|
@ -150,12 +209,11 @@ public class MetarPrecipDataContainer {
|
|||
P1_KEY);
|
||||
Map<String, PrecipData> precipMap1 = null;
|
||||
if (pdc != null) {
|
||||
precipMap1 = createPrecipData(pdc,
|
||||
validTime - ONE_HOUR, validTime, P1_KEY);
|
||||
precipMap1 = createPrecipData(pdc, validTime - ONE_HOUR,
|
||||
validTime, P1_KEY);
|
||||
if (precipMap1 == null) {
|
||||
precipMap1 = createPrecipData(pdc,
|
||||
validTime - ONE_HOUR + FIFTEEN_MIN, validTime
|
||||
- FIFTEEN_MIN, P1_KEY);
|
||||
precipMap1 = createPrecipData(pdc, validTime - ONE_HOUR
|
||||
+ FIFTEEN_MIN, validTime - FIFTEEN_MIN, P1_KEY);
|
||||
}
|
||||
// Data frame 15 minutes ago is better then data now for some
|
||||
// reason
|
||||
|
@ -183,6 +241,13 @@ public class MetarPrecipDataContainer {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the derived precip data for all stations which don't have the base
|
||||
* data. This will attempt to accumulate precipitation from old reports.
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public List<PrecipData> getDerivedPrecipData(DataTime time) {
|
||||
Map<String, PrecipData> precipMap = new HashMap<String, PrecipData>();
|
||||
long validTime = time.getMatchValid();
|
||||
|
@ -217,6 +282,15 @@ public class MetarPrecipDataContainer {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw metar data by summiong up multiple 1 hour observations
|
||||
*
|
||||
* @param validTime
|
||||
* the valid time to get data for
|
||||
* @param sumTime
|
||||
* how many 1hour precip obs to sum up.
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PrecipData> getRawPrecipData1sum(long validTime,
|
||||
int sumTime) {
|
||||
List<Map<String, PrecipData>> maps = new ArrayList<Map<String, PrecipData>>();
|
||||
|
@ -237,6 +311,12 @@ public class MetarPrecipDataContainer {
|
|||
return add(maps.toArray(new Map[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all base 3 hour precip records for the provided times
|
||||
*
|
||||
* @param validTime
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PrecipData> getRawPrecipData3(long validTime) {
|
||||
if (cache3.containsKey(validTime)) {
|
||||
return cache3.get(validTime);
|
||||
|
@ -257,6 +337,12 @@ public class MetarPrecipDataContainer {
|
|||
return precipMap3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all base 6 hour precip records for the provided times
|
||||
*
|
||||
* @param validTime
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PrecipData> getRawPrecipData6(long validTime) {
|
||||
if (cache6.containsKey(validTime)) {
|
||||
return cache6.get(validTime);
|
||||
|
@ -272,6 +358,16 @@ public class MetarPrecipDataContainer {
|
|||
return precipMap6;
|
||||
}
|
||||
|
||||
/**
|
||||
* build PricipData objects for every station in a PointDataContainer
|
||||
*
|
||||
* @param pdc
|
||||
* @param startTime
|
||||
* @param latestTime
|
||||
* @param precipKey
|
||||
* the name of the parameter with precip.
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PrecipData> createPrecipData(PointDataContainer pdc,
|
||||
long startTime, long latestTime, String precipKey) {
|
||||
Map<String, PrecipData> precipMap = new HashMap<String, PrecipData>();
|
||||
|
@ -307,6 +403,15 @@ public class MetarPrecipDataContainer {
|
|||
return precipMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function perfroms the request to edex for point data.
|
||||
*
|
||||
* @param rcMap
|
||||
* @param time
|
||||
* @param duration
|
||||
* @param precipKeys
|
||||
* @return
|
||||
*/
|
||||
private PointDataContainer requestPointData(
|
||||
Map<String, RequestConstraint> rcMap, long time, int duration,
|
||||
String... precipKeys) {
|
||||
|
@ -331,21 +436,84 @@ public class MetarPrecipDataContainer {
|
|||
end.toString() });
|
||||
rcMap.put("dataTime", timeRC);
|
||||
PointDataContainer pdc = null;
|
||||
try {
|
||||
pdc = DataCubeContainer.getPointData("obs",
|
||||
parameters.toArray(new String[0]), rcMap);
|
||||
} catch (VizException e) {
|
||||
statusHandler.handle(Priority.ERROR,
|
||||
"Error getting precip data, some precip will not display.",
|
||||
e);
|
||||
// Over the dateline there might be an envelope on either side.
|
||||
for (Envelope latLonEnvelope : getLatLonEnvelopes()) {
|
||||
PointDataContainer tmppdc = null;
|
||||
RequestConstraint lonRC = new RequestConstraint(null,
|
||||
ConstraintType.BETWEEN);
|
||||
Double minLon = latLonEnvelope.getMinX();
|
||||
Double maxLon = latLonEnvelope.getMaxX();
|
||||
lonRC.setBetweenValueList(new String[] { minLon.toString(),
|
||||
maxLon.toString() });
|
||||
rcMap.put("location.longitude", lonRC);
|
||||
RequestConstraint latRC = new RequestConstraint(null,
|
||||
ConstraintType.BETWEEN);
|
||||
Double minLat = latLonEnvelope.getMinY();
|
||||
Double maxLat = latLonEnvelope.getMaxY();
|
||||
latRC.setBetweenValueList(new String[] { minLat.toString(),
|
||||
maxLat.toString() });
|
||||
rcMap.put("location.latitude", latRC);
|
||||
try {
|
||||
tmppdc = DataCubeContainer.getPointData("obs",
|
||||
parameters.toArray(new String[0]), rcMap);
|
||||
} catch (VizException e) {
|
||||
statusHandler
|
||||
.handle(Priority.ERROR,
|
||||
"Error getting precip data, some precip will not display.",
|
||||
e);
|
||||
}
|
||||
if (tmppdc != null) {
|
||||
tmppdc.setCurrentSz(tmppdc.getAllocatedSz());
|
||||
if (pdc != null) {
|
||||
pdc.combine(tmppdc);
|
||||
pdc.setCurrentSz(pdc.getAllocatedSz());
|
||||
} else {
|
||||
pdc = tmppdc;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pdc != null) {
|
||||
pdc.setCurrentSz(pdc.getAllocatedSz());
|
||||
return pdc;
|
||||
}
|
||||
return null;
|
||||
return pdc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get envelopes describing the latlon area that should be used to constrain
|
||||
* all queries
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private List<Envelope> getLatLonEnvelopes() {
|
||||
if (latLonEnvelopes == null) {
|
||||
this.latLonEnvelopes = new ArrayList<Envelope>(2);
|
||||
try {
|
||||
Geometry intersection = EnvelopeIntersection
|
||||
.createEnvelopeIntersection(descriptorEnvelope,
|
||||
WORLD_LAT_LON_ENVELOPE, 0.1, 180, 180);
|
||||
if (intersection instanceof GeometryCollection) {
|
||||
GeometryCollection gc = (GeometryCollection) intersection;
|
||||
for (int n = 0; n < gc.getNumGeometries(); n += 1) {
|
||||
latLonEnvelopes.add(gc.getGeometryN(n)
|
||||
.getEnvelopeInternal());
|
||||
}
|
||||
} else {
|
||||
latLonEnvelopes.add(intersection.getEnvelopeInternal());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
statusHandler.handle(Priority.VERBOSE, e.getLocalizedMessage(),
|
||||
e);
|
||||
this.latLonEnvelopes.add(WORLD_LAT_LON_ENVELOPE);
|
||||
}
|
||||
}
|
||||
return latLonEnvelopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract the precip amounts in map2 from those in map1 for all stations
|
||||
* that are in both maps.
|
||||
*
|
||||
* @param map1
|
||||
* @param map2
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PrecipData> subtract(Map<String, PrecipData> map1,
|
||||
Map<String, PrecipData> map2) {
|
||||
Map<String, PrecipData> result = new HashMap<String, PrecipData>();
|
||||
|
@ -364,6 +532,14 @@ public class MetarPrecipDataContainer {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract the precip amounts in several maps for all stations that are in
|
||||
* all maps.
|
||||
*
|
||||
* @param map1
|
||||
* @param map2
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PrecipData> add(Map<String, PrecipData>... maps) {
|
||||
Map<String, PrecipData> result = new HashMap<String, PrecipData>();
|
||||
for (Map<String, PrecipData> map : maps) {
|
||||
|
@ -387,6 +563,13 @@ public class MetarPrecipDataContainer {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* combine all maps so there is only one entry for each station. For
|
||||
* stations in multiple maps the entry from the first map is used.
|
||||
*
|
||||
* @param maps
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PrecipData> combine(Map<String, PrecipData>... maps) {
|
||||
Map<String, PrecipData> result = new HashMap<String, PrecipData>();
|
||||
List<Map<String, PrecipData>> mapsList = new ArrayList<Map<String, PrecipData>>(
|
||||
|
|
Loading…
Add table
Reference in a new issue