diff --git a/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/GenerateGeoDataSetDialog.java b/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/GenerateGeoDataSetDialog.java new file mode 100644 index 0000000000..04466828d7 --- /dev/null +++ b/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/GenerateGeoDataSetDialog.java @@ -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. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 20, 2014 3353       rferrel     Initial creation
+ * 
+ * 
+ * + * @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(); + } + } + } +} diff --git a/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java b/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java index 97db80d599..88b938d093 100644 --- a/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java +++ b/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java @@ -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. * * * @author mschenke @@ -231,6 +237,7 @@ public class WarngenLayer extends AbstractStormTrackResource { String uniqueFip = null; Map geomArea = new HashMap(); + Map geomCentroid = new HashMap(); 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 siteMap = new HashMap(); @@ -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 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 geoms = new ArrayList( - 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 locals = new ArrayList(); - - 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 geoms = new ArrayList(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 locals = new ArrayList(); + + 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 countyMap = FipsUtil.parseHeader(record - .getCountyheader(), "County"); + Map 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 getUgcsForWatches(Polygon polygon) - throws Exception { + public Set 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; diff --git a/edexOsgi/com.raytheon.edex.plugin.warning/res/spring/warning-request.xml b/edexOsgi/com.raytheon.edex.plugin.warning/res/spring/warning-request.xml index 39556857c7..28860d3bb6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.warning/res/spring/warning-request.xml +++ b/edexOsgi/com.raytheon.edex.plugin.warning/res/spring/warning-request.xml @@ -15,6 +15,13 @@ + + + + + + + + * + * SOFTWARE HISTORY + * + * Date Ticket# Engineer Description + * ------------ ---------- ----------- -------------------------- + * Aug 19, 2014 3353 rferrel Initial creation + * + * + * + * @author rferrel + * @version 1.0 + */ + +public class GenerateGeospatialTimeSetRequestHandler implements + IRequestHandler { + + /* + * (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()); + } +} diff --git a/edexOsgi/com.raytheon.edex.plugin.warning/src/com/raytheon/edex/plugin/warning/gis/GeospatialDataGenerator.java b/edexOsgi/com.raytheon.edex.plugin.warning/src/com/raytheon/edex/plugin/warning/gis/GeospatialDataGenerator.java index 6285223a40..ab40c5a799 100644 --- a/edexOsgi/com.raytheon.edex.plugin.warning/src/com/raytheon/edex/plugin/warning/gis/GeospatialDataGenerator.java +++ b/edexOsgi/com.raytheon.edex.plugin.warning/src/com/raytheon/edex/plugin/warning/gis/GeospatialDataGenerator.java @@ -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. * * * @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 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 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(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(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) { diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GenerateGeospatialTimeSetRequest.java b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GenerateGeospatialTimeSetRequest.java new file mode 100644 index 0000000000..a241ab44c3 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GenerateGeospatialTimeSetRequest.java @@ -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. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 19, 2014            rferrel     Initial creation
+ * 
+ * 
+ * + * @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; + } +} diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialFactory.java b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialFactory.java index b7359ec1cd..6a56e58bee 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialFactory.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialFactory.java @@ -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. * * * @@ -85,12 +86,25 @@ public class GeospatialFactory { private static SingleTypeJAXBManager jaxb = SingleTypeJAXBManager .createWithoutException(GeospatialTimeSet.class); - public static GeospatialData[] getGeoSpatialList(String site, - GeospatialMetadata metaData) throws SpatialException { - Map 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 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 loadLastRunGeoTimeSet( - String site) { - Map rval = null; + GeospatialTimeSet timeSet) { + Map rval = timeSet.getDataAsMap(); + if (rval == null) { + rval = new HashMap(); + } + 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(); + statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), + e); } - } else { - rval = new HashMap(); } - - return rval; + return new GeospatialTimeSet(); } public static Map getMetaDataMap( diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTime.java b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTime.java index 1a11dfb91e..5dabf0c9dc 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTime.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTime.java @@ -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. * * * @@ -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() { diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTimeSet.java b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTimeSet.java index 3f86c2ffd0..0749c997ac 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTimeSet.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/gis/GeospatialTimeSet.java @@ -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. * * * @@ -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 data; public List getData() {