Omaha #4354: Modify Damage Path tool so each polygon has its own properties, demo bug fixes.

Change-Id: Ib69c19fa518f5e89638af1eba391ee6954f7891d

Former-commit-id: affaf23ed435cd3dc62e074cf19a8c4c478d7295
This commit is contained in:
David Gillingham 2015-06-18 14:47:58 -05:00
parent 372f7b5362
commit 744b5c3c5b
12 changed files with 320 additions and 120 deletions

View file

@ -3,12 +3,6 @@
<plugin> <plugin>
<extension <extension
point="com.raytheon.viz.ui.contextualMenu"> point="com.raytheon.viz.ui.contextualMenu">
<contextualMenu
actionClass="com.raytheon.uf.viz.damagepath.OpenGeoJsonPropertiesDlgAction"
capabilityClass="com.raytheon.uf.viz.damagepath.DamagePathLayer"
name="Set Properties"
sortID="3">
</contextualMenu>
<contextualMenu <contextualMenu
actionClass="com.raytheon.uf.viz.damagepath.ImportFromDistanceSpeedAction" actionClass="com.raytheon.uf.viz.damagepath.ImportFromDistanceSpeedAction"
capabilityClass="com.raytheon.uf.viz.damagepath.DamagePathLayer" capabilityClass="com.raytheon.uf.viz.damagepath.DamagePathLayer"

View file

