Omaha #3353 Changes to remove Geospatial data set generation off the UI thread.

Change-Id: I5213f627a97a27bdb92c4857eac4071c5450b790

Former-commit-id: f16a8ff2c51c392957e13ef63423abe9f99b3301
This commit is contained in:
Roger Ferrel 2014-08-21 08:25:26 -05:00
parent 6370aaba96
commit ca3a0595d2
9 changed files with 674 additions and 234 deletions

View file

@ -0,0 +1,166 @@
/**
* 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.gui;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialFactory;
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialMetadata;
import com.raytheon.uf.common.geospatial.SpatialException;
import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.viz.ui.dialogs.CaveSWTDialog;
/**
* A blocking modal dialog that generates a geometry spatial data set. This
* dialog will only display in the rare scenario where the Geospatial data is
* not ready. It conveys to the user that the data is still being created and
* will be ready soon.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 20, 2014 3353 rferrel Initial creation
*
* </pre>
*
* @author rferrel
* @version 1.0
*/
public class GenerateGeoDataSetDialog extends CaveSWTDialog {
private final String site;
private final GeospatialMetadata gmd;
private ProgressBar progressBar;
private final String messageFormat = "Please wait. Generating Geometry Spatial Data Set for %s.\nThis may take several minutes.";
/**
* @param parentShell
* @param site
* @param gmd
*/
protected GenerateGeoDataSetDialog(Shell parentShell, String site,
GeospatialMetadata gmd) {
/*
* This needs to be a blocking dialog. The return value is used to
* placed an entry in WarngenLayer's siteMap. Several layers of calls in
* both WarngenLayer and WarngenDialog assume the siteMap contains the
* values generated by this dialog.
*
* If made non-blocking these layers of calls would need a non-blocking
* way of being informed when the siteMap is updated. Also synchronize
* on siteMap become more complicated.
*/
super(parentShell, SWT.PRIMARY_MODAL | SWT.BORDER, CAVE.NONE);
this.site = site;
this.gmd = gmd;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.ui.dialogs.CaveSWTDialogBase#initializeComponents(org
* .eclipse.swt.widgets.Shell)
*/
@Override
protected void initializeComponents(Shell shell) {
shell.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_YELLOW));
Composite mainComposite = new Composite(shell, SWT.NONE);
GridLayout gl = new GridLayout(1, false);
gl.verticalSpacing = 2;
gl.marginHeight = 1;
gl.marginWidth = 1;
mainComposite.setLayout(gl);
Label label = new Label(mainComposite, SWT.NONE);
label.setText(String.format(messageFormat, site));
label.setBackground(shell.getBackground());
progressBar = new ProgressBar(mainComposite, SWT.DEFAULT);
GridData gd = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
progressBar.setLayoutData(gd);
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.ui.dialogs.CaveSWTDialog#preOpened()
*/
@Override
protected void preOpened() {
super.preOpened();
progressBar.setSelection(progressBar.getMinimum());
progressBar.setSelection(SWT.NORMAL);
setShellCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_WAIT));
Job job = new Job("Generate GeoSpatial data set") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
setReturnValue(GeospatialFactory.generateGeospatialDataSet(
site, gmd));
} catch (SpatialException e) {
setReturnValue(e);
} finally {
VizApp.runAsync(new Runnable() {
@Override
public void run() {
setShellCursor(null);
close();
}
});
}
return Status.OK_STATUS;
}
};
job.schedule();
}
private void setShellCursor(Cursor cursor) {
if (!shell.isDisposed()) {
shell.setCursor(cursor);
Composite comp = shell.getParent();
while (comp != null) {
Shell parentShell = comp.getShell();
if (parentShell.isDisposed()) {
break;
}
parentShell.setCursor(cursor);
comp = parentShell.getParent();
}
}
}
}

View file

