Omaha #3226 added filter support for lightning data

Change-Id: Ie5b90aeb3662c01a637803b604ae1488140102a4

Former-commit-id: 6b2a618685dad201b1d88abc132d4a5e31251f58
This commit is contained in:
Brian Clements 2014-06-10 13:31:31 -05:00
parent 9b73096d46
commit a02a7a90c7
12 changed files with 983 additions and 23 deletions

View file

@ -9,9 +9,19 @@ Export-Package: com.raytheon.edex.plugin.binlightning.dao
Import-Package: com.raytheon.edex.esb, Import-Package: com.raytheon.edex.esb,
com.raytheon.edex.exception, com.raytheon.edex.exception,
com.raytheon.edex.plugin, 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.numeric,
com.raytheon.uf.common.serialization,
com.raytheon.uf.common.serialization.adapters,
com.raytheon.uf.common.status, com.raytheon.uf.common.status,
com.raytheon.uf.common.wmo, 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, gov.noaa.nws.ost.edex.plugin.binlightning,
org.apache.commons.logging org.apache.commons.logging
Require-Bundle: com.raytheon.uf.common.dataplugin.binlightning;bundle-version="1.12.1174", Require-Bundle: com.raytheon.uf.common.dataplugin.binlightning;bundle-version="1.12.1174",

View file

@ -37,6 +37,7 @@ import javax.crypto.IllegalBlockSizeException;
import com.raytheon.edex.esb.Headers; import com.raytheon.edex.esb.Headers;
import com.raytheon.edex.exception.DecoderException; import com.raytheon.edex.exception.DecoderException;
import com.raytheon.edex.plugin.AbstractDecoder; 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.BinLightningFactory;
import com.raytheon.edex.plugin.binlightning.impl.IBinLightningDecoder; import com.raytheon.edex.plugin.binlightning.impl.IBinLightningDecoder;
import com.raytheon.edex.plugin.binlightning.impl.LightningDataSource; 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 * added decodeBinLightningData() and decodeBitShiftedBinLightningData() from BinLightningDecoderUtil
* Jun 05, 2014 3226 bclement LightningStikePoint refactor, added extractPData() * Jun 05, 2014 3226 bclement LightningStikePoint refactor, added extractPData()
* Jun 09, 2014 3226 bclement moved data array decrypt prep to EncryptedBinLightingCipher * Jun 09, 2014 3226 bclement moved data array decrypt prep to EncryptedBinLightingCipher
* Jun 10, 2014 3226 bclement added filter support
* *
* </pre> * </pre>
* *
@ -190,7 +192,7 @@ public class BinLightningDecoder extends AbstractDecoder {
// post processing data - if not keep-alive record // post processing data - if not keep-alive record
BinLightningRecord report = null; BinLightningRecord report = null;
if (strikes.size() > 0) { if (strikes.size() > 0) {
report = new BinLightningRecord(strikes); report = LightningGeoFilter.createFilteredRecord(strikes);
} else { } else {
return new PluginDataObject[0]; return new PluginDataObject[0];
} }

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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));
}
}

View file

@ -30,6 +30,7 @@ import java.util.List;
import com.raytheon.edex.esb.Headers; import com.raytheon.edex.esb.Headers;
import com.raytheon.edex.exception.DecoderException; import com.raytheon.edex.exception.DecoderException;
import com.raytheon.edex.plugin.binlightning.BinLightningDecoder; 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.PluginDataObject;
import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord; import com.raytheon.uf.common.dataplugin.binlightning.BinLightningRecord;
import com.raytheon.uf.common.dataplugin.binlightning.impl.BaseLightningPoint; 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 * May 30, 2014 3226 bclement Initial creation
* Jun 09, 2014 3226 bclement added encryption support * Jun 09, 2014 3226 bclement added encryption support
* Jun 10, 2014 3226 bclement added filter support
* *
* </pre> * </pre>
* *
@ -193,7 +195,8 @@ public class TotalLightningDecoder {
} }
List<LightningStrikePoint> strikes = decodeStrikes(fileName, pdata); List<LightningStrikePoint> strikes = decodeStrikes(fileName, pdata);
if (!strikes.isEmpty()) { if (!strikes.isEmpty()) {
BinLightningRecord record = new BinLightningRecord(strikes); BinLightningRecord record = LightningGeoFilter
.createFilteredRecord(strikes);
return new PluginDataObject[] { record }; return new PluginDataObject[] { record };
} else { } else {
return new PluginDataObject[0]; return new PluginDataObject[0];

View file

@ -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>

View file

@ -44,6 +44,7 @@ import com.raytheon.uf.common.time.util.TimeUtil;
* Aug 30, 2013 2298 rjpeter Make getPluginName abstract * Aug 30, 2013 2298 rjpeter Make getPluginName abstract
* Feb 12, 2014 2655 njensen Set source * Feb 12, 2014 2655 njensen Set source
* Jun 05, 2014 3226 bclement LightningStikePoint refactor * Jun 05, 2014 3226 bclement LightningStikePoint refactor
* Jun 10, 2014 3226 bclement fixed source
* *
* </pre> * </pre>
* *
@ -56,6 +57,12 @@ public class TextLightningDecoder extends AbstractDecoder implements
private String traceId = null; 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 * Construct a TextLightning decoder. Calling hasNext() after construction
* will return false, decode() will return a null. * will return false, decode() will return a null.
@ -96,9 +103,7 @@ public class TextLightningDecoder extends AbstractDecoder implements
report.setTraceId(traceId); report.setTraceId(traceId);
// TODO anyone have any idea what the source should actually be report.setSource(SOURCE);
// named?
report.setSource("text");
return new PluginDataObject[] { report }; return new PluginDataObject[] { report };
} }

View file

@ -21,6 +21,7 @@ package com.raytheon.uf.common.dataplugin.binlightning;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -88,6 +89,7 @@ import com.raytheon.uf.common.time.util.TimeUtil;
* May 14, 2014 2536 bclement removed TimeTools usage * May 14, 2014 2536 bclement removed TimeTools usage
* Jun 05, 2014 3226 bclement moved data arrays into map for easier management * Jun 05, 2014 3226 bclement moved data arrays into map for easier management
* replaced addStrike() with List constructor, added pulses * replaced addStrike() with List constructor, added pulses
* Jun 10, 2014 3226 bclement collections instead of lists, made data source logic public
* *
* </pre> * </pre>
* *
@ -162,7 +164,7 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
* *
* @param strikes * @param strikes
*/ */
public BinLightningRecord(final List<LightningStrikePoint> strikes) { public BinLightningRecord(final Collection<LightningStrikePoint> strikes) {
final int arraysSize = strikes.size(); final int arraysSize = strikes.size();
long[] obsTimes = new long[arraysSize]; long[] obsTimes = new long[arraysSize];
float[] latitudes = new float[arraysSize]; float[] latitudes = new float[arraysSize];
@ -191,8 +193,8 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
sensorCounts); sensorCounts);
if (arraysSize > 0) { if (arraysSize > 0) {
LightningStrikePoint sample = strikes.get(0); LightningStrikePoint sample = strikes.iterator().next();
setDataSource(sample); this.source = getDataSource(sample);
} }
long startTimeMillis = Long.MAX_VALUE; long startTimeMillis = Long.MAX_VALUE;
@ -248,7 +250,7 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
* @param pulseDataCount * @param pulseDataCount
* total number of pulses for all strikes * total number of pulses for all strikes
*/ */
private void setPulseData(final List<LightningStrikePoint> strikes, private void setPulseData(final Collection<LightningStrikePoint> strikes,
final int pulseDataCount) { final int pulseDataCount) {
long[] pulseTimes = new long[pulseDataCount]; long[] pulseTimes = new long[pulseDataCount];
float[] pulseLats = new float[pulseDataCount]; float[] pulseLats = new float[pulseDataCount];
@ -292,22 +294,15 @@ public class BinLightningRecord extends PersistablePluginDataObject implements
* Extract data source from strike * Extract data source from strike
* *
* @param strike * @param strike
* @return
*/ */
private void setDataSource(LightningStrikePoint strike) { public static String getDataSource(LightningStrikePoint strike) {
if (source == null) {
if (strike.getLightSource() == null) { if (strike.getLightSource() == null) {
source = "NLDN"; return "NLDN";
} else if (strike.getLightSource().isEmpty()) { } else if (strike.getLightSource().isEmpty()) {
source = "UNKN"; return "UNKN";
} else { } else {
source = strike.getLightSource(); return strike.getLightSource();
}
} else {
if (strike.getLightSource() == null) {
source = "NLDN";
} else if (!source.equals(strike.getLightSource())) {
source = "UNKN";
}
} }
} }