@ -23,7 +23,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -32,6 +31,9 @@ import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IMenuManager;
import org.geotools.data.DataUtilities;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeature;
@ -49,15 +51,16 @@ import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
import com.raytheon.uf.common.localization.LocalizationFile; import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.LocalizationFileOutputStream; import com.raytheon.uf.common.localization.LocalizationFileOutputStream;
import com.raytheon.uf.common.localization.PathManagerFactory; import com.raytheon.uf.common.localization.PathManagerFactory;
import com.raytheon.uf.common.util.Pair;
import com.raytheon.uf.viz.core.IGraphicsTarget; import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.LoadProperties; import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.capabilities.EditableCapability;
import com.raytheon.uf.viz.drawing.polygon.DrawablePolygon; import com.raytheon.uf.viz.drawing.polygon.DrawablePolygon;
import com.raytheon.uf.viz.drawing.polygon.PolygonLayer; import com.raytheon.uf.viz.drawing.polygon.PolygonLayer;
import com.raytheon.uf.viz.drawing.polygon.PolygonUtil; import com.raytheon.uf.viz.drawing.polygon.PolygonUtil;
import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.Polygon;
/** /**
@ -78,6 +81,8 @@ import com.vividsolutions.jts.geom.Polygon;
* Jun 08, 2015 4355 dgilling Fix NullPointerException in loadJob. * Jun 08, 2015 4355 dgilling Fix NullPointerException in loadJob.
* Jun 12, 2015 4375 dgilling Fix ConcurrentModificationException in * Jun 12, 2015 4375 dgilling Fix ConcurrentModificationException in
* initInternal. * initInternal.
* Jun 18, 2015 4354 dgilling Allow each polygon to have their own
* properties.
* *
* </pre> * </pre>
* *
@ -111,8 +116,6 @@ public class DamagePathLayer<T extends DamagePathResourceData> extends
private static final String PATH = DIR + IPathManager.SEPARATOR + FILE; private static final String PATH = DIR + IPathManager.SEPARATOR + FILE;
private Map<String, String> featureProperties = Collections.emptyMap();
/** /**
* JVM property to specify the localization level to attempt to save/load * JVM property to specify the localization level to attempt to save/load
* with. Falls back to USER if not defined. * with. Falls back to USER if not defined.
@ -171,7 +174,7 @@ public class DamagePathLayer<T extends DamagePathResourceData> extends
private void setDefaultPolygon() { private void setDefaultPolygon() {
Polygon polygon = PolygonUtil.makeDefaultPolygon(getResourceContainer() Polygon polygon = PolygonUtil.makeDefaultPolygon(getResourceContainer()
.getActiveDisplayPane().getRenderableDisplay()); .getActiveDisplayPane().getRenderableDisplay());
DrawablePolygon drawablePolygon = new DrawablePolygon(polygon, this); DrawablePolygon drawablePolygon = new DamagePathPolygon(polygon, this);
polygons.add(0, drawablePolygon); polygons.add(0, drawablePolygon);
} }
@ -250,18 +253,24 @@ public class DamagePathLayer<T extends DamagePathResourceData> extends
protected void loadDamagePath(LocalizationFile file) { protected void loadDamagePath(LocalizationFile file) {
try { try {
DamagePathLoader loader = new DamagePathLoader(file); DamagePathLoader loader = new DamagePathLoader(file);
Collection<Polygon> newPolygons = loader.getPolygons(); Collection<Pair<Polygon, Map<String, String>>> newData = loader
if (!newPolygons.isEmpty()) { .getDamagePathData();
if (!newData.isEmpty()) {
Collection<DrawablePolygon> newDamagePaths = new ArrayList<>(
newData.size());
for (Pair<Polygon, Map<String, String>> data : newData) {
newDamagePaths.add(new DamagePathPolygon(data.getFirst(),
data.getSecond(), this));
}
/* /*
* specifically call super.resetPolygon() cause * specifically call super.resetPolygon() cause
* this.resetPolygon() will save the file and we don't want to * this.resetPolygon() will save the file and we don't want to
* do that or we could infinite loop of load, save, load, * do that or we could infinite loop of load, save, load,
* save... * save...
*/ */
super.resetPolygons(newPolygons); super.resetPolygons(newDamagePaths);
} }
featureProperties = loader.getProperties();
} catch (Exception e) { } catch (Exception e) {
statusHandler.error( statusHandler.error(
"Error loading damage path file " + file.getName(), e); "Error loading damage path file " + file.getName(), e);
@ -273,8 +282,8 @@ public class DamagePathLayer<T extends DamagePathResourceData> extends
try { try {
fos = file.openOutputStream(); fos = file.openOutputStream();
IGeoJsonService json = new SimpleGeoJsonService(); IGeoJsonService json = new SimpleGeoJsonService();
SimpleFeature feature = buildFeature(); SimpleFeatureCollection featureCollection = buildFeatureCollection();
json.serialize(feature, fos); json.serialize(featureCollection, fos);
fos.closeAndSave(); fos.closeAndSave();
} catch (Throwable t) { } catch (Throwable t) {
if (fos != null) { if (fos != null) {
@ -289,18 +298,16 @@ public class DamagePathLayer<T extends DamagePathResourceData> extends
} }
} }
public SimpleFeature buildFeature() { private SimpleFeature buildFeature(final DamagePathPolygon damagePath) {
Map<String, String> jsonProps = getFeatureProperties(); Map<String, String> jsonProps = damagePath.getProperties();
String id = jsonProps.get(GeoJsonMapUtil.ID_KEY); String id = jsonProps.get(GeoJsonMapUtil.ID_KEY);
SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder(); SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
typeBuilder.setName("feature"); typeBuilder.setName("feature");
Collection<Polygon> polygons = getPolygons(); Geometry polygon = damagePath.getPolygon();
GeometryCollection geomCollection = PolygonUtil.FACTORY
.createGeometryCollection(polygons.toArray(new Geometry[0]));
typeBuilder.setDefaultGeometry("the_geom"); typeBuilder.setDefaultGeometry("the_geom");
typeBuilder.add("the_geom", geomCollection.getClass()); typeBuilder.add("the_geom", polygon.getClass());
Collection<String> keysToIgnore = Arrays.asList(GeoJsonMapUtil.ID_KEY); Collection<String> keysToIgnore = Arrays.asList(GeoJsonMapUtil.ID_KEY);
Set<String> keySet = jsonProps.keySet(); Set<String> keySet = jsonProps.keySet();
@ -315,25 +322,25 @@ public class DamagePathLayer<T extends DamagePathResourceData> extends
SimpleFeatureType type = typeBuilder.buildFeatureType(); SimpleFeatureType type = typeBuilder.buildFeatureType();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);
if (geomCollection != null) { if (polygon != null) {
featureBuilder.add(geomCollection); featureBuilder.add(polygon);
} }
featureBuilder.addAll(values); featureBuilder.addAll(values);
return featureBuilder.buildFeature(id); return featureBuilder.buildFeature(id);
} }
public Map<String, String> getFeatureProperties() { public SimpleFeatureCollection buildFeatureCollection() {
return featureProperties; List<SimpleFeature> features = new ArrayList<>(polygons.size());
} for (DrawablePolygon polygon : polygons) {
features.add(buildFeature((DamagePathPolygon) polygon));
}
public void setFeatureProperties(Map<String, String> featureProperties) { return DataUtilities.collection(features);
this.featureProperties = featureProperties;
saveJob.schedule();
} }
@Override @Override
public void addPolygon(Coordinate[] coords) { public void addPolygon(Coordinate[] coords) {
super.addPolygon(coords); super.addPolygon(new DamagePathPolygon(coords, this));
saveJob.schedule(); saveJob.schedule();
} }
@ -342,4 +349,28 @@ public class DamagePathLayer<T extends DamagePathResourceData> extends
super.deletePolygon(index); super.deletePolygon(index);
saveJob.schedule(); saveJob.schedule();
} }
@Override
public void addContextMenuItems(IMenuManager menuManager, int x, int y) {
if (!getCapability(EditableCapability.class).isEditable()) {
return;
}
super.addContextMenuItems(menuManager, x, y);
int onPolygonIdx = uiInput.pointOnPolygon(x, y);
if (onPolygonIdx >= 0) {
menuManager.add(new OpenGeoJsonPropertiesDlgAction(
(DamagePathPolygon) polygons.get(onPolygonIdx)));
}
}
@Override
protected DrawablePolygon getNewDrawable() {
return new DamagePathPolygon(this);
}
protected void scheduleSaveJob() {
saveJob.schedule();
}
} }

View file

@ -26,11 +26,15 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.Property; import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name; import org.opengis.feature.type.Name;
import com.raytheon.uf.common.json.JsonException; import com.raytheon.uf.common.json.JsonException;
@ -40,6 +44,7 @@ import com.raytheon.uf.common.json.geo.IGeoJsonService;
import com.raytheon.uf.common.json.geo.SimpleGeoJsonService; import com.raytheon.uf.common.json.geo.SimpleGeoJsonService;
import com.raytheon.uf.common.localization.LocalizationFile; import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.exception.LocalizationException; import com.raytheon.uf.common.localization.exception.LocalizationException;
import com.raytheon.uf.common.util.Pair;
import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.operation.valid.IsValidOp; import com.vividsolutions.jts.operation.valid.IsValidOp;
@ -55,6 +60,8 @@ import com.vividsolutions.jts.operation.valid.IsValidOp;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Jun 05, 2015 #4375 dgilling Initial creation * Jun 05, 2015 #4375 dgilling Initial creation
* Jun 18, 2015 #4354 dgilling Support FeatureCollections so each polygon
* can have its own properties.
* *
* </pre> * </pre>
* *
@ -70,9 +77,7 @@ public final class DamagePathLoader {
private static final String INVALID_POLYGON = "Damage path file contains an invalid Polyon at index %d: %s"; private static final String INVALID_POLYGON = "Damage path file contains an invalid Polyon at index %d: %s";
private final Collection<Polygon> polygons; private final Collection<Pair<Polygon, Map<String, String>>> damagePathData;
private final Map<String, String> properties;
public DamagePathLoader(LocalizationFile locFile) public DamagePathLoader(LocalizationFile locFile)
throws LocalizationException, IOException, JsonException { throws LocalizationException, IOException, JsonException {
@ -86,8 +91,7 @@ public final class DamagePathLoader {
private DamagePathLoader(LocalizationFile locFileSource, Path realFileSource) private DamagePathLoader(LocalizationFile locFileSource, Path realFileSource)
throws LocalizationException, IOException, JsonException { throws LocalizationException, IOException, JsonException {
this.polygons = new ArrayList<>(); this.damagePathData = new ArrayList<>();
this.properties = new LinkedHashMap<>();
if (locFileSource != null) { if (locFileSource != null) {
loadFromLocalizationFile(locFileSource); loadFromLocalizationFile(locFileSource);
@ -96,12 +100,8 @@ public final class DamagePathLoader {
} }
} }
public Collection<Polygon> getPolygons() { public Collection<Pair<Polygon, Map<String, String>>> getDamagePathData() {
return polygons; return damagePathData;
}
public Map<String, String> getProperties() {
return properties;
} }
private void loadFromLocalizationFile(final LocalizationFile locFile) private void loadFromLocalizationFile(final LocalizationFile locFile)
@ -120,29 +120,36 @@ public final class DamagePathLoader {
private void loadFromInputStream(final InputStream is) throws JsonException { private void loadFromInputStream(final InputStream is) throws JsonException {
Geometry deserializedGeom = null; Geometry deserializedGeom = null;
Map<String, String> deserializedProps = Collections.emptyMap();
GeoJsonMapUtil geoJsonUtil = new GeoJsonMapUtil(); GeoJsonMapUtil geoJsonUtil = new GeoJsonMapUtil();
/* /*
* For compatibility with any users that may have an autosaved damage * For compatibility with any users that may have an autosaved damage
* path file from build 15.1, we'll support deserializing both Geometry * path file from previous builds, we'll support deserializing Geometry,
* and Feature GeoJSON types. * Feature and FeatureCollection GeoJSON types.
* *
* TODO: remove this code for code that just expects the file to always * TODO: remove this code for code that just expects the file to always
* be a Feature. * be a FeatureCollection.
*/ */
Map<String, Object> jsonObject = (Map<String, Object>) new BasicJsonService() Map<String, Object> jsonObject = (Map<String, Object>) new BasicJsonService()
.deserialize(is, LinkedHashMap.class); .deserialize(is, LinkedHashMap.class);
String geoJsonType = jsonObject.get(GeoJsonMapUtil.TYPE_KEY).toString(); String geoJsonType = jsonObject.get(GeoJsonMapUtil.TYPE_KEY).toString();
if (geoJsonType.equals(GeoJsonMapUtil.FEATURE_TYPE)) { if (geoJsonType.equals(GeoJsonMapUtil.FEATURE_COLL_TYPE)) {
FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = geoJsonUtil
.populateFeatureCollection(jsonObject);
populateDataFromFeatureCollection(featureCollection);
return;
} else if (geoJsonType.equals(GeoJsonMapUtil.FEATURE_TYPE)) {
SimpleFeature feature = geoJsonUtil.populateFeature(jsonObject); SimpleFeature feature = geoJsonUtil.populateFeature(jsonObject);
deserializedGeom = (Geometry) feature.getDefaultGeometry(); deserializedGeom = (Geometry) feature.getDefaultGeometry();
Name defaultGeomAttrib = feature.getDefaultGeometryProperty() Name defaultGeomAttrib = feature.getDefaultGeometryProperty()
.getName(); .getName();
properties.put(GeoJsonMapUtil.ID_KEY, feature.getID()); deserializedProps = new LinkedHashMap<>();
deserializedProps.put(GeoJsonMapUtil.ID_KEY, feature.getID());
for (Property p : feature.getProperties()) { for (Property p : feature.getProperties()) {
if (!defaultGeomAttrib.equals(p.getName())) { if (!defaultGeomAttrib.equals(p.getName())) {
properties.put(p.getName().toString(), p.getValue() deserializedProps.put(p.getName().toString(), p.getValue()
.toString()); .toString());
} }
} }
@ -160,7 +167,9 @@ public final class DamagePathLoader {
Polygon newPolygon = (Polygon) geomN; Polygon newPolygon = (Polygon) geomN;
IsValidOp validator = new IsValidOp(newPolygon); IsValidOp validator = new IsValidOp(newPolygon);
if (validator.isValid()) { if (validator.isValid()) {
polygons.add(newPolygon); Pair<Polygon, Map<String, String>> polygonAndProps = new Pair<>(
newPolygon, deserializedProps);
damagePathData.add(polygonAndProps);
} else { } else {
throw new JsonException(String.format(INVALID_POLYGON, i, throw new JsonException(String.format(INVALID_POLYGON, i,
validator.getValidationError())); validator.getValidationError()));
@ -190,32 +199,49 @@ public final class DamagePathLoader {
private void loadFromInputStreamFuture(final InputStream is) private void loadFromInputStreamFuture(final InputStream is)
throws JsonException { throws JsonException {
IGeoJsonService json = new SimpleGeoJsonService(); IGeoJsonService json = new SimpleGeoJsonService();
SimpleFeature feature = json.deserializeFeature(is); FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = json
Geometry featureGeom = (Geometry) feature.getDefaultGeometry(); .deserializeFeatureCollection(is);
populateDataFromFeatureCollection(featureCollection);
}
int numGeometries = featureGeom.getNumGeometries(); private void populateDataFromFeatureCollection(
for (int i = 0; i < numGeometries; i++) { FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection)
Geometry geomN = featureGeom.getGeometryN(i); throws JsonException {
if (geomN instanceof Polygon) { try (FeatureIterator<SimpleFeature> iter = featureCollection.features()) {
Polygon newPolygon = (Polygon) geomN; int featureIdx = 0;
IsValidOp validator = new IsValidOp(newPolygon); while (iter.hasNext()) {
if (validator.isValid()) { SimpleFeature feature = iter.next();
polygons.add(newPolygon);
Geometry geom = (Geometry) feature.getDefaultGeometry();
if (geom instanceof Polygon) {
Polygon newPolygon = (Polygon) geom;
IsValidOp validator = new IsValidOp(newPolygon);
if (!validator.isValid()) {
throw new JsonException(String.format(INVALID_POLYGON,
featureIdx, validator.getValidationError()));
}
Map<String, String> properties = new LinkedHashMap<>();
Name defaultGeomAttrib = feature
.getDefaultGeometryProperty().getName();
properties.put(GeoJsonMapUtil.ID_KEY, feature.getID());
for (Property p : feature.getProperties()) {
if (!defaultGeomAttrib.equals(p.getName())) {
properties.put(p.getName().toString(), p.getValue()
.toString());
}
}
Pair<Polygon, Map<String, String>> polygonAndProps = new Pair<>(
newPolygon, properties);
damagePathData.add(polygonAndProps);
} else { } else {
throw new JsonException(String.format(INVALID_POLYGON, i, throw new JsonException(String.format(
validator.getValidationError())); UNSUPPORTED_GEOM_TYPE, geom.getGeometryType(),
featureIdx));
} }
} else {
throw new JsonException(String.format(UNSUPPORTED_GEOM_TYPE,
geomN.getGeometryType(), i));
}
}
Name defaultGeomAttrib = feature.getDefaultGeometryProperty().getName(); featureIdx++;
properties.put(GeoJsonMapUtil.ID_KEY, feature.getID());
for (Property p : feature.getProperties()) {
if (!defaultGeomAttrib.equals(p.getName())) {
properties.put(p.getName().toString(), p.getValue().toString());
} }
} }
} }

View file

@ -0,0 +1,91 @@
/**
* 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.viz.damagepath;
import java.util.Collections;
import java.util.Map;
import com.raytheon.uf.viz.drawing.polygon.DrawablePolygon;
import com.raytheon.uf.viz.drawing.polygon.PolygonLayer;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Polygon;
/**
* Extension of {@code DrawablePolygon} to support GeoJSON properties.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 18, 2015 #4354 dgilling Initial creation
*
* </pre>
*
* @author dgilling
* @version 1.0
*/
public class DamagePathPolygon extends DrawablePolygon {
private static final Map<String, String> DEFAULT_PROPS = Collections
.emptyMap();
private Map<String, String> properties;
public DamagePathPolygon(PolygonLayer<?> polygonLayer) {
super(polygonLayer);
this.properties = DEFAULT_PROPS;
}
public DamagePathPolygon(Polygon polygon, PolygonLayer<?> polygonLayer) {
this(polygon, DEFAULT_PROPS, polygonLayer);
}
public DamagePathPolygon(Polygon polygon, Map<String, String> properties,
PolygonLayer<?> polygonLayer) {
super(polygon, polygonLayer);
this.properties = properties;
}
public DamagePathPolygon(Coordinate[] coords, PolygonLayer<?> polygonLayer) {
super(coords, polygonLayer);
this.properties = DEFAULT_PROPS;
}
@Override
public void resetPolygon(DrawablePolygon newPolygon) {
super.resetPolygon(newPolygon);
if (newPolygon instanceof DamagePathPolygon) {
DamagePathPolygon newDamagePath = (DamagePathPolygon) newPolygon;
properties = newDamagePath.getProperties();
}
}
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
}

View file

@ -25,7 +25,7 @@ import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Shell;
import org.opengis.feature.simple.SimpleFeature; import org.geotools.data.simple.SimpleFeatureCollection;
import com.raytheon.uf.common.json.geo.IGeoJsonService; import com.raytheon.uf.common.json.geo.IGeoJsonService;
import com.raytheon.uf.common.json.geo.SimpleGeoJsonService; import com.raytheon.uf.common.json.geo.SimpleGeoJsonService;
@ -34,7 +34,6 @@ import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.viz.core.VizApp; import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.viz.ui.VizWorkbenchManager; import com.raytheon.viz.ui.VizWorkbenchManager;
import com.raytheon.viz.ui.cmenu.AbstractRightClickAction; import com.raytheon.viz.ui.cmenu.AbstractRightClickAction;
import com.vividsolutions.jts.geom.Geometry;
/** /**
* Action to export a damage path as GeoJSON to a file specified by the user. * Action to export a damage path as GeoJSON to a file specified by the user.
@ -50,6 +49,8 @@ import com.vividsolutions.jts.geom.Geometry;
* Jun 05, 2015 4375 dgilling Prompt user before exporting feature * Jun 05, 2015 4375 dgilling Prompt user before exporting feature
* with no polygons. * with no polygons.
* Jun 09, 2015 4355 dgilling Rename action for UI. * Jun 09, 2015 4355 dgilling Rename action for UI.
* Jun 18, 2015 #4354 dgilling Support FeatureCollections so each
* polygon can have its own properties.
* *
* </pre> * </pre>
* *
@ -85,11 +86,10 @@ public class ExportDamagePathAction extends AbstractRightClickAction {
if (filename != null) { if (filename != null) {
DamagePathLayer<?> layer = (DamagePathLayer<?>) getSelectedRsc(); DamagePathLayer<?> layer = (DamagePathLayer<?>) getSelectedRsc();
SimpleFeature feature = layer.buildFeature(); SimpleFeatureCollection featureCollection = layer
.buildFeatureCollection();
Geometry defaultGeometry = (Geometry) feature if (featureCollection.size() < 1) {
.getDefaultGeometry();
if (defaultGeometry.getNumGeometries() < 1) {
boolean export = MessageDialog.openConfirm(shell, boolean export = MessageDialog.openConfirm(shell,
CONFIRM_DLG_TITLE, CONFIRM_DLG_MSG); CONFIRM_DLG_TITLE, CONFIRM_DLG_MSG);
if (!export) { if (!export) {
@ -101,7 +101,7 @@ public class ExportDamagePathAction extends AbstractRightClickAction {
try (FileOutputStream fos = new FileOutputStream(filename)) { try (FileOutputStream fos = new FileOutputStream(filename)) {
IGeoJsonService json = new SimpleGeoJsonService(); IGeoJsonService json = new SimpleGeoJsonService();
json.serialize(feature, fos); json.serialize(featureCollection, fos);
} catch (Exception e) { } catch (Exception e) {
statusHandler.error( statusHandler.error(
"Error exporting damage path file to " "Error exporting damage path file to "

View file

@ -22,7 +22,7 @@ package com.raytheon.uf.viz.damagepath;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import org.opengis.feature.simple.SimpleFeature; import org.geotools.data.simple.SimpleFeatureCollection;
import com.raytheon.uf.common.damagepath.request.ExportToLdadRequest; import com.raytheon.uf.common.damagepath.request.ExportToLdadRequest;
import com.raytheon.uf.common.json.JsonException; import com.raytheon.uf.common.json.JsonException;
@ -46,6 +46,8 @@ import com.raytheon.viz.ui.cmenu.AbstractRightClickAction;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Jun 08, 2015 #4355 dgilling Initial creation * Jun 08, 2015 #4355 dgilling Initial creation
* Jun 18, 2015 #4354 dgilling Support FeatureCollections so each
* polygon can have its own properties.
* *
* </pre> * </pre>
* *
@ -69,9 +71,10 @@ public class ExportToLdadAction extends AbstractRightClickAction {
try (ByteArrayOutputStream outStream = new ByteArrayOutputStream()) { try (ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
DamagePathLayer<?> layer = (DamagePathLayer<?>) getSelectedRsc(); DamagePathLayer<?> layer = (DamagePathLayer<?>) getSelectedRsc();
SimpleFeature feature = layer.buildFeature(); SimpleFeatureCollection featureCollection = layer
.buildFeatureCollection();
new SimpleGeoJsonService().serialize(feature, outStream); new SimpleGeoJsonService().serialize(featureCollection, outStream);
jsonData = outStream.toByteArray(); jsonData = outStream.toByteArray();
} catch (JsonException | IOException e) { } catch (JsonException | IOException e) {
statusHandler.error( statusHandler.error(

View file

@ -19,7 +19,9 @@
**/ **/
package com.raytheon.uf.viz.damagepath; package com.raytheon.uf.viz.damagepath;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.FileDialog;
@ -28,7 +30,11 @@ import org.eclipse.swt.widgets.Shell;
import com.raytheon.uf.common.json.JsonException; import com.raytheon.uf.common.json.JsonException;
import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.util.Pair;
import com.raytheon.uf.viz.core.VizApp; import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.capabilities.EditableCapability;
import com.raytheon.uf.viz.drawing.polygon.DrawablePolygon;
import com.raytheon.viz.ui.VizWorkbenchManager; import com.raytheon.viz.ui.VizWorkbenchManager;
import com.raytheon.viz.ui.cmenu.AbstractRightClickAction; import com.raytheon.viz.ui.cmenu.AbstractRightClickAction;
import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.Polygon;
@ -47,6 +53,8 @@ import com.vividsolutions.jts.geom.Polygon;
* Apr 23, 2015 4354 dgilling Support GeoJSON Feature objects. * Apr 23, 2015 4354 dgilling Support GeoJSON Feature objects.
* Jun 03, 2015 4375 dgilling Support multiple polygons. * Jun 03, 2015 4375 dgilling Support multiple polygons.
* Jun 09, 2015 4355 dgilling Rename action for UI. * Jun 09, 2015 4355 dgilling Rename action for UI.
* Jun 18, 2015 4354 dgilling Support modifications to loader so each
* polygon can have its own properties.
* *
* </pre> * </pre>
* *
@ -80,10 +88,16 @@ public class ImportDamagePathAction extends AbstractRightClickAction {
try { try {
DamagePathLoader loader = new DamagePathLoader(filename); DamagePathLoader loader = new DamagePathLoader(filename);
Collection<Polygon> newPolygons = loader.getPolygons(); Collection<Pair<Polygon, Map<String, String>>> newData = loader
if (!newPolygons.isEmpty()) { .getDamagePathData();
layer.resetPolygons(newPolygons); if (!newData.isEmpty()) {
layer.setFeatureProperties(loader.getProperties()); Collection<DrawablePolygon> newDamagePaths = new ArrayList<>(
newData.size());
for (Pair<Polygon, Map<String, String>> data : newData) {
newDamagePaths.add(new DamagePathPolygon(data
.getFirst(), data.getSecond(), layer));
}
layer.resetPolygons(newDamagePaths);
} else { } else {
throw new JsonException( throw new JsonException(
"Damage path file contains no polygons."); "Damage path file contains no polygons.");
@ -96,4 +110,10 @@ public class ImportDamagePathAction extends AbstractRightClickAction {
} }
}); });
} }
@Override
public boolean isEnabled() {
AbstractVizResource<?, ?> layer = getSelectedRsc();
return layer.getCapability(EditableCapability.class).isEditable();
}
} }

View file

@ -25,6 +25,7 @@ import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.uf.viz.core.drawables.ResourcePair; import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource; import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.ResourceList; import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.capabilities.EditableCapability;
import com.raytheon.viz.awipstools.ui.layer.DistanceSpeedLayer; import com.raytheon.viz.awipstools.ui.layer.DistanceSpeedLayer;
import com.raytheon.viz.ui.cmenu.AbstractRightClickAction; import com.raytheon.viz.ui.cmenu.AbstractRightClickAction;
import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.Polygon;
@ -41,6 +42,8 @@ import com.vividsolutions.jts.geom.Polygon;
* Mar 23, 2015 3977 nabowle Initial creation * Mar 23, 2015 3977 nabowle Initial creation
* Jun 01, 2015 3975 dgilling Update for DamageLayer changes for * Jun 01, 2015 3975 dgilling Update for DamageLayer changes for
* multiple polygon support. * multiple polygon support.
* Jun 18, 2015 4354 dgilling Update isEnabled to consider editable
* capability.
* *
* </pre> * </pre>
* *
@ -85,9 +88,10 @@ public class ImportFromDistanceSpeedAction extends AbstractRightClickAction {
*/ */
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
boolean enabled = super.isEnabled(); AbstractVizResource<?, ?> rsc = getSelectedRsc();
boolean enabled = rsc.getCapability(EditableCapability.class)
.isEditable();
if (enabled) { if (enabled) {
AbstractVizResource<?, ?> rsc = getSelectedRsc();
if (rsc != null) { if (rsc != null) {
enabled = findImportLayer(rsc) != null; enabled = findImportLayer(rsc) != null;
} }
@ -111,5 +115,4 @@ public class ImportFromDistanceSpeedAction extends AbstractRightClickAction {
} }
return null; return null;
} }
} }

View file

@ -39,6 +39,8 @@ import com.raytheon.viz.ui.dialogs.ICloseCallback;
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Apr 23, 2015 #4354 dgilling Initial creation * Apr 23, 2015 #4354 dgilling Initial creation
* Jun 09, 2015 #4355 dgilling Rename action for UI. * Jun 09, 2015 #4355 dgilling Rename action for UI.
* Jun 18, 2015 #4354 dgilling Allow individual properties object for
* each polygon.
* *
* </pre> * </pre>
* *
@ -48,8 +50,11 @@ import com.raytheon.viz.ui.dialogs.ICloseCallback;
public class OpenGeoJsonPropertiesDlgAction extends AbstractRightClickAction { public class OpenGeoJsonPropertiesDlgAction extends AbstractRightClickAction {
public OpenGeoJsonPropertiesDlgAction() { private final DamagePathPolygon damagePath;
public OpenGeoJsonPropertiesDlgAction(final DamagePathPolygon damagePath) {
super("Set Properties..."); super("Set Properties...");
this.damagePath = damagePath;
} }
@Override @Override
@ -59,10 +64,10 @@ public class OpenGeoJsonPropertiesDlgAction extends AbstractRightClickAction {
public void run() { public void run() {
Shell shell = VizWorkbenchManager.getInstance() Shell shell = VizWorkbenchManager.getInstance()
.getCurrentWindow().getShell(); .getCurrentWindow().getShell();
final DamagePathLayer<?> layer = (DamagePathLayer<?>) getSelectedRsc();
final Map<String, String> geoJsonProps = layer
.getFeatureProperties();
final DamagePathLayer<?> layer = (DamagePathLayer<?>) getSelectedRsc();
final Map<String, String> geoJsonProps = damagePath
.getProperties();
EditGeoJsonPropertiesDlg dlg = new EditGeoJsonPropertiesDlg( EditGeoJsonPropertiesDlg dlg = new EditGeoJsonPropertiesDlg(
shell, geoJsonProps); shell, geoJsonProps);
dlg.setCloseCallback(new ICloseCallback() { dlg.setCloseCallback(new ICloseCallback() {
@ -72,7 +77,8 @@ public class OpenGeoJsonPropertiesDlgAction extends AbstractRightClickAction {
if ((returnValue != null) if ((returnValue != null)
&& (!geoJsonProps.equals(returnValue))) { && (!geoJsonProps.equals(returnValue))) {
Map<String, String> updatedProperties = (Map<String, String>) returnValue; Map<String, String> updatedProperties = (Map<String, String>) returnValue;
layer.setFeatureProperties(updatedProperties); damagePath.setProperties(updatedProperties);
layer.scheduleSaveJob();
} }
} }
}); });

View file

@ -50,6 +50,7 @@ import com.vividsolutions.jts.geom.Polygon;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* May 27, 2015 #4375 dgilling Initial creation * May 27, 2015 #4375 dgilling Initial creation
* Jun 18, 2015 #4354 dgilling Correct behavior of project.
* *
* </pre> * </pre>
* *
@ -168,6 +169,23 @@ public class DrawablePolygon implements IRenderable2 {
if (coords != null && coords.length > 0) { if (coords != null && coords.length > 0) {
polygon = PolygonUtil.FACTORY.createPolygon(coords); polygon = PolygonUtil.FACTORY.createPolygon(coords);
} }
resetPolygon();
}
}
public void resetPolygon(DrawablePolygon newPolygon) {
synchronized (lock) {
if ((newPolygon != null) && (newPolygon.getPolygon() != null)) {
polygon = newPolygon.getPolygon();
}
resetPolygon();
}
}
public void resetPolygon() {
synchronized (lock) {
if (wireframeShape != null) { if (wireframeShape != null) {
wireframeShape.reset(); wireframeShape.reset();
} }
@ -193,7 +211,7 @@ public class DrawablePolygon implements IRenderable2 {
public void project(CoordinateReferenceSystem crs) { public void project(CoordinateReferenceSystem crs) {
synchronized (lock) { synchronized (lock) {
dispose(); dispose();
polygon = null; resetPolygon();
} }
} }
} }

View file

@ -44,6 +44,7 @@ import com.vividsolutions.jts.geom.util.AffineTransformation;
* Mar 04, 2015 4194 njensen Block other input on middle click drag on edges * Mar 04, 2015 4194 njensen Block other input on middle click drag on edges
* Jun 03, 2015 4375 dgilling Add methods for adding/deleting polygons, * Jun 03, 2015 4375 dgilling Add methods for adding/deleting polygons,
* support multiple polygons in PolygonLayer. * support multiple polygons in PolygonLayer.
* Jun 18, 2015 4354 dgilling Changed visibility of pointOnPolygon.
* *
* </pre> * </pre>
* *
@ -306,7 +307,7 @@ public class PolygonInputAdapter extends RscInputAdapter<PolygonLayer<?>> {
* @return Index of the polygon this screen point is on or -1 if it is not * @return Index of the polygon this screen point is on or -1 if it is not
* on any polygons. * on any polygons.
*/ */
protected int pointOnPolygon(int screenX, int screenY) { public int pointOnPolygon(int screenX, int screenY) {
int polygonIdx = -1; int polygonIdx = -1;
Coordinate worldCoord = screenToLatLon(screenX, screenY); Coordinate worldCoord = screenToLatLon(screenX, screenY);
if (worldCoord != null) { if (worldCoord != null) {

View file

@ -22,7 +22,6 @@ package com.raytheon.uf.viz.drawing.polygon;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IMenuManager;
import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.CoordinateReferenceSystem;
@ -59,6 +58,9 @@ import com.vividsolutions.jts.geom.Polygon;
* May 15, 2015 4375 dgilling Support multiple polygons. * May 15, 2015 4375 dgilling Support multiple polygons.
* Jun 12, 2015 4375 dgilling Only show AddVertexAction when on polygon's * Jun 12, 2015 4375 dgilling Only show AddVertexAction when on polygon's
* edge and not near a current vertex. * edge and not near a current vertex.
* Jun 17, 2015 4354 dgilling Fix bugs that would clear polygons on
* capability change or reproject.
*
* *
* </pre> * </pre>
* *
@ -75,7 +77,7 @@ public class PolygonLayer<T extends AbstractResourceData> extends
protected PolygonInputAdapter uiInput = new PolygonInputAdapter(this); protected PolygonInputAdapter uiInput = new PolygonInputAdapter(this);
protected final List<DrawablePolygon> polygons; protected final LinkedList<DrawablePolygon> polygons;
public PolygonLayer(T resourceData, LoadProperties loadProperties) { public PolygonLayer(T resourceData, LoadProperties loadProperties) {
super(resourceData, loadProperties); super(resourceData, loadProperties);
@ -128,7 +130,7 @@ public class PolygonLayer<T extends AbstractResourceData> extends
if ((index < polygons.size()) && (coords != null && coords.length > 0)) { if ((index < polygons.size()) && (coords != null && coords.length > 0)) {
DrawablePolygon polygon = polygons.remove(index); DrawablePolygon polygon = polygons.remove(index);
polygon.resetPolygon(coords); polygon.resetPolygon(coords);
polygons.add(0, polygon); polygons.push(polygon);
issueRefresh(); issueRefresh();
} }
} }
@ -136,7 +138,12 @@ public class PolygonLayer<T extends AbstractResourceData> extends
@Override @Override
protected void resourceDataChanged(ChangeType type, Object updateObject) { protected void resourceDataChanged(ChangeType type, Object updateObject) {
if (type.equals(ChangeType.CAPABILITY)) { if (type.equals(ChangeType.CAPABILITY)) {
clearPolygons(); if (polygons != null) {
for (DrawablePolygon polygon : polygons) {
polygon.resetPolygon();
}
}
issueRefresh();
} }
} }
@ -145,6 +152,7 @@ public class PolygonLayer<T extends AbstractResourceData> extends
for (DrawablePolygon drawPolygon : polygons) { for (DrawablePolygon drawPolygon : polygons) {
drawPolygon.project(crs); drawPolygon.project(crs);
} }
issueRefresh();
} }
public Polygon getPolygon(int index) { public Polygon getPolygon(int index) {
@ -155,24 +163,13 @@ public class PolygonLayer<T extends AbstractResourceData> extends
resetPolygon(index, polygon.getExteriorRing().getCoordinates()); resetPolygon(index, polygon.getExteriorRing().getCoordinates());
} }
protected void clearPolygons() { public void resetPolygons(Collection<DrawablePolygon> newPolygons) {
if (polygons != null) {
for (DrawablePolygon polygon : polygons) {
polygon.dispose();
}
polygons.clear();
issueRefresh();
}
}
public void resetPolygons(Collection<Polygon> newPolygons) {
if ((polygons != null) && (!newPolygons.isEmpty())) { if ((polygons != null) && (!newPolygons.isEmpty())) {
resizePolygonStack(newPolygons.size()); resizePolygonStack(newPolygons.size());
int i = 0; int i = 0;
for (Polygon newPolygon : newPolygons) { for (DrawablePolygon newPolygon : newPolygons) {
polygons.get(i).resetPolygon( polygons.get(i).resetPolygon(newPolygon);
newPolygon.getExteriorRing().getCoordinates());
i++; i++;
} }
@ -185,21 +182,27 @@ public class PolygonLayer<T extends AbstractResourceData> extends
if (newSize > currentSize) { if (newSize > currentSize) {
int toAdd = newSize - currentSize; int toAdd = newSize - currentSize;
for (int i = 0; i < toAdd; i++) { for (int i = 0; i < toAdd; i++) {
polygons.add(new DrawablePolygon(this)); polygons.add(getNewDrawable());
} }
} else if (currentSize > newSize) { } else if (currentSize > newSize) {
int toDelete = currentSize - newSize; int toDelete = currentSize - newSize;
for (int i = 0; i < toDelete; i++) { for (int i = 0; i < toDelete; i++) {
DrawablePolygon polygonToDelete = polygons.remove(polygons DrawablePolygon polygonToDelete = polygons.removeLast();
.size() - 1);
polygonToDelete.dispose(); polygonToDelete.dispose();
} }
} }
} }
protected DrawablePolygon getNewDrawable() {
return new DrawablePolygon(this);
}
public void addPolygon(Coordinate[] coords) { public void addPolygon(Coordinate[] coords) {
DrawablePolygon polygon = new DrawablePolygon(coords, this); addPolygon(new DrawablePolygon(coords, this));
polygons.add(0, polygon); }
protected void addPolygon(DrawablePolygon newPolygon) {
polygons.push(newPolygon);
} }
public void deletePolygon(int index) { public void deletePolygon(int index) {
@ -224,6 +227,10 @@ public class PolygonLayer<T extends AbstractResourceData> extends
*/ */
@Override @Override
public void addContextMenuItems(IMenuManager menuManager, int x, int y) { public void addContextMenuItems(IMenuManager menuManager, int x, int y) {
if (!getCapability(EditableCapability.class).isEditable()) {
return;
}
int edgePolygonIdx = uiInput.pointOnEdge(x, y); int edgePolygonIdx = uiInput.pointOnEdge(x, y);
boolean onEdge = (edgePolygonIdx >= 0); boolean onEdge = (edgePolygonIdx >= 0);