Merge "Issue #2177 Improve county portion descriptions." into omaha_13.5.2

Former-commit-id: a0437634dc [formerly 07f087013f] [formerly 321ddebdde [formerly 1b7f1d8dce2cc1161ce06ca0c4a8df68803a12e1]]
Former-commit-id: 321ddebdde
Former-commit-id: 0af2308491
This commit is contained in:
Lee Venable 2013-08-22 11:53:18 -05:00 committed by Gerrit Code Review
commit f5f1525809
9 changed files with 1528 additions and 126 deletions

View file

@ -32,6 +32,7 @@ import java.util.List;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 11, 2007 #601 chammack Initial Creation.
* Aug 19, 2013 2177 jsanchez Removed suppress attribute.
*
* </pre>
*
@ -49,11 +50,6 @@ public class AffectedAreas {
*/
protected List<String> partOfArea;
/**
* The partOfArea to suppress (e.g. "ns")
*/
protected String suppress;
/** The notation of the area affected (e.g. "COUNTY") */
protected String areaNotation;
@ -89,17 +85,15 @@ public class AffectedAreas {
public String getName() {
return name;
}
/**
* @param name
* the name to set
* @param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the areaNotation
*/

View file

@ -46,7 +46,6 @@ import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.warngen.gui.WarngenLayer;
import com.raytheon.viz.warngen.suppress.SuppressMap;
import com.raytheon.viz.warngen.util.Abbreviation;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
@ -74,6 +73,7 @@ import com.vividsolutions.jts.geom.prep.PreparedGeometry;
* Nov 9, 2012 DR 15430 D. Friedman Extracted method converFeAreaToPartList.
* Apr 29, 2013 1955 jsanchez Ignored comparing the geometry's user data when finding intersected areas.
* May 2, 2013 1963 jsanchez Updated method to determine partOfArea.
* Aug 19, 2013 2177 jsanchez Used portionsUtil to calculate area portion descriptions.
* </pre>
*
* @author chammack
@ -89,13 +89,15 @@ public class Area {
*/
public static final double DEFAULT_PORTION_TOLERANCE = 0.60;
private Area() {
private PortionsUtil portionsUtil;
public Area(PortionsUtil portionsUtil) {
this.portionsUtil = portionsUtil;
}
public static AffectedAreas[] findAffectedAreas(
WarngenConfiguration config, Geometry polygon,
Geometry warningArea, String localizedSite) throws VizException {
public AffectedAreas[] findAffectedAreas(WarngenConfiguration config,
Geometry polygon, Geometry warningArea, String localizedSite)
throws VizException {
// --- Begin argument checking ---
Validate.notNull(config.getGeospatialConfig().getAreaSource(),
@ -113,7 +115,7 @@ public class Area {
localizedSite, geoms);
}
private static AffectedAreas[] findAffectedAreas(
private AffectedAreas[] findAffectedAreas(
AreaSourceConfiguration areaConfig,
GeospatialConfiguration geospatialConfig, Geometry polygon,
String localizedSite, List<Geometry> geoms) throws VizException {
@ -183,7 +185,6 @@ public class Area {
area.stateabbr = regionFeature.attributes.get(areaNotationField)
.toString();
area.size = regionGeom.getArea();
area.suppress = suppressType(areaSource, area.stateabbr, area.fips);
Object tzData = regionFeature.attributes.get(timezonePathcastField);
@ -219,9 +220,16 @@ public class Area {
double tolerCheck = regionGeom.getArea()
* DEFAULT_PORTION_TOLERANCE;
if (areaIntersection < tolerCheck) {
area.partOfArea = GisUtil
.asStringList(GisUtil.calculatePortion(regionGeom,
intersection, true, true));
try {
String entityID = area.stateabbr + areaSource.charAt(0)
+ area.fips.substring(2);
area.partOfArea = GisUtil.asStringList(portionsUtil
.getPortions(entityID, regionGeom, intersection,
true));
} catch (Exception e) {
statusHandler.error("Unable to calculate part of area for "
+ area.name, e);
}
}
// Search the parent region
@ -277,10 +285,9 @@ public class Area {
* @return
* @throws VizException
*/
public static Map<String, Object> findInsectingAreas(
WarngenConfiguration config, Geometry warnPolygon,
Geometry warnArea, String localizedSite, WarngenLayer warngenLayer)
throws VizException {
public Map<String, Object> findInsectingAreas(WarngenConfiguration config,
Geometry warnPolygon, Geometry warnArea, String localizedSite,
WarngenLayer warngenLayer) throws VizException {
Map<String, Object> areasMap = new HashMap<String, Object>();
String hatchedAreaSource = config.getHatchedAreaSource()
@ -311,18 +318,6 @@ public class Area {
}
private static String suppressType(String areaSource, String state,
String fips) {
String retVal = SuppressMap.NONE;
String type = areaSource.equalsIgnoreCase("zone") ? "Z" : "C";
if (state != null && fips != null) {
String key = state + type + fips.substring(2);
retVal = SuppressMap.getInstance().getType(key);
}
return retVal;
}
public static List<String> converFeAreaToPartList(String feArea) {
final List<String> partList = new ArrayList<String>();
if (feArea == null) {

View file

@ -0,0 +1,178 @@
/**
* 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.viz.warngen.gis;
/**
* Port of A1 constants applied to a grid to determine county or zone portions.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 5, 2013 2177 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class CoverageConstants {
public static final int XSOUTH = 0x0001;
public static final int SOUTH = 0x0002;
public static final int NORTH = 0x0040;
public static final int XNORTH = 0x0080;
public static final int CENTER_NS = 0x0018;
public static final int CENTRAL_NS = 0x0024;
public static final int XWEST = 0x0100;
public static final int WEST = 0x0200;
public static final int EAST = 0x4000;
public static final int XEAST = 0x8000;
public static final int CENTER_EW = 0x1800;
public static final int CENTRAL_EW = 0x2400;
public static final int SOUTHERN = 0x0003;
public static final int NORTHERN = 0x00C0;
public static final int WESTERN = 0x0300;
public static final int EASTERN = 0xC000;
public static final int SOUTHSIDE = 0x000F;
public static final int NORTHSIDE = 0x00F0;
public static final int WESTSIDE = 0x0F00;
public static final int EASTSIDE = 0xF000;
public static final int EXTREME = 0x8181;
public static final int NOT_EXTREME = 0x7E7E;
public static final int EXTREME_NS = 0x0081;
public static final int EXTREME_EW = 0x8100;
public static final int CENTRAL = 0x2424;
public static final int CENTER = 0x1818;
public static final int NOT_CENTRAL = 0xC3C3;
public static final int NORTH_SOUTH = 0x00FF;
public static final int EAST_WEST = 0xFF00;
public static final int NNE = 0x0001;
public static final int ENE = 0x0002;
public static final int ESE = 0x0004;
public static final int SSE = 0x0008;
public static final int SSW = 0x0010;
public static final int WSW = 0x0020;
public static final int WNW = 0x0040;
public static final int NNW = 0x0080;
public static final int EXTREME_YES = 0xFFFF00;
public static final int EXTREME_NO = 0x00FF;
public static final int EXTREME_CORNER = 0xFF0000;
public static int[] NS_MASK = new int[256];
public static int[] EW_MASK = new int[256];
static {
int i;
NS_MASK[0] = 0;
for (i = 1; i < 256; i++) {
if (i < 87) {
NS_MASK[i] = XSOUTH | SOUTH;
} else if (i > 167) {
NS_MASK[i] = XNORTH | NORTH;
} else if (i < 106) {
NS_MASK[i] = SOUTH;
} else if (i > 148) {
NS_MASK[i] = NORTH;
} else if (i < 118) {
NS_MASK[i] = CENTRAL_NS | SOUTH;
} else if (i > 138) {
NS_MASK[i] = CENTRAL_NS | NORTH;
} else if (i < 127) {
NS_MASK[i] = CENTER_NS | CENTRAL_NS | SOUTH;
} else if (i > 127) {
NS_MASK[i] = CENTER_NS | CENTRAL_NS | NORTH;
} else {
NS_MASK[i] = CENTER_NS | CENTRAL_NS;
}
}
EW_MASK[0] = 0;
for (i = 1; i < 256; i++) {
if (i < 87) {
EW_MASK[i] = XWEST | WEST;
} else if (i > 167) {
EW_MASK[i] = XEAST | EAST;
} else if (i < 106) {
EW_MASK[i] = WEST;
} else if (i > 145) {
EW_MASK[i] = EAST;
} else if (i < 118) {
EW_MASK[i] = CENTRAL_EW | WEST;
} else if (i > 138) {
EW_MASK[i] = CENTRAL_EW | EAST;
} else if (i < 127) {
EW_MASK[i] = CENTER_EW | CENTRAL_EW | WEST;
} else if (i > 127) {
EW_MASK[i] = CENTER_EW | CENTRAL_EW | EAST;
} else {
EW_MASK[i] = CENTER_EW | CENTRAL_EW;
}
}
}
private CoverageConstants() {
}
}

View file

@ -0,0 +1,65 @@
/**
* 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.viz.warngen.gis;
/**
* Simple port of an A1 struct created by GridUtil and used by PortionsUtil.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 5, 2013 2177 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class EntityData {
private int meanMask = 0;
private int coverageMask = 0;
private int octants = 0;
public EntityData(int meanMask, int coverageMask, int octants) {
this.meanMask = meanMask;
this.coverageMask = coverageMask;
this.octants = octants;
}
public int getMeanMask() {
return meanMask;
}
public int getCoverageMask() {
return coverageMask;
}
public int getOctants() {
return octants;
}
}

View file

@ -0,0 +1,616 @@
/**
* 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.viz.warngen.gis;
import java.util.ArrayList;
import java.util.List;
import org.geotools.coverage.grid.GeneralGridGeometry;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.referencing.operation.DefaultMathTransformFactory;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.referencing.operation.MathTransform;
import com.raytheon.uf.common.dataplugin.warning.util.GeometryUtil;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.warngen.gui.WarngenLayer;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
/**
* Converts the county or zone and the intersecting warning area to grids. The
* county or zone is also weighted to determine the northern, southern, eastern,
* and western parts of the county or zone. Most of the code is ported from A1
* to create an EntityData object that will be used by PortionsUtil to determine
* the applicable impacted portions of a county or zone.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 5, 2013 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class GridUtil {
private int ny = 0;
private int nx = 0;
private int[] ewGrid;
private int[] nsGrid;
private byte[] warnedAreaGrid;
private byte[] countyOrZoneGrid;
private WarngenLayer layer;
private MathTransform latLonToContour, contourToLatLon;
public GridUtil(WarngenLayer layer, GeneralGridGeometry localGridGeometry,
MathTransform localToLatLon) throws Exception {
this.layer = layer;
GridEnvelope range = localGridGeometry.getGridRange();
this.nx = range.getHigh(0);
this.ny = range.getHigh(1);
org.opengis.geometry.Envelope ge = localGridGeometry.getEnvelope();
contourToLatLon = new DefaultMathTransformFactory()
.createConcatenatedTransform(new GridGeometry2D(range, ge)
.getGridToCRS(PixelOrientation.CENTER), localToLatLon);
latLonToContour = contourToLatLon.inverse();
ewGrid = new int[nx * ny];
nsGrid = new int[nx * ny];
}
/**
* Converts the countyOrZone geometry and the warnedArea into grids and sets
* the appropriate data in an EntityData object.
*
* @param countyOrZone
* @param warnedArea
* @return
* @throws Exception
*/
public EntityData calculateGrids(Geometry countyOrZone, Geometry warnedArea)
throws Exception {
countyOrZoneGrid = toByteArray(countyOrZone);
warnedAreaGrid = toByteArray(warnedArea);
int[] bounds = awips1FinishAreaEntity();
EntityData entityData = finishDefineArea(bounds);
return entityData;
}
/**
* Converts the geometry into a byte array that is expected by ported A1
* code.
*
* @param geometry
* @return
* @throws VizException
*/
private byte[] toByteArray(Geometry geometry) throws VizException {
byte[] bytes = new byte[nx * ny];
float[][] floatData = toFloatData(geometry);
// Rotates grid
int k = 0;
for (int j = ny - 1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
if (floatData[i][j] == 1) {
bytes[k] = 1;
}
k++;
}
}
return bytes;
}
/**
* Converts the geometry into a 2-D float array.
*
* @param geometry
* @return
* @throws VizException
*/
private float[][] toFloatData(Geometry geometry) throws VizException {
Geometry contoured = layer.convertGeom(geometry, latLonToContour);
List<Geometry> geomList = new ArrayList<Geometry>(
contoured.getNumGeometries());
GeometryUtil.buildGeometryList(geomList, contoured);
List<PreparedGeometry> prepped = new ArrayList<PreparedGeometry>(
geomList.size());
for (Geometry g : geomList) {
prepped.add(PreparedGeometryFactory.prepare(g));
}
GeometryFactory gf = geometry.getFactory();
Point point = gf.createPoint(new Coordinate(0, 0));
CoordinateSequence pointCS = point.getCoordinateSequence();
float[][] contourAreaData = new float[nx][ny];
for (PreparedGeometry geom : prepped) {
Envelope env = geom.getGeometry().getEnvelopeInternal();
int startX = (int) env.getMinX();
int startY = (int) env.getMinY();
int width = (int) env.getMaxX();
int height = (int) env.getMaxY();
if (startX < 0 || width > nx || startY < 0 || height > ny) {
continue;
}
startX = Math.max(0, startX - 1);
startY = Math.max(0, startY - 1);
width = Math.min(nx, width + 1);
height = Math.min(ny, height + 1);
for (int x = startX; x < width; ++x) {
for (int y = startY; y < height; ++y) {
pointCS.setOrdinate(0, 0, x);
pointCS.setOrdinate(0, 1, y);
point.geometryChanged();
if (contourAreaData[x][y] == 0.0f && geom.intersects(point)) {
contourAreaData[x][y] = 1.0f;
}
}
}
}
return contourAreaData;
}
/**
* Ported only the logic from A1 code
* GeoEntityLookupTable::finishDefineArea() that calculates the meanMask,
* coverageMask,and octants for an entity (i.e. county or zone).
*
* @param countyOrZone
* @param warnedArea
* @return
*/
private EntityData finishDefineArea(int[] bounds) {
int meanMask = 0;
int coverageMask = 0;
int octants = 0;
int ewCount = 0;
int nsCount = 0;
int ewTotal = 0;
int nsTotal = 0;
int k = 0;
int min_i = bounds[0];
int max_i = bounds[1];
int min_j = bounds[2];
int max_j = bounds[3];
for (int j = min_j; j < max_j; j++) {
k = nx * j + min_i;
for (int i = min_i; i < max_i; i++, k++) {
if (warnedAreaGrid[k] == 1) {
int e = countyOrZoneGrid[k];
int ii = ewGrid[k];
int jj = nsGrid[k];
if (ii == 0 && jj == 0) {
continue;
}
ewTotal += ii;
if (ii > 0) {
ewCount++;
}
nsTotal += jj;
if (jj > 0) {
nsCount++;
}
int m = CoverageConstants.EW_MASK[ii]
| CoverageConstants.NS_MASK[jj];
coverageMask |= m;
if ((m & CoverageConstants.CENTRAL) == CoverageConstants.CENTRAL) {
continue;
}
if (ii == 0) {
ii = 127;
}
if (jj == 0) {
jj = 127;
}
if (ii < 127) {
if (jj < 127) {
e = (ii > jj ? CoverageConstants.SSW
: CoverageConstants.WSW);
} else {
e = (ii > 254 - jj ? CoverageConstants.NNW
: CoverageConstants.WNW);
}
} else {
if (jj < 127) {
e = (ii < 254 - jj ? CoverageConstants.SSE
: CoverageConstants.ESE);
} else {
e = (ii < jj ? CoverageConstants.NNE
: CoverageConstants.ENE);
}
}
if ((m & CoverageConstants.EXTREME_NS) > 0) {
e <<= 8;
}
if ((m & CoverageConstants.EXTREME_EW) > 0) {
e <<= 8;
}
octants |= e;
} else {
warnedAreaGrid[k] = 0;
}
}
}
if (ewCount > 0) {
ewTotal = (ewTotal + ewCount / 2) / ewCount;
}
if (nsCount > 0) {
nsTotal = (nsTotal + nsCount / 2) / nsCount;
}
meanMask = CoverageConstants.NS_MASK[nsTotal]
| CoverageConstants.EW_MASK[ewTotal];
return new EntityData(meanMask, coverageMask, octants);
}
/**
* Calculates the _ewGrid and _nsGrid via A1 ~~ mAgIc ~~
*/
private int[] awips1FinishAreaEntity() {
final double EXTREME_FRAC = 0.1;
final double MIN_EXTREME = 87;
final double MAX_EXTREME = 167;
int k = 0;
int ii, jj;
/*
* identify those points on the boundary of the entity so we can shrink
* from there
*/
int i_mean = 0;
int j_mean = 0;
int new_tot = 0;
int ii_mean = 0;
int jj_mean = 0;
int min_i = Integer.MAX_VALUE;
int min_j = Integer.MAX_VALUE;
int max_i = Integer.MIN_VALUE;
int max_j = Integer.MIN_VALUE;
for (jj = 0; jj < ny; jj++) {
k = nx * jj;
for (ii = 0; ii < nx; ii++, k++) {
// If the entity is not 1 then it's not part of the county or
// zone area.
if (countyOrZoneGrid[k] != 1) {
continue;
}
if (ii > max_i) {
max_i = ii;
}
if (ii < min_i) {
min_i = ii;
}
if (jj > max_j) {
max_j = jj;
}
if (jj < min_j) {
min_j = jj;
}
++new_tot;
ii_mean += ii;
jj_mean += jj;
}
}
/*
* restablish some things that might have changed since the first time
* they were calculated.
*/
// if (!outside) {
ii_mean /= new_tot;
jj_mean /= new_tot;
i_mean = ii_mean;
j_mean = jj_mean;
// }/*endif*/
/* assign correct base for directional computation */
// I changed this from ii_mean to this
double i_base = (min_i + max_i) / 2;
// I changed this from jj_mean to this
double j_base = (min_j + max_j) / 2;
/*
* calculate needed rotation factors for computation of amount each
* point is north and east of centroid
*/
double x_intcpg = 0.5;
double y_intcpg = 0.5;
double dx = 1, dy = 1;
double x = (i_base - x_intcpg);
double y = (j_base - y_intcpg);
// Below is some code from the original A1 code. I assumed that x_intcpg
// to be 0.5 to avoid porting all the methods in gelt_maker.c.
// xy_to_ll(&x,&y,&lat,&lon,&my_proj);
// lat01=lat+0.1;
// ll_to_xy(&lat01,&lon,&dx,&dy,&my_proj);
// dx -= x;
dy -= y;
double mag = Math.sqrt(dx * dx + dy * dy);
dx /= mag;
dy /= mag;
double erot_i = -dy;
double erot_j = -dx;
double nrot_i = dx;
double nrot_j = dy;
int[] ew_hist = new int[nx * ny];
int[] ns_hist = new int[nx * ny];
/* Calculate north/south & east/west offsets, create histogram of these. */
// TODO I did not fully implement the histograms as used in a1. Using
// the histograms created index out of bounds errors. If the field is
// unhappy with the portions, then porting the histograms needs to be
// re-investigated.
int ns1 = 0, ns2 = 0, ew1 = 0, ew2 = 0;
int np_n = 0, np_s = 0, np_e = 0, np_w = 0;
ns_hist[0] = 0;
ew_hist[0] = 0;
for (jj = min_j; jj < max_j; jj++) {
k = nx * jj + min_i;
for (ii = min_i; ii < max_i; ii++, k++) {
// If the entity is not 1 then it's not part of the county or
// zone area.
if (countyOrZoneGrid[k] != 1) {
continue;
}
double di = ii - i_base;
double dj = jj - j_base;
double dns = (int) (nrot_i * di + nrot_j * dj);
while (ns1 > dns) {
// ns_hist[--ns1] = 0;
--ns1;
}
while (ns2 < dns) {
// ns_hist[++ns2] = 0;
++ns2;
}
// ns_hist[(int) dns]++;
double dew = (int) (erot_i * di + erot_j * dj);
while (ew1 > dew) {
// ew_hist[--ew1] = 0;
--ew1;
}
while (ew2 < dew) {
// ew_hist[++ew2] = 0;
++ew2;
}
// ew_hist[(int) dew]++;
if (dew < 0) {
np_w++;
}
if (dew > 0) {
np_e++;
}
if (dns < 0) {
np_s++;
}
if (dns > 0) {
np_n++;
}
}/* end for */
}/* end for */
/*
* Transform n-s & e-w offsets into normalized distances north and
* south. This is done based on a preferred fraction of each area that
* is "extreme".
*/
// a lot of assumptions were made here. therefore, not everything in
// this part was fully ported.
double target = np_w * EXTREME_FRAC;
// for (ii = 0, k = ew1; k < -1 && ii < target; k++) {
// ii += ew_hist[k];
// }
// if (ii / target > 1.5) {
// k--;
// }
// if (k < ew1) {
// k = ew1;
// }
k = ew1;
double mu_w = (MIN_EXTREME - 127) / (k + 0.5);
target = np_e * EXTREME_FRAC;
// for (ii = 0, k = ew2; k > 1 && ii < target; k--) {
// ii += ew_hist[k];
// }
// if (ii / target > 1.5) {
// k++;
// }
// if (k > ew2) {
// k = ew2;
// }
k = ew2;
double mu_e = (MAX_EXTREME - 127) / (k - 0.5);
target = np_s * EXTREME_FRAC;
// for (ii = 0, k = ns1; k < -1 && ii < target; k++) {
// ii += ns_hist[k];
// }
// if (ii / target > 1.5) {
// k--;
// }
// if (k < ns1) {
// k = ns1;
// }
k = ns1;// TODO - REPLACE WITH ABOVE
double mu_s = (MIN_EXTREME - 127) / (k + 0.5);
target = np_n * EXTREME_FRAC;
// for (ii = 0, k = ns2; k > 1 && ii < target; k--) {
// ii += ns_hist[k];
// }
// if (ii / target > 1.5) {
// k++;
// }
// if (k > ns2) {
// k = ns2;
// }
k = ns2;
double mu_n = (MAX_EXTREME - 127) / (k - 0.5);
for (jj = min_j; jj < max_j; jj++) {
k = nx * jj + min_i;
for (ii = min_i; ii < max_i; ii++, k++) {
// If the entity is not 1 then it's not part of the county or
// zone area.
if (countyOrZoneGrid[k] != 1) {
ewGrid[k] = 0;
nsGrid[k] = 0;
continue;
}
double di = ii - i_base;
double dj = jj - j_base;
double dns = (int) (nrot_i * di + nrot_j * dj);
double dew = (int) (erot_i * di + erot_j * dj);
int c_ns2 = (int) (dns);
int c_ew2 = (int) (dew);
if (c_ew2 < 0) {
dx = c_ew2 * mu_w;
} else {
dx = c_ew2 * mu_e;
}
if (dx > 127) {
dx = 127;
}
if (dx < -127) {
dx = -127;
}
ewGrid[k] = (int) (127 + (int) (dx));
if (c_ns2 < 0) {
dy = c_ns2 * mu_s;
} else {
dy = c_ns2 * mu_n;
}
if (dy > 127) {
dy = 127;
}
if (dy < -127) {
dy = -127;
}
nsGrid[k] = (int) (127 + (int) (dy));
}
}
// System.out.println("-----------------------------------");
// printGrids(countyOrZoneGrid, min_i, max_i, min_j, max_j);
// System.out.println("-----------------------------------");
// printGrids(_currentArea, min_i, max_i, min_j, max_j);
// System.out.println("-------------- EAST WEST ---------------------");
// printGrids(ewGrid, min_i, max_i, min_j, max_j);
// System.out.println("-------------- NORTH SOUTH ---------------------");
// printGrids(nsGrid, min_i, max_i, min_j, max_j);
// System.out.println("north/south - east/west done");
return new int[] { min_i, max_i, min_j, max_j };
}
// For debugging and view the grid in ascii format.
private void printGrids(int[] grid, int min_i, int max_i, int min_j,
int max_j) {
int k = 0;
for (int jj = min_j; jj < max_j; jj++) {
k = nx * jj + min_i;
for (int ii = min_i; ii < max_i; ii++) {
System.out.print((int) grid[k]);
k++;
}
System.out.println("-");
}
}
// For debugging and view the grid in ascii format.
private void printGrids(byte[] grid, int min_i, int max_i, int min_j,
int max_j) {
int k = 0;
for (int jj = min_j; jj < max_j; jj++) {
k = nx * jj + min_i;
for (int ii = min_i; ii < max_i; ii++) {
System.out.print((int) grid[k]);
k++;
}
System.out.println("-");
}
}
}

View file

@ -0,0 +1,471 @@
/**
* 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.viz.warngen.gis;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import com.raytheon.viz.warngen.gis.GisUtil.Direction;
import com.raytheon.viz.warngen.gui.WarngenLayer;
import com.vividsolutions.jts.geom.Geometry;
/**
* Port of A1 code that determines the portions of the county or zone
* descriptions, such as NORTHWEST.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 5, 2013 2177 jsanchez Initial creation
*
* </pre>
*
* @author jsanchez
* @version 1.0
*/
public class PortionsUtil {
private GridUtil gridUtil;
public PortionsUtil(WarngenLayer layer) throws Exception {
gridUtil = new GridUtil(layer, layer.getLocalGridGeometry(),
layer.getlocalToLatLon());
}
/**
* Determines the the appropriate portion description for the warnedArea
* intersecting the countyOrZone.
*
* @param entityID
* @param countyOrZone
* @param warnedArea
* @param useExtreme
* @return
* @throws Exception
*/
public EnumSet<Direction> getPortions(String entityID,
Geometry countyOrZone, Geometry warnedArea, boolean useExtreme)
throws Exception {
countyOrZone.getUserData();
EntityData entityData = gridUtil.calculateGrids(countyOrZone,
warnedArea);
EnumSet<Direction> portions = getAreaDesc(entityData.getMeanMask(),
entityData.getCoverageMask(), entityData.getOctants(),
useExtreme);
return suppressPortions(entityID, portions);
}
/**
* Looks up if the designated entity ID has an suppressed directions. For
* example, a county or zone may not need to include the north and sound
* direction description if it was included in the area.suppress file.
*
* @param entityID
* @param portions
* @return
*/
private EnumSet<Direction> suppressPortions(String entityID,
EnumSet<Direction> portions) {
Map<String, List<Direction>> suppressedCounties = SuppressMap
.getInstance().getAreas();
if (entityID != null && suppressedCounties != null
&& !suppressedCounties.isEmpty()) {
List<Direction> suppressedDirections = suppressedCounties
.get(entityID.toUpperCase());
if (suppressedDirections != null && !suppressedDirections.isEmpty()) {
portions.removeAll(suppressedDirections);
}
}
return portions;
}
/**
* Port from A1 code of GeoEntityLookupTable::getAreaDesc.
*
* @param meanMask
* @param areaMask
* @param octants
* @param exYes
*/
private static EnumSet<Direction> getAreaDesc(int meanMask, int areaMask,
int octants, boolean exYes) {
EnumSet<Direction> portions = EnumSet.noneOf(Direction.class);
// Test for case where we cannot do portions
if (meanMask == 0 || areaMask == 0) {
return portions;
}
// The next block of code is the original port of A1 code but prevented
// producing the correct result:
// Test for case where area is completely within one subsection.
// if (meanMask == areaMask) {
// return getPointDesc(meanMask, exYes);
// }
// Test for central by not being near adjacent borders.
if (octants == 0
|| ((octants & CoverageConstants.EXTREME_YES) == 0)
&& (meanMask & CoverageConstants.CENTER) == CoverageConstants.CENTER) {
portions.add(Direction.CENTRAL);
return portions;
}
if ((octants & 0xFFFF) == 0xFFFF) {
return portions;
}
// Identify quadrants in use, q is typical, qq is diagonal.
int xoctant = octants >> 8;
int xxoctant = octants >> 16;
int nn, ss, ee, ww, ne, nw, se, sw;
nn = ss = ee = ww = ne = nw = se = sw = 0;
int omerge = xxoctant | xoctant | octants;
if ((omerge & (CoverageConstants.NNE | CoverageConstants.ENE)) > 0) {
ne = 1;
}
if ((omerge & (CoverageConstants.SSE | CoverageConstants.ESE)) > 0) {
se = 1;
}
if ((omerge & (CoverageConstants.NNW | CoverageConstants.WNW)) > 0) {
nw = 1;
}
if ((omerge & (CoverageConstants.SSW | CoverageConstants.WSW)) > 0) {
sw = 1;
}
if ((omerge & (CoverageConstants.NNE | CoverageConstants.NNW)) > 0) {
nn = 1;
}
if ((omerge & (CoverageConstants.SSE | CoverageConstants.SSW)) > 0) {
ss = 1;
}
if ((omerge & (CoverageConstants.WNW | CoverageConstants.WSW)) > 0) {
ww = 1;
}
if ((omerge & (CoverageConstants.ENE | CoverageConstants.ESE)) > 0) {
ee = 1;
}
if ((areaMask & CoverageConstants.NORTH_SOUTH) == 0) {
nn = ss = ne = nw = se = sw = 0;
}
if ((areaMask & CoverageConstants.EAST_WEST) == 0) {
ee = ww = ne = nw = se = sw = 0;
}
int q = ne + nw + se + sw;
int qq = nn + ss + ee + ww;
// Identify extremes in use.
int nnx, ssx, eex, wwx;
nnx = ssx = eex = wwx = 0;
if ((areaMask & CoverageConstants.XNORTH) > 0) {
nnx = 1;
}
if ((areaMask & CoverageConstants.XSOUTH) > 0) {
ssx = 1;
}
if ((areaMask & CoverageConstants.XWEST) > 0) {
wwx = 1;
}
if ((areaMask & CoverageConstants.XEAST) > 0) {
eex = 1;
}
int xxx = nnx + ssx + eex + wwx;
// Modify masks based on whether we can use extreme.
if ((octants & CoverageConstants.EXTREME_NO) > 0
&& (areaMask & CoverageConstants.EXTREME) > 0) {
areaMask &= CoverageConstants.NOT_EXTREME;
meanMask &= CoverageConstants.NOT_EXTREME;
}
// Possible case of a stripe across the middle
if (q == 0) {
;// Only one direction encoded
} else if (q == 2 && nw == se || q == 2 && ne == sw || qq == 2
&& nn == ss || qq == 2 && ee == ww) {
if ((meanMask & CoverageConstants.CENTRAL) == CoverageConstants.CENTRAL
|| nnx == ssx && wwx == eex) {
portions.add(Direction.CENTRAL);
return portions;
}
return getPointDesc2(meanMask, exYes, nn, ss, ee, ww);
}
// Modify masks based on whether we can use central.
if (xxx > 2 || nnx != ssx && wwx != eex) {
areaMask &= CoverageConstants.NOT_CENTRAL;
meanMask &= CoverageConstants.NOT_CENTRAL;
}
// Another possible case of a stripe across the middle.
if (q == 4 && (meanMask & CoverageConstants.CENTER) > 0) {
portions.add(Direction.CENTRAL);
return portions;
}
// All quadrants in use.
if (q == 4 && qq == 4) {
return EnumSet.noneOf(Direction.class);
}
// Only one typical quadrant in use.
if (q == 1) {
// if (ne == 1) {
// portions.add(Direction.NORTH);
// portions.add(Direction.EAST);
// } else if (nw == 1) {
// portions.add(Direction.NORTH);
// portions.add(Direction.WEST);
// } else if (se == 1) {
// portions.add(Direction.SOUTH);
// portions.add(Direction.EAST);
// } else if (sw == 1) {
// portions.add(Direction.SOUTH);
// portions.add(Direction.WEST);
// }
// return portions;
return getPointDesc2(meanMask, exYes, nn, ss, ee, ww);
}
// Further modify masks based on whether we can use central.
if (xxx >= 2) {
areaMask &= CoverageConstants.NOT_CENTRAL;
meanMask &= CoverageConstants.NOT_CENTRAL;
}
// No more than two quadrants of any kind in use, or all quadrants.
if (q < 3 && qq < 3) {
if (nnx != ssx && wwx != eex
|| (meanMask & CoverageConstants.CENTRAL) > 0) {
return getPointDesc2(meanMask, exYes, nn, ss, ee, ww);
} else {
return getPointDesc2(areaMask, exYes, nn, ss, ee, ww);
}
}
// Three typical quadrants in use.
if (q == 3 && qq != 3) {
if (ne == 0) {
// The next line is the original port of A1 code but prevented
// producing the correct result:
// if (ne == 0 && (xxoctant & (SSW | WSW)) > 0) {
portions.add(Direction.SOUTH);
portions.add(Direction.WEST);
} else if (se == 0) {
// The next line is the original port of A1 code but prevented
// producing the correct result:
// } else if (se == 0 && (xxoctant & (NNW | WNW)) > 0) {
portions.add(Direction.NORTH);
portions.add(Direction.WEST);
} else if (nw == 0) {
// The next line is the original port of A1 code but prevented
// producing the correct result:
// } else if (nw == 0 && (xxoctant & (SSE | ESE)) > 0) {
portions.add(Direction.SOUTH);
portions.add(Direction.EAST);
} else if (sw == 0) {
// The next line is the original port of A1 code but prevented
// producing the correct result:
// } else if (sw == 0 && (xxoctant & (NNE | ENE)) > 0) {
portions.add(Direction.NORTH);
portions.add(Direction.EAST);
}
// The next line is the original port of A1 code but prevented
// producing the correct result:
// return getPointDesc(meanMask, exYes);
}
// Three diagonal quadrants in use.
if (qq == 3 && portions.isEmpty()) {
if (nn == 0) {
portions.add(Direction.SOUTH);
} else if (ss == 0) {
portions.add(Direction.NORTH);
} else if (ww == 0) {
portions.add(Direction.EAST);
} else if (ee == 0) {
portions.add(Direction.WEST);
}
}
// add extreme for three quadrant case.
if (!portions.isEmpty()) {
if (exYes && ((areaMask & CoverageConstants.EXTREME)) > 0) {
portions.add(Direction.EXTREME);
}
return portions;
}
// All of either type of quadrant in use.
if (q == 4 || qq == 4) {
return EnumSet.noneOf(Direction.class);
}
// Case of a pure simple direction.
nn = areaMask & CoverageConstants.NORTHERN;
ss = areaMask & CoverageConstants.SOUTHERN;
ee = areaMask & CoverageConstants.EASTERN;
ww = areaMask & CoverageConstants.WESTERN;
if (ss > 0 && nn > 0 || q == 0) {
if (ee == 0 && ww > 0) {
portions.add(Direction.WEST);
}
if (ww == 0 && ee > 0) {
portions.add(Direction.EAST);
}
} else if (ee > 0 && ww > 0 || q == 0) {
if (nn == 0 && ss > 0) {
portions.add(Direction.SOUTH);
}
if (ss == 0 && nn > 0) {
portions.add(Direction.NORTH);
}
}
// add extreme for simple direction case.
if (!portions.isEmpty()) {
if (exYes && ((areaMask & CoverageConstants.EXTREME)) > 0) {
portions.add(Direction.EXTREME);
}
return portions;
}
// Catch with the point descriptor one last time
return getPointDesc2(meanMask, exYes, nn, ss, ee, ww);
}
/**
* Port from A1 code of GeoEntityLookupTable::getPointDesc.
*
* @param mask
* @param exYes
* @return
*/
private static EnumSet<Direction> getPointDesc(int mask, boolean exYes) {
EnumSet<Direction> portions = EnumSet.noneOf(Direction.class);
if (mask == 0) {
return portions;
}
int cc = mask & CoverageConstants.CENTRAL;
if (cc == CoverageConstants.CENTRAL) {
portions.add(Direction.CENTRAL);
return portions;
}
if ((mask & CoverageConstants.NORTH_SOUTH) == 0) {
;
} else if ((mask & CoverageConstants.SOUTHERN) == (mask & CoverageConstants.NORTH_SOUTH)) {
portions.add(Direction.SOUTH);
} else if ((mask & CoverageConstants.NORTHERN) == (mask & CoverageConstants.NORTH_SOUTH)) {
portions.add(Direction.NORTH);
}
if ((mask & CoverageConstants.EAST_WEST) == 0) {
;
} else if ((mask & CoverageConstants.WESTERN) == (mask & CoverageConstants.EAST_WEST)) {
portions.add(Direction.WEST);
} else if ((mask & CoverageConstants.EASTERN) == (mask & CoverageConstants.EAST_WEST)) {
portions.add(Direction.EAST);
}
if (portions.isEmpty()) {
return portions;
}
if (cc != 0) {
portions.add(Direction.CENTRAL);
}
if (exYes && ((int) (mask & CoverageConstants.EXTREME) > 0)) {
portions.add(Direction.EXTREME);
}
return portions;
}
/**
* This method is not a direct port from A1. The original getPointDesc did
* not produce the expected results. This method is a modified version of
* getPointDesct that uses the calculated qq values instead of just the
* meanMask.
*
* @param mask
* @param exYes
* @return
*/
private static EnumSet<Direction> getPointDesc2(int mask, boolean exYes,
int nn, int ss, int ee, int ww) {
EnumSet<Direction> portions = EnumSet.noneOf(Direction.class);
if (mask == 0) {
return portions;
}
int counter = 0;
if (nn > 0 && ss > 0) {
;
} else if (ss > 0) {
portions.add(Direction.SOUTH);
counter++;
} else if (nn > 0) {
portions.add(Direction.NORTH);
counter++;
}
if (ee > 0 && ww > 0) {
;
} else if (ww > 0) {
portions.add(Direction.WEST);
counter++;
} else if (ee > 0) {
portions.add(Direction.EAST);
counter++;
}
if (portions.isEmpty()) {
return portions;
}
int cc = mask & CoverageConstants.CENTRAL;
boolean useCentral = counter < 2;
if (useCentral && cc != 0) {
portions.add(Direction.CENTRAL);
}
if (exYes && ((int) (mask & CoverageConstants.EXTREME) > 0)) {
portions.add(Direction.EXTREME);
}
return portions;
}
}

View file

@ -17,7 +17,7 @@
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.warngen.suppress;
package com.raytheon.viz.warngen.gis;
import java.io.BufferedReader;
import java.io.File;
@ -25,7 +25,10 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -38,9 +41,11 @@ import com.raytheon.uf.common.localization.PathManagerFactory;
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.viz.core.localization.LocalizationManager;
import com.raytheon.viz.warngen.gis.GisUtil.Direction;
/**
* TODO Add Description
* Creates a map of all the site's area suppress files.
*
* <pre>
*
@ -48,6 +53,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 2, 2010 jsanchez Initial creation
* Aug 15,2013 2177 jsanchez Refactored.
*
* </pre>
*
@ -66,16 +72,26 @@ public class SuppressMap {
private static final Pattern ugcPattern = Pattern
.compile("[A-Z]{2}[CZ][0-9]{3}");
public static final String NORTH_SOUTH = "NS";
private static final List<Direction> NORTH_SOUTH = Arrays.asList(
Direction.NORTH, Direction.SOUTH);
public static final String EAST_WEST = "EW";
private static final List<Direction> EAST_WEST = Arrays.asList(
Direction.EAST, Direction.WEST);
public static final String NONE = "NONE";
private static final List<Direction> ALL = Arrays.asList(Direction.NORTH,
Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.CENTRAL,
Direction.EXTREME);
public static final String ALL = "ALL";
private static Map<String, Map<String, List<Direction>>> suppressMap = new HashMap<String, Map<String, List<Direction>>>();
private Map<String, String> suppressMap = new HashMap<String, String>();
private SuppressMap() {
}
/**
*
* @return an instance of the SuppressMap object.
*/
public static SuppressMap getInstance() {
if (instance == null) {
instance = new SuppressMap();
@ -83,64 +99,82 @@ public class SuppressMap {
return instance;
}
private SuppressMap() {
readFile();
/**
* Returns the suppressed area map for the current localized site.
*
* @return
*/
public Map<String, List<Direction>> getAreas() {
String threeLetterSiteID = LocalizationManager.getInstance()
.getCurrentSite();
Map<String, List<Direction>> areas = suppressMap.get(threeLetterSiteID);
if (areas == null) {
areas = new HashMap<String, List<Direction>>();
IPathManager pm = PathManagerFactory.getPathManager();
LocalizationContext lc = pm.getContext(
LocalizationType.COMMON_STATIC, LocalizationLevel.SITE);
File file = pm.getFile(lc, AREA_SUPPRESS_FILENAME);
loadFile(file, areas);
}
return areas;
}
private void readFile() {
// load site
IPathManager pathMgr = PathManagerFactory.getPathManager();
LocalizationContext lc = pathMgr.getContext(
LocalizationType.COMMON_STATIC, LocalizationLevel.SITE);
File file = pathMgr.getFile(lc, AREA_SUPPRESS_FILENAME);
loadFile(file, suppressMap);
}
private void loadFile(File file, Map<String, String> aliasMap) {
/**
* Loads the areas map with the suppress information in the file
*
* @param file
* @param areas
*/
private void loadFile(File file, Map<String, List<Direction>> areas) {
if ((file != null) && file.exists()) {
Matcher m = null;
BufferedReader fis = null;
try {
BufferedReader fis = new BufferedReader(new InputStreamReader(
fis = new BufferedReader(new InputStreamReader(
new FileInputStream(file)));
String line = null;
try {
while ((line = fis.readLine()) != null) {
m = ugcPattern.matcher(line);
if (m.find()) {
String dataKey = m.group();
String data = ALL;
List<Direction> suppressedDirections = new ArrayList<Direction>();
String ugc = m.group();
if (line.indexOf("ns") > 5) {
data = NORTH_SOUTH;
suppressedDirections = NORTH_SOUTH;
} else if (line.indexOf("ew") > 5) {
data = EAST_WEST;
suppressedDirections = EAST_WEST;
} else {
suppressedDirections = ALL;
}
aliasMap.put(dataKey, data);
areas.put(ugc, suppressedDirections);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block. Please revise as
// appropriate.
statusHandler.handle(Priority.PROBLEM,
"Could not read counties file: "
+ AREA_SUPPRESS_FILENAME, e);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block. Please revise as
// appropriate.
statusHandler.handle(Priority.PROBLEM,
"Failed to find counties file: "
+ AREA_SUPPRESS_FILENAME, e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
statusHandler.handle(Priority.PROBLEM,
"Error trying to close buffered reader ", e);
}
}
}
}
}
public String getType(String key) {
if (suppressMap.containsKey(key)) {
return suppressMap.get(key);
} else {
return NONE;
}
}
}

View file

@ -49,6 +49,9 @@ import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.PlatformUI;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GeneralGridGeometry;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.GeodeticCalculator;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
@ -57,7 +60,6 @@ import org.opengis.referencing.operation.MathTransform;
import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord;
import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
import com.raytheon.uf.common.dataplugin.warning.config.AreaSourceConfiguration;
import com.raytheon.uf.common.dataplugin.warning.config.AreaSourceConfiguration.AreaType;
import com.raytheon.uf.common.dataplugin.warning.config.BulletActionGroup;
import com.raytheon.uf.common.dataplugin.warning.config.DialogConfiguration;
import com.raytheon.uf.common.dataplugin.warning.config.GridSpacing;
@ -185,6 +187,7 @@ import com.vividsolutions.jts.io.WKTReader;
* 07/26/2013 DR 16376 Qinglu Lin Moved adjustVertex() and computeSlope() to PolygonUtil; removed calculateDistance();
* updated AreaHatcher's run().
* 07/26/2013 DR 16450 D. Friedman Fix logic errors when frame count is one.
* 08/19/2013 2177 jsanchez Set a GeneralGridGeometry object in the GeospatialDataList.
* </pre>
*
* @author mschenke
@ -212,24 +215,28 @@ public class WarngenLayer extends AbstractStormTrackResource {
IExtent localExtent;
int nx, ny;
GeneralGridGeometry localGridGeometry;
}
private static class GeospatialDataAccessor {
GeospatialDataList geoData;
AreaSourceConfiguration areaConfig;
public GeospatialDataAccessor(GeospatialDataList geoData,
AreaSourceConfiguration areaConfig) {
if (geoData == null || areaConfig == null) {
throw new IllegalArgumentException("GeospatialDataAccessor must not be null");
throw new IllegalArgumentException(
"GeospatialDataAccessor must not be null");
}
this.geoData = geoData;
this.areaConfig = areaConfig;
}
/**
* Build the geometry area that intersects the cwa filter for the polygon in
* local projection space
* Build the geometry area that intersects the cwa filter for the
* polygon in local projection space
*
* @param polygon
* polygon to intersect with in lat/lon space
@ -243,8 +250,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
PreparedGeometry prepGeom = (PreparedGeometry) r.attributes
.get(GeospatialDataList.LOCAL_PREP_GEOM);
try {
Geometry intersection = GeometryUtil.intersection(polygon,
prepGeom);
Geometry intersection = GeometryUtil.intersection(
polygon, prepGeom);
if (intersection.isEmpty()) {
continue;
}
@ -407,7 +414,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
}
try {
warningPolygon = PolygonUtil.removeDuplicateCoordinate(warningPolygon);
warningPolygon = PolygonUtil
.removeDuplicateCoordinate(warningPolygon);
Polygon hatched = polygonUtil.hatchWarningArea(
warningPolygon,
removeCounties(warningArea,
@ -425,27 +433,37 @@ public class WarngenLayer extends AbstractStormTrackResource {
LinearRing lr = gf.createLinearRing(coords);
hatchedArea = gf.createPolygon(lr, null);
int adjustPolygon_counter = 0;
while (!hatchedArea.isValid() && adjustPolygon_counter < 1) {
System.out.println("Calling adjustPolygon #" + adjustPolygon_counter);
while (!hatchedArea.isValid()
&& adjustPolygon_counter < 1) {
System.out.println("Calling adjustPolygon #"
+ adjustPolygon_counter);
PolygonUtil.adjustPolygon(coords);
PolygonUtil.round(coords, 2);
coords = PolygonUtil.removeDuplicateCoordinate(coords);
coords = PolygonUtil.removeOverlaidLinesegments(coords);
coords = PolygonUtil
.removeDuplicateCoordinate(coords);
coords = PolygonUtil
.removeOverlaidLinesegments(coords);
lr = gf.createLinearRing(coords);
hatchedArea = gf.createPolygon(lr, null);
adjustPolygon_counter += 1;
}
int counter = 0;
if (!hatchedArea.isValid() && counter < 2) {
System.out.println("calling adjustVertex & alterVertexes: loop #" + counter);
System.out
.println("calling adjustVertex & alterVertexes: loop #"
+ counter);
int adjustVertex_counter = 0;
lr = gf.createLinearRing(coords);
hatchedArea = gf.createPolygon(lr, null);
while (!hatchedArea.isValid() && adjustVertex_counter < 5) {
System.out.println(" Calling adjustVertex #" + adjustVertex_counter);
while (!hatchedArea.isValid()
&& adjustVertex_counter < 5) {
System.out.println(" Calling adjustVertex #"
+ adjustVertex_counter);
coords = PolygonUtil.adjustVertex(coords);
coords = PolygonUtil.removeDuplicateCoordinate(coords);
coords = PolygonUtil.removeOverlaidLinesegments(coords);
coords = PolygonUtil
.removeDuplicateCoordinate(coords);
coords = PolygonUtil
.removeOverlaidLinesegments(coords);
lr = gf.createLinearRing(coords);
hatchedArea = gf.createPolygon(lr, null);
adjustVertex_counter += 1;
@ -453,10 +471,14 @@ public class WarngenLayer extends AbstractStormTrackResource {
int inner_counter = 0;
System.out.println("");
while (!hatchedArea.isValid() && inner_counter < 5) {
System.out.println(" Calling alterVertexes #" + inner_counter);
System.out
.println(" Calling alterVertexes #"
+ inner_counter);
coords = PolygonUtil.alterVertexes(coords);
coords = PolygonUtil.removeDuplicateCoordinate(coords);
coords = PolygonUtil.removeOverlaidLinesegments(coords);
coords = PolygonUtil
.removeDuplicateCoordinate(coords);
coords = PolygonUtil
.removeOverlaidLinesegments(coords);
lr = gf.createLinearRing(coords);
hatchedArea = gf.createPolygon(lr, null);
inner_counter += 1;
@ -674,8 +696,7 @@ public class WarngenLayer extends AbstractStormTrackResource {
displayState.angle = 0;
displayState.speed = 0;
} else if (checkStormTrackData(data = ToolsDataManager
.getInstance()
.getStormTrackData())) {
.getInstance().getStormTrackData())) {
displayState.angle = adjustAngle(data.getMotionDirection());
displayState.speed = knotToMeterPerSec.convert(data
.getMotionSpeed());
@ -995,8 +1016,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
}
if (config != null) {
init(config);
displayState.setInitiallyMotionless(
this.configuration.isTrackEnabled() == false
displayState.setInitiallyMotionless(this.configuration
.isTrackEnabled() == false
|| this.configuration.getPathcastConfig() == null);
}
}
@ -1044,9 +1065,11 @@ public class WarngenLayer extends AbstractStormTrackResource {
+ (System.currentTimeMillis() - t0) + "ms");
}
/** Adds geospatial data to siteMap and timezoneMap for the given
* template configuration. This must not have any site effects on the
* currently loaded template or the current product being edited.
/**
* Adds geospatial data to siteMap and timezoneMap for the given template
* configuration. This must not have any site effects on the currently
* loaded template or the current product being edited.
*
* @param config
*/
private void loadGeodataForConfiguration(WarngenConfiguration config) {
@ -1083,7 +1106,6 @@ public class WarngenLayer extends AbstractStormTrackResource {
Coordinate c = new GeometryFactory()
.buildGeometry(geoms).getCentroid()
.getCoordinate();
gData.latLonToLocal = MapUtil
.getTransformFromLatLon(MapUtil
.constructStereographic(
@ -1146,6 +1168,18 @@ public class WarngenLayer extends AbstractStormTrackResource {
gData.nx = nx;
gData.ny = ny;
GeneralGridEnvelope range = new GeneralGridEnvelope(
new int[] { 0, 0 }, new int[] { gData.nx,
gData.ny }, false);
GeneralEnvelope ge = new GeneralEnvelope(new double[] {
gData.localExtent.getMinX(),
gData.localExtent.getMaxY() }, new double[] {
gData.localExtent.getMaxX(),
gData.localExtent.getMinY() });
gData.localGridGeometry = new GeneralGridGeometry(
range, ge);
System.out.println("Time to lookup geospatial data "
+ (System.currentTimeMillis() - tq0));
siteMap.put(currKey, gData);
@ -1188,6 +1222,14 @@ public class WarngenLayer extends AbstractStormTrackResource {
return new GeospatialData[0];
}
public GeneralGridGeometry getLocalGridGeometry() {
return geoData.localGridGeometry;
}
public MathTransform getlocalToLatLon() {
return geoData.localToLatLon;
}
private GeospatialDataList getGeodataList(String areaSource,
String localizedSite) {
String key = areaSource + "." + localizedSite;
@ -1303,7 +1345,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
* Returns a set of UGCs for each area in the CWA that intersects the given
* polygon.
*/
public Set<String> getUgcsForCountyWatches(Polygon polygon) throws Exception {
public Set<String> getUgcsForCountyWatches(Polygon polygon)
throws Exception {
GeospatialDataAccessor gda = getCountyGeospatialDataAcessor();
Set<String> ugcs = new HashSet<String>();
for (String fips : gda.getAllFipsInArea(gda.buildArea(polygon))) {
@ -1321,17 +1364,20 @@ public class WarngenLayer extends AbstractStormTrackResource {
return ugcs;
}
private GeospatialDataAccessor getCountyGeospatialDataAcessor() throws Exception {
private GeospatialDataAccessor getCountyGeospatialDataAcessor()
throws Exception {
GeospatialDataList gdl = searchCountyGeospatialDataAccessor();
if (gdl == null) {
// Cause county geospatial data to be loaded
// TODO: Should not be referencing tornadoWarning.
WarngenConfiguration torConfig = WarngenConfiguration.loadConfig("tornadoWarning", getLocalizedSite());
WarngenConfiguration torConfig = WarngenConfiguration.loadConfig(
"tornadoWarning", getLocalizedSite());
loadGeodataForConfiguration(torConfig);
gdl = searchCountyGeospatialDataAccessor();
}
// TODO: There should be some way to get the "county" configuration by name
// TODO: There should be some way to get the "county" configuration by
// name
// independent of a template
AreaSourceConfiguration areaConfig = new AreaSourceConfiguration();
areaConfig.setFipsField("FIPS");
@ -1341,7 +1387,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
private GeospatialDataList searchCountyGeospatialDataAccessor() {
synchronized (siteMap) {
for (Map.Entry<String, GeospatialDataList> entry : siteMap.entrySet()) {
for (Map.Entry<String, GeospatialDataList> entry : siteMap
.entrySet()) {
String[] keyParts = entry.getKey().split("\\.");
if (keyParts.length == 2
&& "county".equalsIgnoreCase(keyParts[0])
@ -1938,8 +1985,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
return;
}
if (warningAction == null || warningAction == WarningAction.NEW) {
if ((configuration.isTrackEnabled() == false ||
configuration.getPathcastConfig() == null)
if ((configuration.isTrackEnabled() == false || configuration
.getPathcastConfig() == null)
&& !this.displayState.isNonstationary()
&& this.displayState.displayType != DisplayType.POLY) {
createSquare();
@ -2271,8 +2318,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
}
Point point = displayState.dragMePoint;
if (motdir != null && motspd != null &&
(motspd != 0 || configuration.isTrackEnabled())) {
if (motdir != null && motspd != null
&& (motspd != 0 || configuration.isTrackEnabled())) {
displayState.setInitiallyMotionless(false);
displayState.angle = adjustAngle(motdir);
displayState.speed = knotToMeterPerSec.convert(motspd);
@ -2325,10 +2372,9 @@ public class WarngenLayer extends AbstractStormTrackResource {
if (m.find()) {
int hour = Integer.parseInt(m.group(1));
int minute = Integer.parseInt(m.group(2));
frameTime = TimeUtil.timeOfDayToAbsoluteTime(
hour * TimeUtil.SECONDS_PER_HOUR +
minute * TimeUtil.SECONDS_PER_MINUTE,
warnRecord.getIssueTime());
frameTime = TimeUtil.timeOfDayToAbsoluteTime(hour
* TimeUtil.SECONDS_PER_HOUR + minute
* TimeUtil.SECONDS_PER_MINUTE, warnRecord.getIssueTime());
} else {
frameTime = warnRecord.getIssueTime();
}
@ -2376,8 +2422,7 @@ public class WarngenLayer extends AbstractStormTrackResource {
FramesInfo info = descriptor.getFramesInfo();
int currentFrame = trackUtil.getCurrentFrame(info);
int frameCount = trackUtil.getFrameCount(info);
if (currentFrame == frameCount - 1
|| ! displayState.isNonstationary()) {
if (currentFrame == frameCount - 1 || !displayState.isNonstationary()) {
return coordinate;
}
DataTime[] datatimes = trackUtil.getDataTimes(info);
@ -2686,7 +2731,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
getFips(f));
if (tmp.isEmpty()) {
String fip = getFips(f);
if (fip != null && uniqueFip != null && fip.equals(uniqueFip)) {
if (fip != null && uniqueFip != null
&& fip.equals(uniqueFip)) {
updateWarnedAreas(true);
}
break;
@ -2769,8 +2815,7 @@ public class WarngenLayer extends AbstractStormTrackResource {
if (areaHatcher != null) {
Polygon polygon = state.getWarningPolygon();
polygon = tryToIntersectWithOriginalPolygon(polygon);
areaHatcher.hatchArea(polygon,
state.getWarningArea(),
areaHatcher.hatchArea(polygon, state.getWarningArea(),
state.getOldWarningPolygon());
}
}

View file

@ -98,6 +98,7 @@ import com.raytheon.viz.warngen.gis.Area;
import com.raytheon.viz.warngen.gis.ClosestPointComparator;
import com.raytheon.viz.warngen.gis.GisUtil;
import com.raytheon.viz.warngen.gis.PathCast;
import com.raytheon.viz.warngen.gis.PortionsUtil;
import com.raytheon.viz.warngen.gis.Wx;
import com.raytheon.viz.warngen.gui.BackupData;
import com.raytheon.viz.warngen.gui.FollowupData;
@ -154,6 +155,7 @@ import com.vividsolutions.jts.io.WKTReader;
* May 10, 2013 1951 rjpeter Updated ugcZones references
* May 30, 2013 DR 16237 D. Friedman Fix watch query.
* Jun 18, 2013 2118 njensen Only calculate pathcast if it's actually used
* Aug 19, 2013 2177 jsanchez Passed PortionsUtil to Area class.
* </pre>
*
* @author njensen
@ -301,17 +303,18 @@ public class TemplateRunner {
AffectedAreas[] cancelareas = null;
Map<String, Object> intersectAreas = null;
Wx wx = null;
Area area = new Area(new PortionsUtil(warngenLayer));
long wwaMNDTime = 0l;
try {
t0 = System.currentTimeMillis();
areas = Area.findAffectedAreas(config, warnPolygon, warningArea,
areas = area.findAffectedAreas(config, warnPolygon, warningArea,
threeLetterSiteId);
System.out.println("Time to get areas = "
+ (System.currentTimeMillis() - t0));
context.put(config.getHatchedAreaSource().getVariable(), areas);
t0 = System.currentTimeMillis();
intersectAreas = Area.findInsectingAreas(config, warnPolygon,
intersectAreas = area.findInsectingAreas(config, warnPolygon,
warningArea, threeLetterSiteId, warngenLayer);
System.out.println("Time to get intersecting areas = "
+ (System.currentTimeMillis() - t0));
@ -324,10 +327,11 @@ public class TemplateRunner {
double minSize = 1.0E-3d;
if ((areas != null) && (areas.length > 0)) {
Set<String> timeZones = new HashSet<String>();
for (AffectedAreas area : areas) {
if (area.getTimezone() != null) {
for (AffectedAreas affectedAreas : areas) {
if (affectedAreas.getTimezone() != null) {
// Handles counties that span two time zones
String oneLetterTimeZones = area.getTimezone().trim();
String oneLetterTimeZones = affectedAreas.getTimezone()
.trim();
oneLetterTZ = new String[oneLetterTimeZones.length()];
if (oneLetterTimeZones.length() == 1) {
timeZones.add(String.valueOf(oneLetterTimeZones
@ -799,14 +803,14 @@ public class TemplateRunner {
Geometry removedAreas = warngenLayer.getWarningAreaForGids(
oldGids, oldWarningArea);
if (removedAreas.isEmpty() == false) {
cancelareas = Area.findAffectedAreas(config,
cancelareas = area.findAffectedAreas(config,
oldWarn.getGeometry(), removedAreas,
threeLetterSiteId);
for (int i = 0; i < cancelareas.length; i++) {
for (AffectedAreas area : areas) {
for (AffectedAreas affectedAreas : areas) {
if ((cancelareas[i] != null)
&& cancelareas[i].getFips().equals(
area.getFips())) {
affectedAreas.getFips())) {
cancelareas[i] = null;
}
}
@ -824,7 +828,7 @@ public class TemplateRunner {
// This may not be efficient enough. Is it possible that
// a removed intersected county be in the affected
// intersected county. Need an example to fully test.
Map<String, Object> intersectRemovedAreas = Area
Map<String, Object> intersectRemovedAreas = area
.findInsectingAreas(config, warnPolygon,
removedAreas, threeLetterSiteId,
warngenLayer);