Omaha #3226 added filter support for lightning data
Change-Id: Ie5b90aeb3662c01a637803b604ae1488140102a4 Former-commit-id:acc77a71b2
[formerlya02a7a90c7
[formerly 6b2a618685dad201b1d88abc132d4a5e31251f58]] Former-commit-id:a02a7a90c7
Former-commit-id:a3da85d395
This commit is contained in:
parent
cab065ff19
commit
2644ce312a
12 changed files with 983 additions and 23 deletions
|
@ -9,9 +9,19 @@ Export-Package: com.raytheon.edex.plugin.binlightning.dao
|
|||
Import-Package: com.raytheon.edex.esb,
|
||||
com.raytheon.edex.exception,
|
||||
com.raytheon.edex.plugin,
|
||||
com.raytheon.uf.common.dataquery.requests,
|
||||
com.raytheon.uf.common.geospatial,
|
||||
com.raytheon.uf.common.localization,
|
||||
com.raytheon.uf.common.localization.exception,
|
||||
com.raytheon.uf.common.numeric,
|
||||
com.raytheon.uf.common.serialization,
|
||||
com.raytheon.uf.common.serialization.adapters,
|
||||
com.raytheon.uf.common.status,
|
||||
com.raytheon.uf.common.wmo,
|
||||
com.vividsolutions.jts,
|
||||
com.vividsolutions.jts.geom,
|
||||
com.vividsolutions.jts.geom.prep,
|
||||
com.vividsolutions.jts.io,
|
||||
gov.noaa.nws.ost.edex.plugin.binlightning,
|
||||
org.apache.commons.logging
|
||||
Require-Bundle: com.raytheon.uf.common.dataplugin.binlightning;bundle-version="1.12.1174",
|
||||
|
|
|
@ -37,6 +37,7 @@ import javax.crypto.IllegalBlockSizeException;
|
|||
import com.raytheon.edex.esb.Headers;
|
||||
import com.raytheon.edex.exception.DecoderException;
|
||||
import com.raytheon.edex.plugin.AbstractDecoder;
|
||||
import com.raytheon.edex.plugin.binlightning.filter.LightningGeoFilter;
|
||||
import com.raytheon.edex.plugin.binlightning.impl.BinLightningFactory;
|
||||
import com.raytheon.edex.plugin.binlightning.impl.IBinLightningDecoder;
|
||||
import com.raytheon.edex.plugin.binlightning.impl.LightningDataSource;
|
||||
|
@ -86,6 +87,7 @@ import com.raytheon.uf.edex.decodertools.core.IBinDataSource;
|
|||
* added decodeBinLightningData() and decodeBitShiftedBinLightningData() from BinLightningDecoderUtil
|
||||
* Jun 05, 2014 3226 bclement LightningStikePoint refactor, added extractPData()
|
||||
* Jun 09, 2014 3226 bclement moved data array decrypt prep to EncryptedBinLightingCipher
|
||||
* Jun 10, 2014 3226 bclement added filter support
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -190,7 +192,7 @@ public class BinLightningDecoder extends AbstractDecoder {
|
|||
// post processing data - if not keep-alive record
|
||||
BinLightningRecord report = null;
|
||||
if (strikes.size() > 0) {
|
||||
report = new BinLightningRecord(strikes);
|
||||
report = LightningGeoFilter.createFilteredRecord(strikes);
|
||||
} else {
|
||||
return new PluginDataObject[0];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.edex.plugin.binlightning.filter;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
|
||||
/**
|
||||
* JAXB POJO for filter bounding box
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 11, 2014 3226 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bclement
|
||||
* @version 1.0
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.NONE)
|
||||
public class GeoFilterBbox {
|
||||
|
||||
@XmlAttribute(required = true)
|
||||
private double minx;
|
||||
|
||||
@XmlAttribute(required = true)
|
||||
private double maxx;
|
||||
|
||||
@XmlAttribute(required = true)
|
||||
private double miny;
|
||||
|
||||
@XmlAttribute(required = true)
|
||||
private double maxy;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public GeoFilterBbox() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minx
|
||||
* @param maxx
|
||||
* @param miny
|
||||
* @param maxy
|
||||
*/
|
||||
public GeoFilterBbox(double minx, double maxx, double miny, double maxy) {
|
||||
this.minx = minx;
|
||||
this.maxx = maxx;
|
||||
this.miny = miny;
|
||||
this.maxy = maxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minx
|
||||
*/
|
||||
public double getMinx() {
|
||||
return minx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minx
|
||||
* the minx to set
|
||||
*/
|
||||
public void setMinx(double minx) {
|
||||
this.minx = minx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maxx
|
||||
*/
|
||||
public double getMaxx() {
|
||||
return maxx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxx
|
||||
* the maxx to set
|
||||
*/
|
||||
public void setMaxx(double maxx) {
|
||||
this.maxx = maxx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the miny
|
||||
*/
|
||||
public double getMiny() {
|
||||
return miny;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param miny
|
||||
* the miny to set
|
||||
*/
|
||||
public void setMiny(double miny) {
|
||||
this.miny = miny;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maxy
|
||||
*/
|
||||
public double getMaxy() {
|
||||
return maxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxy
|
||||
* the maxy to set
|
||||
*/
|
||||
public void setMaxy(double maxy) {
|
||||
this.maxy = maxy;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.edex.plugin.binlightning.filter;
|
||||
|
||||
/**
|
||||
* Exception thrown during lightning filter config parsing
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 10, 2014 3226 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bclement
|
||||
* @version 1.0
|
||||
*/
|
||||
public class GeoFilterException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -7455324971125247083L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public GeoFilterException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
*/
|
||||
public GeoFilterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cause
|
||||
*/
|
||||
public GeoFilterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @param cause
|
||||
*/
|
||||
public GeoFilterException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.edex.plugin.binlightning.filter;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
|
||||
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
|
||||
import com.raytheon.uf.common.geospatial.SpatialException;
|
||||
import com.raytheon.uf.common.geospatial.SpatialQueryFactory;
|
||||
import com.raytheon.uf.common.geospatial.SpatialQueryResult;
|
||||
import com.raytheon.uf.common.serialization.JAXBManager;
|
||||
import com.raytheon.uf.common.serialization.SerializationException;
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
import com.vividsolutions.jts.geom.GeometryFactory;
|
||||
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
|
||||
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
|
||||
|
||||
/**
|
||||
* Parses geographic filter configuration files. Example file located in utility
|
||||
* directory.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 10, 2014 3226 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bclement
|
||||
* @version 1.0
|
||||
*/
|
||||
public class GeoFilterParser {
|
||||
|
||||
private static final double LAT_MIN = -90;
|
||||
|
||||
private static final double LAT_MAX = 90;
|
||||
|
||||
private static final double LON_MIN = -180;
|
||||
|
||||
private static final double LON_MAX = 180;
|
||||
|
||||
/* CWA database constants */
|
||||
|
||||
private static final String CWA_TABLE = "cwa";
|
||||
|
||||
private static final String CWA_COLUMN = "cwa";
|
||||
|
||||
/* helper factories */
|
||||
|
||||
private static JAXBManager jaxbManager;
|
||||
|
||||
private static volatile boolean initialized = false;
|
||||
|
||||
private static final GeometryFactory GEOM_FACTORY = new GeometryFactory();
|
||||
|
||||
/**
|
||||
* initialize jaxb manager
|
||||
*
|
||||
* @throws JAXBException
|
||||
*/
|
||||
private static synchronized void initialize() throws JAXBException {
|
||||
if (!initialized) {
|
||||
jaxbManager = new JAXBManager(GeoFilters.class);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse filter config file from input stream. Does not close input stream.
|
||||
* Any non-fatal parsing errors are returned in result.
|
||||
*
|
||||
* @param in
|
||||
* @return
|
||||
* @throws JAXBException
|
||||
* @throws SerializationException
|
||||
*/
|
||||
public static GeoFilterResult parse(InputStream in) throws JAXBException,
|
||||
SerializationException {
|
||||
if (!initialized) {
|
||||
initialize();
|
||||
}
|
||||
Object obj = jaxbManager.unmarshalFromInputStream(in);
|
||||
if (obj instanceof GeoFilters) {
|
||||
return parseInternal((GeoFilters) obj);
|
||||
} else {
|
||||
throw new SerializationException("Unexpected XML object type: "
|
||||
+ obj.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #parse
|
||||
* @param filters
|
||||
* jaxb pojo
|
||||
* @return
|
||||
* @throws XMLStreamException
|
||||
*/
|
||||
private static GeoFilterResult parseInternal(GeoFilters filters) {
|
||||
List<Geometry> geoms = new ArrayList<Geometry>();
|
||||
List<GeoFilterException> errors = new ArrayList<GeoFilterException>(0);
|
||||
List<GeoFilterBbox> bboxes = filters.getBboxes();
|
||||
if (bboxes != null && !bboxes.isEmpty()) {
|
||||
for (GeoFilterBbox bbox : bboxes) {
|
||||
try {
|
||||
geoms.addAll(convertBbox(bbox));
|
||||
} catch (GeoFilterException e) {
|
||||
errors.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<String> cwas = filters.getCwas();
|
||||
if (cwas != null && !cwas.isEmpty()) {
|
||||
for (String cwa : cwas) {
|
||||
try {
|
||||
geoms.addAll(getCWA(cwa));
|
||||
} catch (GeoFilterException e) {
|
||||
errors.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Geometry> geometries = filters.getGeometries();
|
||||
if (geometries != null) {
|
||||
geoms.addAll(geometries);
|
||||
}
|
||||
List<PreparedGeometry> rval = new ArrayList<PreparedGeometry>(
|
||||
geoms.size());
|
||||
for (Geometry geom : geoms) {
|
||||
rval.add(PreparedGeometryFactory.prepare(geom));
|
||||
}
|
||||
return new GeoFilterResult(rval, errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get county warning area geometries by name. Performs a database lookup.
|
||||
*
|
||||
* @param cwa
|
||||
* @return
|
||||
* @throws GeoFilterException
|
||||
*/
|
||||
private static Collection<Geometry> getCWA(String cwa)
|
||||
throws GeoFilterException {
|
||||
Map<String, RequestConstraint> map = new HashMap<String, RequestConstraint>();
|
||||
map.put(CWA_COLUMN, new RequestConstraint(cwa));
|
||||
SpatialQueryResult[] results;
|
||||
try {
|
||||
results = SpatialQueryFactory.create().query(CWA_TABLE, null, null,
|
||||
map, null);
|
||||
} catch (SpatialException e) {
|
||||
throw new GeoFilterException("Unable to query database for CWA: "
|
||||
+ cwa, e);
|
||||
}
|
||||
if (results == null || results.length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Geometry> rval = new ArrayList<Geometry>(results.length);
|
||||
for (SpatialQueryResult result : results) {
|
||||
rval.add(result.geometry);
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate bounding box coordinates in attributes. Converts bounding box to
|
||||
* polygon.
|
||||
*
|
||||
* @param bbox
|
||||
* @return
|
||||
*/
|
||||
private static Collection<Geometry> convertBbox(GeoFilterBbox bbox)
|
||||
throws GeoFilterException {
|
||||
|
||||
Coordinate upper = new Coordinate(bbox.getMaxx(), bbox.getMaxy());
|
||||
Coordinate lower = new Coordinate(bbox.getMinx(), bbox.getMiny());
|
||||
|
||||
if (lower.x > upper.x || lower.y > upper.y) {
|
||||
throw new GeoFilterException(
|
||||
"Invalid bounding box tag. Minimum coordinate values "
|
||||
+ "cannot be larger than maximum coordinate values: "
|
||||
+ lower + " " + upper);
|
||||
}
|
||||
|
||||
if (lower.y < LAT_MIN || upper.y > LAT_MAX) {
|
||||
throw new GeoFilterException(
|
||||
"Invalid bounding box tag. Box cannot cross poles: "
|
||||
+ lower.y + "," + upper.y);
|
||||
}
|
||||
|
||||
List<Geometry> rval = null;
|
||||
lower.x = normalizeLon(lower.x);
|
||||
upper.x = normalizeLon(upper.x);
|
||||
/*
|
||||
* if normalization switched order, it means it crossed the antimeridian
|
||||
* and needs to be split into two boxes
|
||||
*/
|
||||
if (lower.x > upper.x) {
|
||||
/* create a new box on the left of the map */
|
||||
Coordinate leftLower = new Coordinate(LON_MIN, lower.y);
|
||||
Coordinate leftUpper = new Coordinate(upper.x, upper.y);
|
||||
/* change the old box to only cover the right of the map */
|
||||
upper.x = LON_MAX;
|
||||
Geometry left = createPolygon(leftLower, leftUpper);
|
||||
Geometry right = createPolygon(lower, upper);
|
||||
rval = Arrays.asList(left, right);
|
||||
} else {
|
||||
rval = Arrays.asList(createPolygon(lower, upper));
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lon
|
||||
* @return longitude normalized to be between -180 and 180
|
||||
*/
|
||||
private static double normalizeLon(double lon) {
|
||||
/* 360 degrees */
|
||||
final double LON_TOTAL = LON_MAX * 2;
|
||||
/* account for any far out degrees */
|
||||
double rval = lon % LON_TOTAL;
|
||||
/* subtract or add to get in range */
|
||||
if (rval > LON_MAX) {
|
||||
rval -= LON_TOTAL;
|
||||
} else if (rval < LON_MIN) {
|
||||
rval += LON_TOTAL;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mins
|
||||
* @param maxes
|
||||
* @return polygon defining a bounding box using corner points
|
||||
*/
|
||||
private static Geometry createPolygon(Coordinate mins, Coordinate maxes) {
|
||||
Coordinate[] coordinates = new Coordinate[5];
|
||||
coordinates[0] = new Coordinate(mins.x, mins.y);
|
||||
coordinates[1] = new Coordinate(mins.x, maxes.y);
|
||||
coordinates[2] = new Coordinate(maxes.x, maxes.y);
|
||||
coordinates[3] = new Coordinate(maxes.x, mins.y);
|
||||
/* polygons need a closed ring */
|
||||
coordinates[4] = coordinates[0];
|
||||
return GEOM_FACTORY.createPolygon(coordinates);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.edex.plugin.binlightning.filter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
|
||||
|
||||
/**
|
||||
* Geographic filter parsing results. Wraps parsed data and any parsing errors.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 10, 2014 3226 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bclement
|
||||
* @version 1.0
|
||||
*/
|
||||
public class GeoFilterResult {
|
||||
|
||||
private final Collection<GeoFilterException> errors;
|
||||
|
||||
private final Collection<PreparedGeometry> filters;
|
||||
|
||||
|
||||
/**
|
||||
* @param filters
|
||||
*/
|
||||
public GeoFilterResult(Collection<PreparedGeometry> filters) {
|
||||
this(filters, Collections.<GeoFilterException> emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filters
|
||||
* @param errors
|
||||
*/
|
||||
public GeoFilterResult(Collection<PreparedGeometry> filters,
|
||||
Collection<GeoFilterException> errors) {
|
||||
this.filters = filters;
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if any errors were encountered during parsing
|
||||
* @see #getErrors()
|
||||
*/
|
||||
public boolean hasErrors() {
|
||||
return !errors.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the errors
|
||||
* @see #hasErrors()
|
||||
*/
|
||||
public Collection<GeoFilterException> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the filters
|
||||
*/
|
||||
public Collection<PreparedGeometry> getFilters() {
|
||||
return filters;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.edex.plugin.binlightning.filter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
import com.raytheon.uf.common.serialization.adapters.GeometryAdapter;
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
|
||||
/**
|
||||
* Root JAXB POJO for geographic filter configuration
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 11, 2014 3226 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bclement
|
||||
* @version 1.0
|
||||
*/
|
||||
@XmlRootElement(name = "filters")
|
||||
@XmlAccessorType(XmlAccessType.NONE)
|
||||
public class GeoFilters {
|
||||
|
||||
@XmlElement(name = "wkt")
|
||||
@XmlJavaTypeAdapter(value = GeometryAdapter.class)
|
||||
private List<Geometry> geometries;
|
||||
|
||||
@XmlElement(name = "cwa")
|
||||
private List<String> cwas;
|
||||
|
||||
@XmlElement(name = "bbox")
|
||||
private List<GeoFilterBbox> bboxes;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public GeoFilters() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param geometries
|
||||
* @param cwas
|
||||
* @param bboxes
|
||||
*/
|
||||
public GeoFilters(List<Geometry> geometries,
|
||||
List<String> cwas,
|
||||
List<GeoFilterBbox> bboxes) {
|
||||
this.geometries = geometries;
|
||||
this.cwas = cwas;
|
||||
this.bboxes = bboxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the geometries
|
||||
*/
|
||||
public List<Geometry> getGeometries() {
|
||||
return geometries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param geometries
|
||||
* the geometries to set
|
||||
*/
|
||||
public void setGeometries(List<Geometry> geometries) {
|
||||
this.geometries = geometries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cwas
|
||||
*/
|
||||
public List<String> getCwas() {
|
||||
return cwas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cwas
|
||||
* the cwas to set
|
||||
*/
|
||||
public void setCwas(List<String> cwas) {
|
||||
this.cwas = cwas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bboxes
|
||||
*/
|
||||
public List<GeoFilterBbox> getBboxes() {
|
||||
return bboxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bboxes
|
||||
* the bboxes to set
|
||||
*/
|
||||
public void setBboxes(List<GeoFilterBbox> bboxes) {
|
||||
this.bboxes = bboxes;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.edex.plugin.binlightning.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
|
||||
import com.raytheon.uf.common.dataplugin.binlightning.impl.LightningStrikePoint;
|
||||
import com.raytheon.uf.common.localization.IPathManager;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile;
|
||||
import com.raytheon.uf.common.localization.PathManagerFactory;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationException;
|
||||
import com.raytheon.uf.common.serialization.SerializationException;
|
||||
import com.raytheon.uf.common.status.IUFStatusHandler;
|
||||
import com.raytheon.uf.common.status.UFStatus;
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
import com.vividsolutions.jts.geom.GeometryFactory;
|
||||
import com.vividsolutions.jts.geom.Point;
|
||||
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
|
||||
|
||||
/**
|
||||
* Geographic filtering utility for lightning data. Configured in localization.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 10, 2014 3226 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bclement
|
||||
* @version 1.0
|
||||
* @see {@link GeoFilterParser}
|
||||
*/
|
||||
public class LightningGeoFilter {
|
||||
|
||||
private static final IUFStatusHandler log = UFStatus
|
||||
.getHandler(LightningGeoFilter.class);
|
||||
|
||||
public static final String LOCALIZATION_FILTER_DIR = "binlightning/filters";
|
||||
|
||||
private static final GeometryFactory GEOM_FACTORY = new GeometryFactory();
|
||||
|
||||
/**
|
||||
* map of lightning source names to filter geometries
|
||||
*/
|
||||
private static final Map<String, Collection<PreparedGeometry>> geometryMap = new ConcurrentHashMap<String, Collection<PreparedGeometry>>(
|
||||
4);
|
||||
|
||||
private static volatile boolean initialized = false;
|
||||
|
||||
/**
|
||||
* read filter configuration files from localization. Threadsafe and
|
||||
* idempotent
|
||||
*/
|
||||
private static void initialize() {
|
||||
synchronized (geometryMap) {
|
||||
if (!initialized) {
|
||||
IPathManager pathMgr = PathManagerFactory.getPathManager();
|
||||
|
||||
LocalizationFile[] files = pathMgr.listFiles(pathMgr
|
||||
.getLocalSearchHierarchy(LocalizationType.EDEX_STATIC),
|
||||
LOCALIZATION_FILTER_DIR, new String[] { ".xml" }, true,
|
||||
true);
|
||||
|
||||
for (LocalizationFile file : files) {
|
||||
Collection<PreparedGeometry> filters = getFilterGeometries(file);
|
||||
if (!filters.isEmpty()) {
|
||||
String bareName = getFileNameWithoutExtension(file
|
||||
.getName());
|
||||
geometryMap.put(bareName.toLowerCase(), filters);
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse filter geometries from filter config file
|
||||
*
|
||||
* @param file
|
||||
* @return empty list on error
|
||||
*/
|
||||
private static Collection<PreparedGeometry> getFilterGeometries(
|
||||
LocalizationFile file) {
|
||||
Collection<PreparedGeometry> rval;
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = file.openInputStream();
|
||||
GeoFilterResult result = GeoFilterParser.parse(in);
|
||||
if (result.hasErrors()) {
|
||||
for (GeoFilterException e : result.getErrors()) {
|
||||
log.error(e.getLocalizedMessage(), e);
|
||||
}
|
||||
log.warn("Filter parsing included errors, filters will be incomplete");
|
||||
}
|
||||
return result.getFilters();
|
||||
} catch (JAXBException e) {
|
||||
log.error("Unable to parse filter file: " + file.getName(), e);
|
||||
rval = Collections.emptyList();
|
||||
} catch (LocalizationException e) {
|
||||
log.error("Unable to open filter file: " + file.getName(), e);
|
||||
rval = Collections.emptyList();
|
||||
} catch (SerializationException e) {
|
||||
log.error("Unable to parse filter file: " + file.getName(), e);
|
||||
rval = Collections.emptyList();
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
log.error(
|
||||
"Problem closing localization file: "
|
||||
+ file.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the bare file name from path. Removes path and file suffix (if
|
||||
* either exist)
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
private static String getFileNameWithoutExtension(String path) {
|
||||
int slashIndex = path.lastIndexOf(IPathManager.SEPARATOR);
|
||||
int start = slashIndex < 0 ? 0 : slashIndex + 1;
|
||||
int dotIndex = path.lastIndexOf('.');
|
||||
if (dotIndex < 0) {
|
||||
return path.substring(start);
|
||||
} else {
|
||||
return path.substring(start, dotIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies data source filter to strikes. Source is determined from sample
|
||||
* strike. If no filter exists for source, provided collection is returned.
|
||||
*
|
||||
* @param strikes
|
||||
* @return
|
||||
*/
|
||||
public static Collection<LightningStrikePoint> filterStrikes(
|
||||
Collection<LightningStrikePoint> strikes) {
|
||||
if (!initialized) {
|
||||
initialize();
|
||||
}
|
||||
Collection<LightningStrikePoint> rval = strikes;
|
||||
if (!strikes.isEmpty()) {
|
||||
LightningStrikePoint sample = strikes.iterator().next();
|
||||
String source = BinLightningRecord.getDataSource(sample);
|
||||
Collection<PreparedGeometry> filter = geometryMap.get(source
|
||||
.toLowerCase());
|
||||
if (filter != null && !filter.isEmpty()) {
|
||||
rval = new ArrayList<LightningStrikePoint>(strikes.size());
|
||||
for (LightningStrikePoint strike : strikes) {
|
||||
if (passesFilter(filter, strike)) {
|
||||
rval.add(strike);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filter
|
||||
* @param strike
|
||||
* @return true if strike is located in any of the filter geometries
|
||||
*/
|
||||
private static boolean passesFilter(Collection<PreparedGeometry> filter,
|
||||
LightningStrikePoint strike) {
|
||||
boolean passes = false;
|
||||
Point p = GEOM_FACTORY.createPoint(new Coordinate(
|
||||
strike.getLongitude(), strike.getLatitude()));
|
||||
for (PreparedGeometry g : filter) {
|
||||
/* covers instead of contains to include points at boundary */
|
||||
if (g.covers(p)) {
|
||||
passes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return passes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BinLightningRecord after applying data source filter to list of
|
||||
* strikes
|
||||
*
|
||||
* @see #filterStrikes(Collection)
|
||||
* @param strikes
|
||||
* @return
|
||||
*/
|
||||
public static BinLightningRecord createFilteredRecord(
|
||||
Collection<LightningStrikePoint> strikes) {
|
||||
return new BinLightningRecord(filterStrikes(strikes));
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ import java.util.List;
|
|||
import com.raytheon.edex.esb.Headers;
|
||||
import com.raytheon.edex.exception.DecoderException;
|
||||
import com.raytheon.edex.plugin.binlightning.BinLightningDecoder;
|
||||
import com.raytheon.edex.plugin.binlightning.filter.LightningGeoFilter;
|
||||
import com.raytheon.uf.common.dataplugin.PluginDataObject;
|
||||
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
|
||||
import com.raytheon.uf.common.dataplugin.binlightning.impl.BaseLightningPoint;
|
||||
|
@ -56,6 +57,7 @@ import com.raytheon.uf.common.wmo.WMOTimeParser;
|
|||
* ------------ ---------- ----------- --------------------------
|
||||
* May 30, 2014 3226 bclement Initial creation
|
||||
* Jun 09, 2014 3226 bclement added encryption support
|
||||
* Jun 10, 2014 3226 bclement added filter support
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -193,7 +195,8 @@ public class TotalLightningDecoder {
|
|||
}
|
||||
List<LightningStrikePoint> strikes = decodeStrikes(fileName, pdata);
|
||||
if (!strikes.isEmpty()) {
|
||||
BinLightningRecord record = new BinLightningRecord(strikes);
|
||||
BinLightningRecord record = LightningGeoFilter
|
||||
.createFilteredRecord(strikes);
|
||||
return new PluginDataObject[] { record };
|
||||
} else {
|
||||
return new PluginDataObject[0];
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<!-- example binlightning geospatial filter file
|
||||
name of file corresponds to data source (eg NLDN.xml)
|
||||
data is persisted if lightning strike point is in any of the filter areas
|
||||
if a data source does not have any filters, all data is persisted -->
|
||||
<filters>
|
||||
<!-- bounding box filter: x is degrees longitude, y is degrees latitude-->
|
||||
<bbox minx="-98.23" maxx="-97.24" miny="38.27" maxy="39.39" />
|
||||
<!-- CWA filters: correspond to polygons defining the county warning areas -->
|
||||
<cwa>OAX</cwa>
|
||||
<cwa>DMX</cwa>
|
||||
<cwa>FSD</cwa>
|
||||
<!-- well known text geometries: x coordinates are longitude, y coordinates are latitude
|
||||
first coordinate is always the same as last to close the polygon region -->
|
||||
<wkt>POLYGON ((-101.24 41.4, -100.6 41.15, -101.01 40.75, -101.24 41.4))</wkt>
|
||||
</filters>
|
|
@ -44,6 +44,7 @@ import com.raytheon.uf.common.time.util.TimeUtil;
|
|||
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract
|
||||
* Feb 12, 2014 2655 njensen Set source
|
||||
* Jun 05, 2014 3226 bclement LightningStikePoint refactor
|
||||
* Jun 10, 2014 3226 bclement fixed source
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -56,6 +57,12 @@ public class TextLightningDecoder extends AbstractDecoder implements
|
|||
|
||||
private String traceId = null;
|
||||
|
||||
/*
|
||||
* inferred from Wufeng Zhou comment in BinLightningDecoderUtil, stands for
|
||||
* World Wide Lightning Location Network
|
||||
*/
|
||||
private static final String SOURCE = "WWLLN";
|
||||
|
||||
/**
|
||||
* Construct a TextLightning decoder. Calling hasNext() after construction
|
||||
* will return false, decode() will return a null.
|
||||
|
@ -96,9 +103,7 @@ public class TextLightningDecoder extends AbstractDecoder implements
|
|||
|
||||
report.setTraceId(traceId);
|
||||
|
||||
// TODO anyone have any idea what the source should actually be
|
||||
// named?
|
||||
report.setSource("text");
|
||||
report.setSource(SOURCE);
|
||||
|
||||
return new PluginDataObject[] { report };
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package com.raytheon.uf.common.dataplugin.binlightning;
|
|||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -88,6 +89,7 @@ import com.raytheon.uf.common.time.util.TimeUtil;
|
|||
* May 14, 2014 2536 bclement removed TimeTools usage
|
||||
* Jun 05, 2014 3226 bclement moved data arrays into map for easier management
|
||||
* replaced addStrike() with List constructor, added pulses
|
||||
* Jun 10, 2014 3226 bclement collections instead of lists, made data source logic public
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -162,7 +164,7 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
|
|||
*
|
||||
* @param strikes
|
||||
*/
|
||||
public BinLightningRecord(final List<LightningStrikePoint> strikes) {
|
||||
public BinLightningRecord(final Collection<LightningStrikePoint> strikes) {
|
||||
final int arraysSize = strikes.size();
|
||||
long[] obsTimes = new long[arraysSize];
|
||||
float[] latitudes = new float[arraysSize];
|
||||
|
@ -191,8 +193,8 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
|
|||
sensorCounts);
|
||||
|
||||
if (arraysSize > 0) {
|
||||
LightningStrikePoint sample = strikes.get(0);
|
||||
setDataSource(sample);
|
||||
LightningStrikePoint sample = strikes.iterator().next();
|
||||
this.source = getDataSource(sample);
|
||||
}
|
||||
|
||||
long startTimeMillis = Long.MAX_VALUE;
|
||||
|
@ -248,7 +250,7 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
|
|||
* @param pulseDataCount
|
||||
* total number of pulses for all strikes
|
||||
*/
|
||||
private void setPulseData(final List<LightningStrikePoint> strikes,
|
||||
private void setPulseData(final Collection<LightningStrikePoint> strikes,
|
||||
final int pulseDataCount) {
|
||||
long[] pulseTimes = new long[pulseDataCount];
|
||||
float[] pulseLats = new float[pulseDataCount];
|
||||
|
@ -292,22 +294,15 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
|
|||
* Extract data source from strike
|
||||
*
|
||||
* @param strike
|
||||
* @return
|
||||
*/
|
||||
private void setDataSource(LightningStrikePoint strike) {
|
||||
if (source == null) {
|
||||
if (strike.getLightSource() == null) {
|
||||
source = "NLDN";
|
||||
} else if (strike.getLightSource().isEmpty()) {
|
||||
source = "UNKN";
|
||||
} else {
|
||||
source = strike.getLightSource();
|
||||
}
|
||||
public static String getDataSource(LightningStrikePoint strike) {
|
||||
if (strike.getLightSource() == null) {
|
||||
return "NLDN";
|
||||
} else if (strike.getLightSource().isEmpty()) {
|
||||
return "UNKN";
|
||||
} else {
|
||||
if (strike.getLightSource() == null) {
|
||||
source = "NLDN";
|
||||
} else if (!source.equals(strike.getLightSource())) {
|
||||
source = "UNKN";
|
||||
}
|
||||
return strike.getLightSource();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue