ASM #15658 - WarnGen: inclusion of watch information in marine products

Change-Id: I9f99e6136027f1a6419800f87e5a5709fe5be6b9

Former-commit-id: a2dfe8e8f6 [formerly a2dfe8e8f6 [formerly 6bfa1d6662b00925814be7126592bbe340651649]]
Former-commit-id: ea9230b37c
Former-commit-id: bc22f7309c
This commit is contained in:
David Friedman 2014-08-28 15:43:32 +00:00
parent a29865b064
commit bfc3fa98b4
9 changed files with 288 additions and 18 deletions

View file

@ -0,0 +1,99 @@
package com.raytheon.viz.warngen.gis;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.raytheon.uf.common.dataplugin.warning.util.WarnFileUtil;
import com.raytheon.uf.common.serialization.SingleTypeJAXBManager;
import com.raytheon.uf.viz.core.localization.LocalizationManager;
import com.raytheon.viz.warngen.gui.WarngenLayer;
/**
* WarngenWordingConfiguration
*
* <pre>
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- -------------- --------------------------
* 2014-08-28 ASM #15658 D. Friedman Initial Creation.
*/
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "zoneWordingConfig")
public class MarineWordingConfiguration {
private static final String FILE_NAME = "marineZoneWording.xml";
@XmlElement(name = "entry")
private List<MarineWordingEntry> entries = new ArrayList<MarineWordingEntry>();
public List<MarineWordingEntry> getEntries() {
return entries;
}
public void setEntries(List<MarineWordingEntry> entries) {
this.entries = entries;
}
@XmlAccessorType(XmlAccessType.NONE)
public static class MarineWordingEntry {
@XmlAttribute(name = "match")
private String matchText;
@XmlAttribute(name = "replace")
private String replacementText;
private Pattern ugcPattern;
public String getMatchText() {
return matchText;
}
public void setMatchText(String matchText) {
this.matchText = matchText;
this.ugcPattern = null;
}
public String getReplacementText() {
return replacementText;
}
public void setReplacementText(String replacementText) {
this.replacementText = replacementText;
}
public Pattern getUgcPattern() {
if (ugcPattern == null) {
if (matchText != null) {
ugcPattern = Pattern.compile(matchText);
}
}
return ugcPattern;
}
}
private static final SingleTypeJAXBManager<MarineWordingConfiguration> jaxb = SingleTypeJAXBManager
.createWithoutException(MarineWordingConfiguration.class);
public static MarineWordingConfiguration load(WarngenLayer forLayer) throws Exception {
String xmlText = WarnFileUtil.convertFileContentsToString(FILE_NAME,
LocalizationManager.getInstance().getCurrentSite(),
forLayer.getLocalizedSite());
MarineWordingConfiguration config = (MarineWordingConfiguration)
jaxb.unmarshalFromXml(xmlText);
for (MarineWordingEntry entry : config.getEntries()) {
// Validate patterns by compiling now.
entry.getUgcPattern();
}
return config;
}
}

View file