@ -46,6 +46,7 @@ import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.PlatformUI;
@ -54,8 +55,11 @@ 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.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import com.raytheon.uf.common.dataplugin.warning.AbstractWarningRecord;
import com.raytheon.uf.common.dataplugin.warning.WarningRecord.WarningAction;
@ -65,6 +69,7 @@ import com.raytheon.uf.common.dataplugin.warning.config.DialogConfiguration;
import com.raytheon.uf.common.dataplugin.warning.config.GridSpacing;
import com.raytheon.uf.common.dataplugin.warning.config.WarngenConfiguration;
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialData;
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialDataSet;
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialFactory;
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialMetadata;
import com.raytheon.uf.common.dataplugin.warning.portions.GisUtil;
@ -218,6 +223,7 @@ import com.vividsolutions.jts.io.WKTReader;
* 07/01/2014 DR 17450 D. Friedman Use list of templates from backup site.
* 07/28/2014 DR 17475 Qinglu Lin Updated populateStrings() and findLargestQuadrant(), removed findLargestGeometry(),
* added createAreaAndCentroidMaps() and movePopulatePt(), updated paintText() to center W.
* 08/20/2014 3353 rferrel Generating Geo Spatial data set no longer on the UI thread.
* </pre>
*
* @author mschenke
@ -231,6 +237,7 @@ public class WarngenLayer extends AbstractStormTrackResource {
String uniqueFip = null;
Map<String, Double> geomArea = new HashMap<String, Double>();
Map<String, Point> geomCentroid = new HashMap<String, Point>();
private static class GeospatialDataList {
@ -597,21 +604,24 @@ public class WarngenLayer extends AbstractStormTrackResource {
}
private static class GeomMetaDataUpdateNotificationObserver implements INotificationObserver {
private static class GeomMetaDataUpdateNotificationObserver implements
INotificationObserver {
private static final String SHAPEFILE_UPDATE_TOPIC = "edex.geospatialUpdate.msg";
private static GeomMetaDataUpdateNotificationObserver instance = null;
static WarngenLayer warngenLayer;
private GeomMetaDataUpdateNotificationObserver() {
}
public static synchronized GeomMetaDataUpdateNotificationObserver getInstance(WarngenLayer wl) {
public static synchronized GeomMetaDataUpdateNotificationObserver getInstance(
WarngenLayer wl) {
if (instance == null) {
instance = new GeomMetaDataUpdateNotificationObserver();
NotificationManagerJob.addObserver(SHAPEFILE_UPDATE_TOPIC, instance);
NotificationManagerJob.addObserver(SHAPEFILE_UPDATE_TOPIC,
instance);
warngenLayer = wl;
}
return instance;
@ -623,7 +633,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
*/
public static synchronized void removeNotificationObserver() {
if (instance != null) {
NotificationManagerJob.removeObserver(SHAPEFILE_UPDATE_TOPIC, instance);
NotificationManagerJob.removeObserver(SHAPEFILE_UPDATE_TOPIC,
instance);
instance = null;
}
}
@ -633,8 +644,10 @@ public class WarngenLayer extends AbstractStormTrackResource {
for (NotificationMessage message : messages) {
try {
Object payload = message.getMessagePayload();
if (payload instanceof String ) {
System.out.println("Geometry Metadata has been updated based on " + payload + " shapefile data");
if (payload instanceof String) {
System.out
.println("Geometry Metadata has been updated based on "
+ payload + " shapefile data");
warngenLayer.siteMap.clear();
warngenLayer.init(warngenLayer.configuration);
}
@ -645,7 +658,6 @@ public class WarngenLayer extends AbstractStormTrackResource {
}
}
}
private static Map<String, GeospatialDataList> siteMap = new HashMap<String, GeospatialDataList>();
@ -1207,7 +1219,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
private void initializeGeomUpdateObserver() {
if (geomUpdateObserver == null) {
geomUpdateObserver= GeomMetaDataUpdateNotificationObserver.getInstance(this);
geomUpdateObserver = GeomMetaDataUpdateNotificationObserver
.getInstance(this);
}
}
@ -1221,9 +1234,10 @@ public class WarngenLayer extends AbstractStormTrackResource {
private void loadGeodataForConfiguration(WarngenConfiguration config) {
Map<String, GeospatialMetadata> metadataMap = GeospatialFactory
.getMetaDataMap(config);
String site = getLocalizedSite();
final String site = getLocalizedSite();
synchronized (siteMap) {
for (String areaSource : metadataMap.keySet()) {
String currKey = areaSource + "." + site;
GeospatialMetadata gmd = metadataMap.get(areaSource);
@ -1233,122 +1247,28 @@ public class WarngenLayer extends AbstractStormTrackResource {
try {
long tq0 = System.currentTimeMillis();
gData = new GeospatialDataList();
gData.features = GeospatialFactory.getGeoSpatialList(
getLocalizedSite(), gmd);
// set the CountyUserData
List<Geometry> geoms = new ArrayList<Geometry>(
gData.features.length);
for (GeospatialData gd : gData.features) {
geoms.add(gd.geometry);
CountyUserData cud = new CountyUserData(gd,
String.valueOf(gd.attributes
.get(WarngenLayer.GID)));
GeometryUtil.setUserData(gd.geometry, cud);
}
List<Geometry> locals = new ArrayList<Geometry>();
Coordinate c = new GeometryFactory()
.buildGeometry(geoms).getCentroid()
.getCoordinate();
gData.latLonToLocal = MapUtil
.getTransformFromLatLon(MapUtil
.constructStereographic(
MapUtil.AWIPS_EARTH_RADIUS,
MapUtil.AWIPS_EARTH_RADIUS,
c.y, c.x));
gData.localToLatLon = gData.latLonToLocal.inverse();
for (GeospatialData gd : gData.features) {
Geometry local = JTS.transform(gd.geometry,
gData.latLonToLocal);
gd.attributes.put(AREA, local.getArea());
gd.attributes.put(GeospatialDataList.LOCAL_GEOM,
local);
gd.attributes.put(
GeospatialDataList.LOCAL_PREP_GEOM,
PreparedGeometryFactory.prepare(local));
locals.add(local);
}
Envelope env = new GeometryFactory().buildGeometry(
locals).getEnvelopeInternal();
IExtent localExtent = new PixelExtent(env.getMinX(),
env.getMaxX(), env.getMinY(), env.getMaxY());
int nx = 600;
int ny = 600;
// Boolean to change the aspect ratio of the extent to
// match
boolean keepAspectRatio = true;
GridSpacing gridSpacing = dialogConfig.getGridSpacing();
if ((gridSpacing != null)
&& (gridSpacing.getNx() != null)
&& (gridSpacing != null)) {
nx = gridSpacing.getNx();
ny = gridSpacing.getNy();
keepAspectRatio = gridSpacing.isKeepAspectRatio();
}
double xinc, yinc;
double width = localExtent.getWidth();
double height = localExtent.getHeight();
if (!keepAspectRatio) {
xinc = (width / nx);
yinc = (height / ny);
GeospatialDataSet dataSet = GeospatialFactory
.getGeoSpatialDataSet(getLocalizedSite(), gmd);
if (dataSet != null) {
updateGeoData(gData, dataSet, gmd, currKey, tq0);
} else {
if (width > height) {
ny = (int) ((height * nx) / width);
} else if (height > width) {
nx = (int) ((width * ny) / height);
}
GenerateGeoDataSetDialog genDialog = new GenerateGeoDataSetDialog(
Display.getCurrent().getActiveShell(),
site, gmd);
xinc = yinc = (width / nx);
}
gData.localExtent = new PixelExtent(
localExtent.getMinX() - xinc,
localExtent.getMaxX() + xinc,
localExtent.getMinY() - yinc,
localExtent.getMaxY() + yinc);
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);
GeospatialData[] timezones = GeospatialFactory
.getTimezones();
if (timezones != null) {
for (GeospatialData timezone : timezones) {
if (timezone.attributes.containsKey(gmd
.getTimeZoneField())) {
String oneLetterTimezone = String
.valueOf(timezone.attributes
.get(gmd.getTimeZoneField()));
if (timezoneMap
.containsKey(oneLetterTimezone) == false) {
timezoneMap.put(oneLetterTimezone,
timezone.geometry);
}
}
// Assume this is a blocking dialog.
genDialog.open();
Object o = genDialog.getReturnValue();
if (o instanceof GeospatialDataSet) {
dataSet = (GeospatialDataSet) genDialog
.getReturnValue();
updateGeoData(gData, dataSet, gmd, currKey, tq0);
} else if (o instanceof Exception) {
Exception e = (Exception) o;
statusHandler.handle(Priority.WARN,
"Error in initializing geometries.", e);
}
}
} catch (Exception e) {
statusHandler.handle(Priority.WARN,
"Error in initializing geometries.", e);
@ -1358,6 +1278,107 @@ public class WarngenLayer extends AbstractStormTrackResource {
}// end synchronize
}
private void updateGeoData(GeospatialDataList gData,
GeospatialDataSet dataSet, GeospatialMetadata gmd, String currKey,
long tq0) throws FactoryException, MismatchedDimensionException,
TransformException {
gData.features = GeospatialFactory.getGeoSpatialList(dataSet, gmd);
// set the CountyUserData
List<Geometry> geoms = new ArrayList<Geometry>(gData.features.length);
for (GeospatialData gd : gData.features) {
geoms.add(gd.geometry);
CountyUserData cud = new CountyUserData(gd,
String.valueOf(gd.attributes.get(WarngenLayer.GID)));
GeometryUtil.setUserData(gd.geometry, cud);
}
List<Geometry> locals = new ArrayList<Geometry>();
Coordinate c = new GeometryFactory().buildGeometry(geoms).getCentroid()
.getCoordinate();
gData.latLonToLocal = MapUtil.getTransformFromLatLon(MapUtil
.constructStereographic(MapUtil.AWIPS_EARTH_RADIUS,
MapUtil.AWIPS_EARTH_RADIUS, c.y, c.x));
gData.localToLatLon = gData.latLonToLocal.inverse();
for (GeospatialData gd : gData.features) {
Geometry local = JTS.transform(gd.geometry, gData.latLonToLocal);
gd.attributes.put(AREA, local.getArea());
gd.attributes.put(GeospatialDataList.LOCAL_GEOM, local);
gd.attributes.put(GeospatialDataList.LOCAL_PREP_GEOM,
PreparedGeometryFactory.prepare(local));
locals.add(local);
}
Envelope env = new GeometryFactory().buildGeometry(locals)
.getEnvelopeInternal();
IExtent localExtent = new PixelExtent(env.getMinX(), env.getMaxX(),
env.getMinY(), env.getMaxY());
int nx = 600;
int ny = 600;
// Boolean to change the aspect ratio of the extent to
// match
boolean keepAspectRatio = true;
GridSpacing gridSpacing = dialogConfig.getGridSpacing();
if ((gridSpacing != null) && (gridSpacing.getNx() != null)
&& (gridSpacing != null)) {
nx = gridSpacing.getNx();
ny = gridSpacing.getNy();
keepAspectRatio = gridSpacing.isKeepAspectRatio();
}
double xinc, yinc;
double width = localExtent.getWidth();
double height = localExtent.getHeight();
if (!keepAspectRatio) {
xinc = (width / nx);
yinc = (height / ny);
} else {
if (width > height) {
ny = (int) ((height * nx) / width);
} else if (height > width) {
nx = (int) ((width * ny) / height);
}
xinc = yinc = (width / nx);
}
gData.localExtent = new PixelExtent(localExtent.getMinX() - xinc,
localExtent.getMaxX() + xinc, localExtent.getMinY() - yinc,
localExtent.getMaxY() + yinc);
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);
GeospatialData[] timezones = GeospatialFactory.getTimezones();
if (timezones != null) {
for (GeospatialData timezone : timezones) {
if (timezone.attributes.containsKey(gmd.getTimeZoneField())) {
String oneLetterTimezone = String
.valueOf(timezone.attributes.get(gmd
.getTimeZoneField()));
if (timezoneMap.containsKey(oneLetterTimezone) == false) {
timezoneMap.put(oneLetterTimezone, timezone.geometry);
}
}
}
}
}
public GeospatialData[] getGeodataFeatures(String areaSource,
String localizedSite) {
GeospatialDataList geoDataList = getGeodataList(areaSource,
@ -1405,29 +1426,34 @@ public class WarngenLayer extends AbstractStormTrackResource {
DialogConfiguration dc = null;
if (backupSite != null) {
boolean haveBackupConfig = DialogConfiguration.isSiteDialogConfigExtant(backupSite);
boolean haveBackupConfig = DialogConfiguration
.isSiteDialogConfigExtant(backupSite);
if (haveBackupConfig) {
try {
dc = DialogConfiguration.loadDialogConfigNoUser(backupSite);
} catch (Exception e) {
statusHandler.error(String.format(
"Unable to load WarnGen configuration for site %s. Falling back to local configuration.",
getLocalizedSite()), e);
statusHandler
.error(String
.format("Unable to load WarnGen configuration for site %s. Falling back to local configuration.",
getLocalizedSite()), e);
}
} else {
statusHandler.warn(String.format(
"WarnGen configuration for site %s does not exist. Falling back to local configuration.",
backupSite));
statusHandler
.warn(String
.format("WarnGen configuration for site %s does not exist. Falling back to local configuration.",
backupSite));
}
if (dc == null) {
try {
dc = DialogConfiguration.loadDialogConfigNoUser(LocalizationManager
.getInstance().getCurrentSite());
dc = DialogConfiguration
.loadDialogConfigNoUser(LocalizationManager
.getInstance().getCurrentSite());
} catch (Exception e) {
dc = new DialogConfiguration();
statusHandler.error(String.format(
"Unable to load WarnGen configuration for site %s.",
getLocalizedSite()), e);
statusHandler
.error(String
.format("Unable to load WarnGen configuration for site %s.",
getLocalizedSite()), e);
}
}
} else {
@ -1522,8 +1548,8 @@ public class WarngenLayer extends AbstractStormTrackResource {
*/
public Geometry getWarningAreaFromPolygon(Polygon polygon,
AbstractWarningRecord record) {
Map<String, String[]> countyMap = FipsUtil.parseHeader(record
.getCountyheader(), "County");
Map<String, String[]> countyMap = FipsUtil.parseHeader(
record.getCountyheader(), "County");
try {
return getArea(polygon, countyMap);
} catch (Exception e) {
@ -1536,8 +1562,7 @@ public class WarngenLayer extends AbstractStormTrackResource {
* Returns a set of UGCs for each area in the CWA that intersects the given
* polygon.
*/
public Set<String> getUgcsForWatches(Polygon polygon)
throws Exception {
public Set<String> getUgcsForWatches(Polygon polygon) throws Exception {
GeospatialDataAccessor gda = getGeospatialDataAcessor();
boolean isMarineZone = configuration.getGeospatialConfig()
.getAreaSource().equalsIgnoreCase(MARINE);
@ -1573,8 +1598,7 @@ public class WarngenLayer extends AbstractStormTrackResource {
return ugcs;
}
private GeospatialDataAccessor getGeospatialDataAcessor()
throws Exception {
private GeospatialDataAccessor getGeospatialDataAcessor() throws Exception {
GeospatialDataList gdl = searchGeospatialDataAccessor();
if (gdl == null) {
// Cause county geospatial data to be loaded
@ -3221,7 +3245,7 @@ public class WarngenLayer extends AbstractStormTrackResource {
geomCentroid.clear();
for (GeospatialData f : geoData.features) {
Geometry geom = f.getGeometry();
gid = ((CountyUserData)geom.getUserData()).gid;
gid = ((CountyUserData) geom.getUserData()).gid;
geomArea.put(gid, geom.getArea());
geomCentroid.put(gid, geom.getCentroid());
}
@ -3243,8 +3267,10 @@ public class WarngenLayer extends AbstractStormTrackResource {
double areaM, areaN, maxArea = -1.0;
int geomIndex = -1;
int geomNum = warningArea.getNumGeometries();
// Find an unique index for each county in warningArea. If there is more than one index
// for one county, find the one with max area.
/*
* Find a unique index for each county in warningArea. If there is more
* than one index for one county, find the one with max area.
*/
Geometry warningAreaM = null, warningAreaN = null;
for (int i = 0; i < geomNum; i++) {
warningAreaM = warningArea.getGeometryN(i);
@ -3290,47 +3316,59 @@ public class WarngenLayer extends AbstractStormTrackResource {
prefixM = GeometryUtil.getPrefix(warningAreaM.getUserData());
area = warningAreaM.getArea();
if (area < minArea || area / geomArea.get(prefixM) < threshold) {
// Hatched area inside a county is small, move W toward to default centroid
centroid = movePopulatePt(gf, warningAreaM, geomCentroid.get(prefixM), weight);
// Hatched area inside a county is small, move W toward to
// default centroid
centroid = movePopulatePt(gf, warningAreaM,
geomCentroid.get(prefixM), weight);
populatePt = new Coordinate(centroid.getX(), centroid.getY());
populatePtGeom = PolygonUtil.createPolygonByPoints(gf, populatePt, shift);
populatePtGeom = PolygonUtil.createPolygonByPoints(gf,
populatePt, shift);
} else {
// Use the controid of the hatched area in a county
centroid = warningAreaM.getCentroid();
populatePt = new Coordinate(centroid.getX(), centroid.getY());
populatePtGeom = PolygonUtil.createPolygonByPoints(gf, populatePt, shift);
populatePtGeom = PolygonUtil.createPolygonByPoints(gf,
populatePt, shift);
}
for (GeospatialData gd : geoData.features) {
geomN = gd.getGeometry();
CountyUserData cud = (CountyUserData)geomN.getUserData();
CountyUserData cud = (CountyUserData) geomN.getUserData();
prefixN = cud.gid;
if (prefixN.length() > 0 && prefixM.length() > 0 &&
!prefixN.equals(prefixM)) {
if (prefixN.length() > 0 && prefixM.length() > 0
&& !prefixN.equals(prefixM)) {
if (GeometryUtil.contains(geomN, populatePtGeom)) {
// W is inside a county. Use default centroid of a county (not that of its hatched area)
// W is inside a county. Use default centroid of a
// county (not that of its hatched area)
centroid = geomCentroid.get(prefixM);
populatePt = new Coordinate(centroid.getX(), centroid.getY());
populatePtGeom = PolygonUtil.createPolygonByPoints(gf, populatePt, shift);
populatePt = new Coordinate(centroid.getX(),
centroid.getY());
populatePtGeom = PolygonUtil.createPolygonByPoints(gf,
populatePt, shift);
}
loop = 1;
while (GeometryUtil.contains(geomN, populatePtGeom) && loop < maxLoop) {
// W is still inside a county, move W to the largest quadrant
warningAreaM = findLargestQuadrant(gf, warningAreaM);
while (GeometryUtil.contains(geomN, populatePtGeom)
&& loop < maxLoop) {
// W is still inside a county, move W to the largest
// quadrant
warningAreaM = findLargestQuadrant(gf, warningAreaM);
centroid = warningAreaM.getCentroid();
populatePt = new Coordinate(centroid.getX(), centroid.getY());
populatePtGeom = PolygonUtil.createPolygonByPoints(gf, populatePt, shift);
populatePt = new Coordinate(centroid.getX(),
centroid.getY());
populatePtGeom = PolygonUtil.createPolygonByPoints(gf,
populatePt, shift);
loop += 1;
}
}
}
populatePtMap.put(prefixM, populatePt);
}
for (String key: populatePtMap.keySet()) {
for (String key : populatePtMap.keySet()) {
state.strings.put(populatePtMap.get(key), "W");
}
}
private Point movePopulatePt(GeometryFactory gf, Geometry geom, Point point, double weight) {
private Point movePopulatePt(GeometryFactory gf, Geometry geom,
Point point, double weight) {
Point centroid = geom.getCentroid();
Coordinate coord = new Coordinate();
coord.x = centroid.getX() * weight + point.getX() * (1.0 - weight);
@ -3605,24 +3643,15 @@ public class WarngenLayer extends AbstractStormTrackResource {
return localToLatLon(result);
}
/**
* If g is a GeometryCollection, find the largest Geomery in it; otherwise (i.e., g is Geometry), return g.
*
* @param g
* A Geometry or a GeometryCollection.
* @return Geometry
*/
/**
/**
* Split the hatched area into four quadrants, and return the largest one.
*
* @param hatchedArea
* The initial hatched area or its a sub area.
* @param gf
* - factory to use to generate polygon
* @param geom
* The geometry of a county/zone.
* @return Geometry
* The geometey of largest quadrant among the four, which are the result of
* splitting of a county's hatched area.
* - the geometry of a county/zone.
* @return geometry - The Geometey of largest quadrant among the four, which
* are the result of splitting of a county's hatched area.
*/
private Geometry findLargestQuadrant(GeometryFactory gf, Geometry geom) {
Geometry envelope = geom.getEnvelope();
@ -3635,9 +3664,11 @@ public class WarngenLayer extends AbstractStormTrackResource {
double largestArea = -1.0, area = -1.0;
int index = -1;
for (int i = 0; i < size; i++) {
quadrants[i] = PolygonUtil.createPolygonByPoints(gf, envCoords[i], centroidCoord);
quadrants[i] = PolygonUtil.createPolygonByPoints(gf, envCoords[i],
centroidCoord);
try {
intersections[i] = GeometryUtil.intersection(quadrants[i], geom);
intersections[i] = GeometryUtil
.intersection(quadrants[i], geom);
area = intersections[i].getArea();
if (area > largestArea) {
largestArea = area;

View file

@ -15,6 +15,13 @@
<constructor-arg value="com.raytheon.uf.common.dataplugin.warning.gis.GenerateGeospatialDataRequest"/>
<constructor-arg ref="generateGeospatialDataRequestHandler"/>
</bean>
<bean id="generateGeospatialTimeSetHandler" class="com.raytheon.edex.plugin.warning.gis.GenerateGeospatialTimeSetRequestHandler"/>
<bean id="generateGeospatialTimesetRequest" factory-bean="handlerRegistry" factory-method="register">
<constructor-arg value="com.raytheon.uf.common.dataplugin.warning.gis.GenerateGeospatialTimeSetRequest"/>
<constructor-arg ref="generateGeospatialTimeSetHandler"/>
</bean>
<camelContext id="geospatialDataGenerator-camel"
xmlns="http://camel.apache.org/schema/spring"

View file

@ -0,0 +1,58 @@
/**
* 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.warning.gis;
import com.raytheon.uf.common.dataplugin.warning.gis.GenerateGeospatialTimeSetRequest;
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialTimeSet;
import com.raytheon.uf.common.serialization.comm.IRequestHandler;
/**
* Generate geospatial time set data on demand.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2014 3353 rferrel Initial creation
*
* </pre>
*
* @author rferrel
* @version 1.0
*/
public class GenerateGeospatialTimeSetRequestHandler implements
IRequestHandler<GenerateGeospatialTimeSetRequest> {
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.common.serialization.comm.IRequestHandler#handleRequest
* (com.raytheon.uf.common.serialization.comm.IServerRequest)
*/
@Override
public GeospatialTimeSet handleRequest(
GenerateGeospatialTimeSetRequest request) throws Exception {
return GeospatialDataGenerator.getGeospatialTimeset(request.getSite());
}
}

View file

@ -108,6 +108,7 @@ import com.vividsolutions.jts.simplify.TopologyPreservingSimplifier;
* Mar 19, 2014 2726 rjpeter Made singleton instance.
* Apr 29, 2014 3033 jsanchez Properly handled site and back up site files.
* Jul 15, 2014 3352 rferrel Better logging and threading added.
* Aug 21, 2014 3353 rferrel Added getGeospatialTimeset and cluster locking of METADATA_FILE.
* </pre>
*
* @author rjpeter
@ -159,6 +160,44 @@ public class GeospatialDataGenerator {
}
}
/**
* Common format for cluster tasks details entry.
*
* @param site
* @param fileName
* @return details
*/
private static String getDetails(String site, String fileName) {
return String.format("%s%s%s", site, File.separator, fileName);
}
/**
* Lock cluster task for the site's metadata file and obtain the file's
* geospatial time set.
*
* @param site
* @return geospatialTimeSet
*/
public static GeospatialTimeSet getGeospatialTimeset(String site) {
String metadataDetails = getDetails(site,
GeospatialFactory.METADATA_FILE
.substring(GeospatialFactory.METADATA_FILE
.lastIndexOf(File.separator) + 1));
ClusterTask ct = null;
try {
do {
ct = ClusterLockUtils.lock(CLUSTER_NAME, metadataDetails,
TIME_OUT, true);
} while (!LockState.SUCCESSFUL.equals(ct.getLockState()));
return GeospatialFactory.getGeospatialTimeSet(site);
} finally {
if (ct != null) {
ClusterLockUtils.unlock(ct, false);
}
}
}
/**
* The constructor.
*/
@ -300,7 +339,7 @@ public class GeospatialDataGenerator {
private String getClusterDetails(String site, GeospatialMetadata metaData) {
String fileName = generateGeoDataFilename(metaData);
return String.format("%s%s%s", site, File.separator, fileName);
return getDetails(site, fileName);
}
public GeospatialDataSet generateGeoSpatialList(String site,
@ -341,7 +380,8 @@ public class GeospatialDataGenerator {
// NOTE: changes to curTimeMap are not persisted back to the
// GeospatialTimeSet
Map<GeospatialMetadata, GeospatialTime> lastRunTimeMap = GeospatialFactory
.loadLastRunGeoTimeSet(site);
.loadLastRunGeoTimeSet(getGeospatialTimeset(site));
GeospatialTime lastRunTime = lastRunTimeMap.get(metaData);
boolean generate = true;
if (curTime.equals(lastRunTime)) {
@ -764,32 +804,63 @@ public class GeospatialDataGenerator {
return rval;
}
/**
* Save data in the desired file and update the meta data file. Assumes
* already have a cluster lock for the data file. A cluster lock is obtained
* on the metadata file prior to updating it.
*
* @param site
* @param times
* @param curTime
* @param geoData
* @throws SerializationException
* @throws LocalizationException
* @throws JAXBException
*/
private void persistGeoData(String site,
Map<GeospatialMetadata, GeospatialTime> times,
GeospatialTime curTime, GeospatialDataSet geoData)
throws SerializationException, LocalizationException, JAXBException {
String fileName = generateGeoDataFilename(curTime.getMetaData());
String metadataDetails = getDetails(site,
GeospatialFactory.METADATA_FILE
.substring(GeospatialFactory.METADATA_FILE
.lastIndexOf(File.separator) + 1));
IPathManager pathMgr = PathManagerFactory.getPathManager();
LocalizationContext context = pathMgr.getContext(
LocalizationType.COMMON_STATIC, LocalizationLevel.CONFIGURED);
context.setContextName(site);
ClusterTask ct = null;
try {
do {
ct = ClusterLockUtils.lock(CLUSTER_NAME, metadataDetails,
TIME_OUT, true);
} while (!LockState.SUCCESSFUL.equals(ct.getLockState()));
byte[] data = SerializationUtil.transformToThrift(geoData);
LocalizationFile lf = pathMgr.getLocalizationFile(context,
GeospatialFactory.GEO_DIR + fileName);
lf.write(data);
IPathManager pathMgr = PathManagerFactory.getPathManager();
LocalizationContext context = pathMgr.getContext(
LocalizationType.COMMON_STATIC,
LocalizationLevel.CONFIGURED);
context.setContextName(site);
curTime.setFileName(fileName);
times.put(curTime.getMetaData(), curTime);
byte[] data = SerializationUtil.transformToThrift(geoData);
LocalizationFile lf = pathMgr.getLocalizationFile(context,
GeospatialFactory.GEO_DIR + fileName);
lf.write(data);
GeospatialTimeSet set = new GeospatialTimeSet();
set.setData(new ArrayList<GeospatialTime>(times.values()));
String xml = jaxb.marshalToXml(set);
curTime.setFileName(fileName);
times.put(curTime.getMetaData(), curTime);
lf = pathMgr.getLocalizationFile(context,
GeospatialFactory.METADATA_FILE);
lf.write(xml.getBytes());
GeospatialTimeSet set = new GeospatialTimeSet();
set.setData(new ArrayList<GeospatialTime>(times.values()));
String xml = jaxb.marshalToXml(set);
lf = pathMgr.getLocalizationFile(context,
GeospatialFactory.METADATA_FILE);
lf.write(xml.getBytes());
} finally {
if (ct != null) {
ClusterLockUtils.unlock(ct, false);
}
}
}
private void deleteGeomFiles(String site, GeospatialTime time) {

View file

@ -0,0 +1,54 @@
/**
* 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.uf.common.dataplugin.warning.gis;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.serialization.comm.IServerRequest;
/**
* A request for geospatial time set data for desired site.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 19, 2014 rferrel Initial creation
*
* </pre>
*
* @author rferrel
* @version 1.0
*/
@DynamicSerialize
public class GenerateGeospatialTimeSetRequest implements IServerRequest {
@DynamicSerializeElement
private String site;
public String getSite() {
return site;
}
public void setSite(String site) {
this.site = site;
}
}

View file

@ -64,6 +64,7 @@ import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
* Jan 9, 2013 15600 Qinglu Lin Execute "timezones = myTimeZones;" even if timezones != null.
* Oct 22, 2013 2361 njensen Use JAXBManager for XML
* Jun 17, 2014 DR 17390 Qinglu Lin Updated getMetaDataMap() for lonField and latField.
* Aug 21, 2014 3353 rferrel Generating Geo Spatial data set no longer on the UI thread.
*
* </pre>
*
@ -85,12 +86,25 @@ public class GeospatialFactory {
private static SingleTypeJAXBManager<GeospatialTimeSet> jaxb = SingleTypeJAXBManager
.createWithoutException(GeospatialTimeSet.class);
public static GeospatialData[] getGeoSpatialList(String site,
GeospatialMetadata metaData) throws SpatialException {
Map<GeospatialMetadata, GeospatialTime> lastRunTimeMap = loadLastRunGeoTimeSet(site);
/**
* Get existing geospatial data set or null if it does not exist.
*
* @param site
* @param metaData
* @return dataSet
* @throws Exception
*/
public static GeospatialDataSet getGeoSpatialDataSet(String site,
GeospatialMetadata metaData) throws Exception {
GenerateGeospatialTimeSetRequest request = new GenerateGeospatialTimeSetRequest();
request.setSite(site);
GeospatialTimeSet timeset = null;
timeset = (GeospatialTimeSet) RequestRouter.route(request);
Map<GeospatialMetadata, GeospatialTime> lastRunTimeMap = loadLastRunGeoTimeSet(timeset);
GeospatialTime lastRunTime = lastRunTimeMap.get(metaData);
GeospatialDataSet dataSet = null;
boolean generate = true;
if (lastRunTime != null) {
System.out.println("Loading areas from disk");
// load from disk
@ -103,22 +117,43 @@ public class GeospatialFactory {
statusHandler.handle(Priority.WARN,
"Failed to load area geometry files from disk", e);
}
generate = dataSet == null;
}
return dataSet;
}
if (generate) {
// send request to server
GenerateGeospatialDataRequest request = new GenerateGeospatialDataRequest();
request.setMetaData(metaData);
request.setSite(site);
try {
dataSet = (GeospatialDataSet) RequestRouter.route(request);
} catch (Exception e) {
throw new SpatialException(
"Server failed to generate area geometry files.", e);
}
/**
* Force creation of the desired geospatial data set.
*
* @param site
* @param metaData
* @return dataSet
* @throws SpatialException
*/
public static GeospatialDataSet generateGeospatialDataSet(String site,
GeospatialMetadata metaData) throws SpatialException {
// send request to server
GenerateGeospatialDataRequest request = new GenerateGeospatialDataRequest();
request.setMetaData(metaData);
request.setSite(site);
GeospatialDataSet dataSet;
try {
dataSet = (GeospatialDataSet) RequestRouter.route(request);
} catch (Exception e) {
throw new SpatialException(
"Server failed to generate area geometry files.", e);
}
return dataSet;
}
/**
* Convert the geospatial data set into array of geospatial data.
*
* @param dataSet
* @param metaData
* @return areas
*/
public static GeospatialData[] getGeoSpatialList(GeospatialDataSet dataSet,
GeospatialMetadata metaData) {
GeospatialData[] areas = dataSet.getAreas();
GeospatialData[] parentAreas = dataSet.getParentAreas();
@ -163,13 +198,27 @@ public class GeospatialFactory {
}
/**
* Convert the set's data into a map. When no data return empty map.
*
* @param site
* @return
* @param timeSet
* @return geospatialTimeMap
*/
public static Map<GeospatialMetadata, GeospatialTime> loadLastRunGeoTimeSet(
String site) {
Map<GeospatialMetadata, GeospatialTime> rval = null;
GeospatialTimeSet timeSet) {
Map<GeospatialMetadata, GeospatialTime> rval = timeSet.getDataAsMap();
if (rval == null) {
rval = new HashMap<GeospatialMetadata, GeospatialTime>();
}
return rval;
}
/**
* Unmarshal the GeospatialTimeset in the site's metadata file.
*
* @param site
* @return geospatialTimeset
*/
public static GeospatialTimeSet getGeospatialTimeSet(String site) {
IPathManager pathMgr = PathManagerFactory.getPathManager();
LocalizationContext context = pathMgr.getContext(
LocalizationType.COMMON_STATIC, LocalizationLevel.CONFIGURED);
@ -179,26 +228,13 @@ public class GeospatialFactory {
METADATA_FILE);
if (lf.exists()) {
try {
rval = jaxb.unmarshalFromXmlFile(lf.getFile()).getDataAsMap();
return jaxb.unmarshalFromXmlFile(lf.getFile());
} catch (Exception e) {
statusHandler
.handle(Priority.WARN,
"Error occurred deserializing geometry metadata. Deleting metadata and recreating.",
e);
try {
lf.delete();
} catch (Exception e1) {
statusHandler.handle(Priority.WARN,
"Error occurred deleting geometry metadata.", e1);
}
rval = new HashMap<GeospatialMetadata, GeospatialTime>();
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(),
e);
}
} else {
rval = new HashMap<GeospatialMetadata, GeospatialTime>();
}
return rval;
return new GeospatialTimeSet();
}
public static Map<String, GeospatialMetadata> getMetaDataMap(

View file

@ -24,6 +24,9 @@ import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
/**
* Wrapper for GeospatialConfiguration representing last time a the geometry
* data was created for a warngen configuration
@ -35,6 +38,7 @@ import javax.xml.bind.annotation.XmlElement;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 15, 2011 rjpeter Initial creation
* Aug 21, 2014 3353 rferrel Allow serialization.
*
* </pre>
*
@ -43,20 +47,26 @@ import javax.xml.bind.annotation.XmlElement;
*/
@XmlAccessorType(XmlAccessType.NONE)
@DynamicSerialize
public class GeospatialTime {
@XmlElement
@DynamicSerializeElement
private GeospatialMetadata metaData;
@XmlAttribute
@DynamicSerializeElement
private String fileName;
@XmlAttribute
@DynamicSerializeElement
private long areaSourceTime;
@XmlAttribute
@DynamicSerializeElement
private long timeZoneSourceTime;
@XmlAttribute
@DynamicSerializeElement
private long parentSourceTime;
public GeospatialMetadata getMetaData() {

View file

@ -28,6 +28,10 @@ import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.serialization.comm.IServerRequest;
/**
* Collection of GeospatialTime representing a set of generated geospatial data
* for warngen configurations
@ -39,6 +43,7 @@ import javax.xml.bind.annotation.XmlRootElement;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 15, 2011 rjpeter Initial creation
* Aug 21, 2014 3353 rferrel Allow serialization.
*
* </pre>
*
@ -48,8 +53,10 @@ import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "geoTimeSet")
public class GeospatialTimeSet {
@DynamicSerialize
public class GeospatialTimeSet implements IServerRequest {
@XmlElement(name = "geoTime")
@DynamicSerializeElement
private List<GeospatialTime> data;
public List<GeospatialTime> getData() {