From a02a7a90c75ae7fa418e3439e2b6df95b21c644b Mon Sep 17 00:00:00 2001 From: Brian Clements Date: Tue, 10 Jun 2014 13:31:31 -0500 Subject: [PATCH] Omaha #3226 added filter support for lightning data Change-Id: Ie5b90aeb3662c01a637803b604ae1488140102a4 Former-commit-id: 6b2a618685dad201b1d88abc132d4a5e31251f58 --- .../META-INF/MANIFEST.MF | 10 + .../binlightning/BinLightningDecoder.java | 4 +- .../binlightning/filter/GeoFilterBbox.java | 136 +++++++++ .../filter/GeoFilterException.java | 70 +++++ .../binlightning/filter/GeoFilterParser.java | 275 ++++++++++++++++++ .../binlightning/filter/GeoFilterResult.java | 90 ++++++ .../binlightning/filter/GeoFilters.java | 127 ++++++++ .../filter/LightningGeoFilter.java | 232 +++++++++++++++ .../total/TotalLightningDecoder.java | 5 +- .../base/binlightning/filters/example.xml | 15 + .../textlightning/TextLightningDecoder.java | 11 +- .../binlightning/BinLightningRecord.java | 31 +- 12 files changed, 983 insertions(+), 23 deletions(-) create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java create mode 100644 edexOsgi/com.raytheon.edex.plugin.binlightning/utility/edex_static/base/binlightning/filters/example.xml diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF index 0929fedcd6..a348a72729 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/META-INF/MANIFEST.MF @@ -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", diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java index 672eb5c438..7785b49f62 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/BinLightningDecoder.java @@ -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 * * * @@ -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]; } diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java new file mode 100644 index 0000000000..a801e56c8f --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterBbox.java @@ -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 + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 11, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @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; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java new file mode 100644 index 0000000000..7b1522c171 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterException.java @@ -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 + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 10, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @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); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java new file mode 100644 index 0000000000..85cfb5eeae --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterParser.java @@ -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. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 10, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @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 geoms = new ArrayList(); + List errors = new ArrayList(0); + List bboxes = filters.getBboxes(); + if (bboxes != null && !bboxes.isEmpty()) { + for (GeoFilterBbox bbox : bboxes) { + try { + geoms.addAll(convertBbox(bbox)); + } catch (GeoFilterException e) { + errors.add(e); + } + } + } + List cwas = filters.getCwas(); + if (cwas != null && !cwas.isEmpty()) { + for (String cwa : cwas) { + try { + geoms.addAll(getCWA(cwa)); + } catch (GeoFilterException e) { + errors.add(e); + } + } + } + List geometries = filters.getGeometries(); + if (geometries != null) { + geoms.addAll(geometries); + } + List rval = new ArrayList( + 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 getCWA(String cwa) + throws GeoFilterException { + Map map = new HashMap(); + 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 rval = new ArrayList(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 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 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); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java new file mode 100644 index 0000000000..97572d1620 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilterResult.java @@ -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. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 10, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class GeoFilterResult { + + private final Collection errors; + + private final Collection filters; + + + /** + * @param filters + */ + public GeoFilterResult(Collection filters) { + this(filters, Collections. emptyList()); + } + + /** + * @param filters + * @param errors + */ + public GeoFilterResult(Collection filters, + Collection 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 getErrors() { + return errors; + } + + /** + * @return the filters + */ + public Collection getFilters() { + return filters; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java new file mode 100644 index 0000000000..3d6df4bafd --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/GeoFilters.java @@ -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 + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 11, 2014 3226      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +@XmlRootElement(name = "filters") +@XmlAccessorType(XmlAccessType.NONE) +public class GeoFilters { + + @XmlElement(name = "wkt") + @XmlJavaTypeAdapter(value = GeometryAdapter.class) + private List geometries; + + @XmlElement(name = "cwa") + private List cwas; + + @XmlElement(name = "bbox") + private List bboxes; + + /** + * + */ + public GeoFilters() { + } + + /** + * @param geometries + * @param cwas + * @param bboxes + */ + public GeoFilters(List geometries, + List cwas, + List bboxes) { + this.geometries = geometries; + this.cwas = cwas; + this.bboxes = bboxes; + } + + /** + * @return the geometries + */ + public List getGeometries() { + return geometries; + } + + /** + * @param geometries + * the geometries to set + */ + public void setGeometries(List geometries) { + this.geometries = geometries; + } + + /** + * @return the cwas + */ + public List getCwas() { + return cwas; + } + + /** + * @param cwas + * the cwas to set + */ + public void setCwas(List cwas) { + this.cwas = cwas; + } + + /** + * @return the bboxes + */ + public List getBboxes() { + return bboxes; + } + + /** + * @param bboxes + * the bboxes to set + */ + public void setBboxes(List bboxes) { + this.bboxes = bboxes; + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java new file mode 100644 index 0000000000..7178852501 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/filter/LightningGeoFilter.java @@ -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. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 10, 2014 3226       bclement     Initial creation
+ * 
+ * 
+ * + * @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> geometryMap = new ConcurrentHashMap>( + 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 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 getFilterGeometries( + LocalizationFile file) { + Collection 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 filterStrikes( + Collection strikes) { + if (!initialized) { + initialize(); + } + Collection rval = strikes; + if (!strikes.isEmpty()) { + LightningStrikePoint sample = strikes.iterator().next(); + String source = BinLightningRecord.getDataSource(sample); + Collection filter = geometryMap.get(source + .toLowerCase()); + if (filter != null && !filter.isEmpty()) { + rval = new ArrayList(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 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 strikes) { + return new BinLightningRecord(filterStrikes(strikes)); + } + +} diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java index 7f54f2a030..2207fd60aa 100644 --- a/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/src/com/raytheon/edex/plugin/binlightning/total/TotalLightningDecoder.java @@ -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 * * * @@ -193,7 +195,8 @@ public class TotalLightningDecoder { } List 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]; diff --git a/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/edex_static/base/binlightning/filters/example.xml b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/edex_static/base/binlightning/filters/example.xml new file mode 100644 index 0000000000..211e0b8446 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.binlightning/utility/edex_static/base/binlightning/filters/example.xml @@ -0,0 +1,15 @@ + + + + + + OAX + DMX + FSD + + POLYGON ((-101.24 41.4, -100.6 41.15, -101.01 40.75, -101.24 41.4)) + \ No newline at end of file diff --git a/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java b/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java index 6e772c6224..c85564bf5d 100644 --- a/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java +++ b/edexOsgi/com.raytheon.edex.plugin.textlightning/src/com/raytheon/edex/plugin/textlightning/TextLightningDecoder.java @@ -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 * * * @@ -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 }; } diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java index ead40b953a..f81f21d55c 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java @@ -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 * * * @@ -162,7 +164,7 @@ public class BinLightningRecord extends PersistablePluginDataObject implements * * @param strikes */ - public BinLightningRecord(final List strikes) { + public BinLightningRecord(final Collection 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 strikes, + private void setPulseData(final Collection 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(); } }