@ -33,6 +33,7 @@ import java.util.List;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 16, 2014 3419 jsanchez Initial creation
* Aug 28, 2014 ASM #15658 D. Friedman Add marine zone list.
*
* </pre>
*
@ -58,6 +59,8 @@ public class Watch {
private List<String> partOfState;
private List<String> marineAreas;
public Watch(String state, String action, String phenSig, String etn,
Date startTime, Date endTime) {
this.state = state;
@ -132,6 +135,14 @@ public class Watch {
this.etn = etn;
}
public List<String> getMarineAreas() {
return marineAreas;
}
public void setMarineAreas(List<String> marineAreas) {
this.marineAreas = marineAreas;
}
@Override
public int hashCode() {
final int prime = 31;

View file

@ -54,8 +54,11 @@ import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.util.TimeUtil;
import com.raytheon.uf.common.util.Pair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.requests.ThriftClient;
import com.raytheon.viz.core.mode.CAVEMode;
import com.raytheon.viz.warngen.gis.MarineWordingConfiguration.MarineWordingEntry;
import com.raytheon.viz.warngen.gui.WarngenLayer;
import com.raytheon.viz.warngen.gui.WarngenLayer.GeoFeatureType;
import com.vividsolutions.jts.geom.Geometry;
@ -72,6 +75,7 @@ import com.vividsolutions.jts.geom.Polygon;
* ------------ ---------- ----------- --------------------------
* Jul 17, 2014 3419 jsanchez Initial creation
* Aug 20, 2014 ASM #16703 D. Friedman Ensure watches have a state attribute.
* Aug 28, 2014 ASM #15658 D. Friedman Add marine zones.
*
* </pre>
*
@ -106,10 +110,16 @@ public class WatchUtil {
private static final String COUNTY_FE_AREA_FIELD = "FE_AREA";
private static final Object MARINE_ZONE_UGC_FIELD = "ID";
private static final Object MARINE_ZONE_NAME_FIELD = "NAME";
private static final String STATE_FIELD = "STATE";
private static final String COUNTY_TABLE = "County";
private static final String MARINE_ZONE_TABLE = "MarineZones";
private static final String PARENT_NAME_FIELD = "NAME";
private static final String[] REQUEST_FIELDS = new String[] {
@ -118,8 +128,12 @@ public class WatchUtil {
private GeospatialData[] countyGeoData;
private GeospatialData[] marineGeoData;
private WarngenLayer warngenLayer;
private MarineWordingConfiguration marineWordingConfig;
public WatchUtil(WarngenLayer warngenLayer) throws InstantiationException {
countyGeoData = warngenLayer.getGeodataFeatures(COUNTY_TABLE,
warngenLayer.getLocalizedSite());
@ -155,6 +169,17 @@ public class WatchUtil {
Validate.isTrue(watchAreaBuffer >= 0,
"'includedWatchAreaBuffer' can not be negative in .xml file");
if (config.isIncludeMarineAreasInWatches()) {
marineGeoData = warngenLayer.getGeodataFeatures(MARINE_ZONE_TABLE,
warngenLayer.getLocalizedSite());
if (marineGeoData == null) {
throw new VizException("Cannot get geospatial data for "
+ MARINE_ZONE_TABLE + "-based watches");
}
marineWordingConfig = MarineWordingConfiguration.load(warngenLayer);
}
String[] includedWatches = config.getIncludedWatches();
if ((includedWatches != null) && (includedWatches.length > 0)) {
@ -174,10 +199,16 @@ public class WatchUtil {
entityClass = PracticeActiveTableRecord.class;
}
HashSet<String> allUgcs = new HashSet<String>(
warngenLayer.getAllUgcs(GeoFeatureType.COUNTY));
Set<String> marineUgcs = null;
if (config.isIncludeMarineAreasInWatches()) {
marineUgcs = warngenLayer.getAllUgcs(GeoFeatureType.MARINE);
allUgcs.addAll(marineUgcs);
}
DbQueryRequest request = buildRequest(simulatedTime,
phenSigConstraint.toString(),
warngenLayer.getAllUgcs(GeoFeatureType.COUNTY),
entityClass);
phenSigConstraint.toString(), allUgcs, entityClass);
DbQueryResponse response = (DbQueryResponse) ThriftClient
.sendRequest(request);
@ -192,9 +223,14 @@ public class WatchUtil {
/ KmToDegrees);
System.out.println("create watch area buffer time: "
+ (System.currentTimeMillis() - t0));
Set<String> validUgcZones = warngenLayer
.getUgcsForWatches(watchArea, GeoFeatureType.COUNTY);
watches = processRecords(records, validUgcZones);
HashSet<String> validUgcZones = new HashSet<String>(
warngenLayer.getUgcsForWatches(watchArea,
GeoFeatureType.COUNTY));
if (config.isIncludeMarineAreasInWatches()) {
validUgcZones.addAll(warngenLayer.getUgcsForWatches(
watchArea, GeoFeatureType.MARINE));
}
watches = processRecords(records, validUgcZones, marineUgcs);
} catch (RuntimeException e) {
statusHandler
.handle(Priority.ERROR,
@ -302,12 +338,13 @@ public class WatchUtil {
*
* @param activeTableRecords
* @param validUgcZones
* @param marineUgcs
*
* @return
*/
private List<Watch> processRecords(
List<ActiveTableRecord> activeTableRecords,
Set<String> validUgcZones) {
Set<String> validUgcZones, Set<String> marineUgcs) {
List<Watch> watches = new ArrayList<Watch>();
/*
@ -329,14 +366,16 @@ public class WatchUtil {
* validUgcZones here.
*/
String ugcZone = ar.getUgcZone();
String state = getStateName(ugcZone.substring(0, 2));
String state = null;
/*
* Temporary fix for SS DR #16703. Remove when marine watch wording
* is fixed.
*/
if (state == null)
if (marineUgcs != null && marineUgcs.contains(ugcZone)) {
// Just leave state == null
} else {
state = getStateName(ugcZone.substring(0, 2));
if (state == null) {
continue;
}
}
String action = ar.getAct();
String phenSig = ar.getPhensig();
@ -360,9 +399,13 @@ public class WatchUtil {
for (Entry<Watch, List<String>> entry : map.entrySet()) {
Watch watch = entry.getKey();
watch.setAreas(entry.getValue());
if (watch.getState() != null) {
List<String> partOfState = new ArrayList<String>(
determineAffectedPortions(watch.getAreas()));
watch.setPartOfState(partOfState);
} else {
watch.setMarineAreas(determineMarineAreas(watch.getAreas()));
}
watches.add(watch);
}
@ -412,6 +455,40 @@ public class WatchUtil {
return affectedPortions;
}
private List<String> determineMarineAreas(List<String> areas) {
HashSet<Pair<Integer, String>> groupedAreas = new HashSet<Pair<Integer,String>>();
for (String area : areas) {
int entryIndex = 0;
for (MarineWordingEntry entry : marineWordingConfig.getEntries()) {
if (entry.getUgcPattern().matcher(area).matches()) {
String replacement = entry.getReplacementText();
if (replacement != null) {
if (replacement.length() > 0) {
groupedAreas.add(new Pair<Integer, String>(
entryIndex, entry.getReplacementText()));
}
} else {
groupedAreas.add(new Pair<Integer, String>(entryIndex,
getMarineZoneName(area)));
}
}
entryIndex++;
}
}
ArrayList<Pair<Integer, String>> sorted = new ArrayList<Pair<Integer,String>>(groupedAreas);
Collections.sort(sorted, new Comparator<Pair<Integer, String>>() {
public int compare(Pair<Integer, String> o1, Pair<Integer, String> o2) {
int r = o1.getFirst().compareTo(o2.getFirst());
return r != 0 ? r : o1.getSecond().compareTo(o2.getSecond());
};
});
ArrayList<String> result = new ArrayList<String>(sorted.size());
for (Pair<Integer, String> value : sorted) {
result.add(value.getSecond());
}
return result;
}
/**
* Returns the full state name from the state abbreviation.
*
@ -446,6 +523,16 @@ public class WatchUtil {
return null;
}
private String getMarineZoneName(String ugc) {
for (GeospatialData g : marineGeoData) {
if (((String) g.attributes.get(MARINE_ZONE_UGC_FIELD))
.endsWith(ugc)) {
return (String) g.attributes.get(MARINE_ZONE_NAME_FIELD);
}
}
return null;
}
// Based on AWIPS 1 SELSparagraphs.C SELSparagraphs::processWOU().
private String mungeFeAreas(Set<String> feAreas) {
String abrev = "";

View file

@ -61,6 +61,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority;
* Apr 24, 2013 1943 jsanchez Marked areaConfig as Deprecated.
* Oct 22, 2013 2361 njensen Removed ISerializableObject
* Apr 28, 2014 3033 jsanchez Properly handled back up configuration (*.xml) files.
* Aug 28, 2014 ASM #15658 D. Friedman Add marine zone watch wording option.
* </pre>
*
* @author chammack
@ -103,6 +104,9 @@ public class WarngenConfiguration {
@XmlElement(name = "includedWatch")
private String[] includedWatches;
@XmlElement
private boolean includeMarineAreasInWatches;
@XmlElementWrapper(name = "durations")
@XmlElement(name = "duration")
private int[] durations;
@ -392,6 +396,14 @@ public class WarngenConfiguration {
return includedWatches;
}
public boolean isIncludeMarineAreasInWatches() {
return includeMarineAreasInWatches;
}
public void setIncludeMarineAreasInWatches(boolean includeMarineAreasInWatches) {
this.includeMarineAreasInWatches = includeMarineAreasInWatches;
}
public boolean getEnableRestart() {
return enableRestart;
}

View file

@ -12,6 +12,7 @@
##### Evan Bookbinder 09-20-2013 Fixed rural area otherPoints in pathcast section, added rural phrase
##### Qinglu Lin 03-17-2014 DR 16309. Updated inserttorwatches and insertsvrwatches.
##### Qinglu Lin 05-21-2014 DR 16309. Updated inserttorwatches and insertsvrwatches by changing 'FOR##' to 'FOR ##'.
##### D. Friedman 08-28-2014 ASM #15658. Add marine watch wording.
####################################################################################################
#*
Mile Marker Test Code
@ -218,7 +219,11 @@ ${dateUtil.period(${tornadoWatch.endTime},${timeFormat.plain}, 15, ${localtimezo
#set($count = 0)
#foreach(${watch} in ${tornadoWatches})
#set($count = $count + 1)
#if(!${watch.marineAreas})
#areaFormat(${watch.partOfState} true false true)${watch.state}##
#else
#formatMarineAreas(${watch.marineAreas})
#end
#if($count == $numPortions - 1)
AND ##
#elseif($count < $numPortions)
@ -263,7 +268,11 @@ ${dateUtil.period(${svrWatch.endTime},${timeFormat.plain}, 15, ${localtimezone})
#set($count = 0)
#foreach(${watch} in ${severeWatches})
#set($count = $count + 1)
#if(!${watch.marineAreas})
#areaFormat(${watch.partOfState} true false true)${watch.state}##
#else
#formatMarineAreas(${watch.marineAreas})
#end
#if($count == $numPortions - 1)
AND ##
#elseif($count < $numPortions)
@ -278,6 +287,25 @@ ${dateUtil.period(${svrWatch.endTime},${timeFormat.plain}, 15, ${localtimezone})
#end
########END
#macro(formatMarineAreas $marineAreas)
#set($macount = 0)
#set($numMarineAreas = ${list.size(${marineAreas})})
#foreach(${marineArea} in ${marineAreas})
#set($macount = $macount + 1)
#if(${marineArea}=="THE ADJACENT COASTAL WATERS" && $macount > 1)
OTHER ADJACENT COASTAL WATERS##
#else
${marineArea}##
#end
#if($macount == $numMarineAreas - 1)
AND ##
#elseif($macount < $numMarineAreas)
...##
#end
#end
#end
########END MACRO
#macro(printcoords $coordinates $list)
#set($count = 0)
LAT...LON ##

View file

@ -58,6 +58,9 @@ turned on unless the corresponding .vm file is turned on in a given template's .
<includedWatch>SV.A</includedWatch>
</includedWatches>
<!-- Include references to marine zones for watches. See marineZoneWording.xml -->
<includeMarineAreasInWatches>true</includeMarineAreasInWatches>
<!-- durations: the list of possible durations -->
<defaultDuration>60</defaultDuration>
<durations>

View file

@ -0,0 +1,24 @@
<!--
This file defines grouping and replacement text for marine zones in the watch
wording of the template.
For each entry, the "match" attribute defines a regular expression that is
used to match marine zone UGCs. The "replace" attribute defines the text to
pass to the template. If the replacement text is empty, the marine zone
will not be used. If the "replace" attribute is not present, the marine
zone's original name is used.
Marine zone UGCs are matched against entries in their given order; the first
match is used. The order of the entries also determines the order of the
values passed to the template.
-->
<zoneWordingConfig>
<entry match="^LEZ.*" replace="THE ADJACENT COASTAL WATERS OF LAKE ERIE" />
<entry match="^LHZ.*" replace="THE ADJACENT COASTAL WATERS OF LAKE HURON" />
<entry match="^LMZ.*" replace="THE ADJACENT COASTAL WATERS OF LAKE MICHIGAN" />
<entry match="^LOZ.*" replace="THE ADJACENT COASTAL WATERS OF LAKE ONTARIO" />
<entry match="^LSZ.*" replace="THE ADJACENT COASTAL WATERS OF LAKE SUPERIOR" />
<entry match="^LCZ.*" replace="THE ADJACENT COASTAL WATERS OF LAKE SAINT CLAIRE" />
<entry match="^SLZ.*" replace="" /> <!-- Saint Lawrence River -->
<entry match="^.*" replace="THE ADJACENT COASTAL WATERS" />
</zoneWordingConfig>

View file

@ -61,6 +61,9 @@ turned on unless the corresponding .vm file is turned on in a given template's .
<includedWatch>SV.A</includedWatch>
</includedWatches>
<!-- Include references to marine zones for watches. See marineZoneWording.xml. -->
<includeMarineAreasInWatches>true</includeMarineAreasInWatches>
<!-- durations: the list of possible durations of the warning -->
<defaultDuration>30</defaultDuration>
<durations>

View file

@ -63,6 +63,9 @@ turned on unless the corresponding .vm file is turned on in a given template's .
<includedWatch>SV.A</includedWatch>
</includedWatches>
<!-- Include references to marine zones for watches. See marineZoneWording.xml. -->
<includeMarineAreasInWatches>true</includeMarineAreasInWatches>
<!-- durations: the list of possible durations of the svs -->
<!-- THIS REALLY SERVES NO PURPOSE BUT WILL CRASH WARNGEN IF REMVOED -->
<defaultDuration>30</defaultDuration>