/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* JUMP is Copyright (C) 2003 Vivid Solutions
*
* This program implements extensions to JUMP and is
* Copyright (C) 2004 Integrated Systems Analysts, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Integrated Systems Analysts, Inc.
* 630C Anchors St., Suite 101
* Fort Walton Beach, Florida
* USA
*
* (850)862-7321
*/
package org.openjump.core.ui.plugin.tools;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.JComponent;
import org.openjump.core.geomutils.GeoUtils;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.feature.Feature;
import com.vividsolutions.jump.feature.FeatureCollection;
import com.vividsolutions.jump.feature.FeatureDataset;
import com.vividsolutions.jump.task.TaskMonitor;
import com.vividsolutions.jump.workbench.WorkbenchContext;
import com.vividsolutions.jump.workbench.model.Layer;
import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
import com.vividsolutions.jump.workbench.model.UndoableCommand;
import com.vividsolutions.jump.workbench.plugin.*;
import com.vividsolutions.jump.workbench.plugin.util.*;
import com.vividsolutions.jump.workbench.ui.GUIUtil;
import com.vividsolutions.jump.workbench.ui.MenuNames;
import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
import com.vividsolutions.jump.workbench.ui.SelectionManagerProxy;
import com.vividsolutions.jump.workbench.ui.plugin.analysis.GeometryFunction;
public class SplitPolygonPlugIn extends AbstractPlugIn implements ThreadedPlugIn
{
private static String UPDATE_SRC = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Update-the-polygon-with-result");
private static String ADD_TO_SRC = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Add-result-to-the-polygon-layer");
private static String CREATE_LYR = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Create-new-layer-for-result");
private static String sCutPolygon = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Cut-Polygon");
private static String sError = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Errors-found-while-executing-Cut-Polygon");
private static String sExecuting = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Executing-Difference-function");
private static String sMustSelect = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Must-select-one-polygon-and-one-linestring");
private static String sDescription = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Uses-the-selected-linestring-to-cut-the-selected-polygon-into-separate-sections");
private MultiInputDialog dialog;
private Layer srcLayer;
private GeometryFunction differenceFunction = GeometryFunction.getFunction("Difference (Source-Mask)");
private GeometryFunction intersectionFunction = GeometryFunction.getFunction("Intersection");
private boolean createLayer = true;
private boolean updateSource = false;
private boolean addToSource = false;
public SplitPolygonPlugIn(){
}
public void initialize(PlugInContext context) throws Exception
{
//-- load again in the correct language
UPDATE_SRC = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Update-the-polygon-with-result");
ADD_TO_SRC = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Add-result-to-the-polygon-layer");
CREATE_LYR = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Create-new-layer-for-result");
sCutPolygon = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Cut-Polygon");
sError = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Errors-found-while-executing-Cut-Polygon");
sExecuting = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Executing-Difference-function");
sMustSelect = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Must-select-one-polygon-and-one-linestring");
sDescription = I18N.get("org.openjump.core.ui.plugin.tools.SplitPolygonPlugIn.Uses-the-selected-linestring-to-cut-the-selected-polygon-into-separate-sections");
//-- [sstein 11 March 2007] it is a bit circumstantially to access a geometry function
// using i18n strings - we should introduce an unique ID
differenceFunction = GeometryFunction.getFunction(I18N.get("ui.plugin.analysis.GeometryFunction.difference-a-b"));
intersectionFunction = GeometryFunction.getFunction(I18N.get("ui.plugin.analysis.GeometryFunction.intersection"));
WorkbenchContext workbenchContext = context.getWorkbenchContext();
context.getFeatureInstaller().addMainMenuItem(this,
new String[] { MenuNames.TOOLS, MenuNames.TOOLS_EDIT_GEOMETRY },
getName() + "...",
false,
null,
this.createEnableCheck(workbenchContext));
}
public String getName(){
return this.sCutPolygon;
}
public boolean execute(PlugInContext context) throws Exception
{
dialog = new MultiInputDialog(context.getWorkbenchFrame(), getName(), true);
setDialogValues(dialog, context);
GUIUtil.centreOnWindow(dialog);
dialog.setVisible(true);
if (! dialog.wasOKPressed()) { return false; }
getDialogValues(dialog);
return true;
}
public void run(TaskMonitor monitor, PlugInContext context)throws Exception
{
monitor.allowCancellationRequests();
Collection selectedFeatures = context.getLayerViewPanel().getSelectionManager().getFeaturesWithSelectedItems();
Iterator i = selectedFeatures.iterator();
Feature featureOne = (Feature)i.next();
Feature featureTwo = (Feature)i.next();
//this works because enable check ensures that one is polygon and the other is linestring
Feature polyFeature = featureOne;
Geometry linestring = featureTwo.getGeometry();
if (linestring instanceof Polygon)
{
polyFeature = featureTwo;
linestring = featureOne.getGeometry();
}
final double bufferWidth = 0.01;
Geometry buffer = linestring.buffer(bufferWidth);
//find the poly layer since linestring can be on different layer
for (Iterator lyr = context.getWorkbenchContext().getLayerViewPanel().getSelectionManager().getLayersWithSelectedItems().iterator();lyr.hasNext();)
{
Layer layer = (Layer) lyr.next();
for (Iterator ftr = layer.getFeatureCollectionWrapper().getFeatures().iterator(); ftr.hasNext();)
{
if (polyFeature == ftr.next())
{
srcLayer = layer;
break;
}
}
if (srcLayer == layer) break;
}
monitor.report(sExecuting + "...");
Collection resultFeatures = new ArrayList();
Geometry result = null;
Geometry intersection = null;
try
{
Geometry geoms[] = new Geometry[2];
geoms[0] = polyFeature.getGeometry();
geoms[1] = buffer;
result = differenceFunction.execute(geoms, new double[2]);
geoms[1] = linestring;
intersection = intersectionFunction.execute(geoms, new double[2]);
}
catch (RuntimeException ex)
{
context.getWorkbenchFrame().warnUser(sError);
}
final Coordinate[] intersectionPts = intersection.getCoordinates();
if (result == null || result.isEmpty()) return;
final int lsNumPts = linestring.getNumPoints();
final Coordinate[] lsCoords = linestring.getCoordinates();
for (int j = 0; j < result.getNumGeometries(); j++)
{
Feature fNew = polyFeature.clone(true);
//snap the resulting geos to the cut line
Geometry geo = (Geometry)result.getGeometryN(j).clone();
geo.apply(new CoordinateFilter() {
public void filter(Coordinate coordinate) {
for (int n = 0; n < lsNumPts - 1; n++)
{
Coordinate p0 = lsCoords[n];
Coordinate p1 = lsCoords[n + 1];
double distToLine = GeoUtils.getDistance(coordinate, p0, p1);
if (Math.abs((distToLine) - (bufferWidth)) < 0.001)
{
//is close to buffer boundary
Coordinate snapPt = getNearestSnapPoint(coordinate, intersectionPts);
coordinate.x = snapPt.x;
coordinate.y = snapPt.y;
}
}
}
});
fNew.setGeometry(geo);
resultFeatures.add(fNew);
}
if (createLayer)
{
String outputLayerName = LayerNameGenerator.generateOperationOnLayerName(
sCutPolygon, srcLayer.getName());
FeatureCollection resultFC = new FeatureDataset(srcLayer.getFeatureCollectionWrapper().getFeatureSchema());
resultFC.addAll(resultFeatures);
String categoryName = StandardCategoryNames.RESULT;
context.getLayerManager().addCategory(categoryName);
Layer newLayer = context.addLayer(categoryName, outputLayerName, resultFC);
newLayer.setFeatureCollectionModified(true);
}
else if (updateSource)
{
final Collection undoableNewFeatures = resultFeatures;
final Feature undoablePolyFeatures = polyFeature;
UndoableCommand cmd = new UndoableCommand( getName() ) {
public void execute() {
srcLayer.getFeatureCollectionWrapper().remove(undoablePolyFeatures);
srcLayer.getFeatureCollectionWrapper().addAll(undoableNewFeatures);
}
public void unexecute() {
srcLayer.getFeatureCollectionWrapper().removeAll(undoableNewFeatures);
srcLayer.getFeatureCollectionWrapper().add(undoablePolyFeatures);
}
};
execute( cmd, context );
}
else if (addToSource)
{
final Collection undoableFeatures = resultFeatures;
UndoableCommand cmd = new UndoableCommand( getName() ) {
public void execute() {
srcLayer.getFeatureCollectionWrapper().addAll( undoableFeatures );
}
public void unexecute() {
srcLayer.getFeatureCollectionWrapper().removeAll( undoableFeatures );
}
};
execute( cmd, context );
}
}
private Coordinate getNearestSnapPoint(Coordinate coord, Coordinate[] intersectionPts)
{
Coordinate closestPt = (Coordinate)((Coordinate) intersectionPts[0]).clone();
double shortestDist = coord.distance(intersectionPts[0]);
for (int i = 1; i < intersectionPts.length; i++)
{
if (coord.distance(intersectionPts[i]) < shortestDist)
{
closestPt = (Coordinate)((Coordinate) intersectionPts[i]).clone();
shortestDist = coord.distance(intersectionPts[i]);
}
}
return closestPt;
}
private void setDialogValues(MultiInputDialog dialog, PlugInContext context)
{
dialog.setSideBarDescription(sDescription);
final String OUTPUT_GROUP = "Match Type";
dialog.addRadioButton(CREATE_LYR, OUTPUT_GROUP, createLayer,CREATE_LYR);
dialog.addRadioButton(UPDATE_SRC, OUTPUT_GROUP, updateSource,UPDATE_SRC);
dialog.addRadioButton(ADD_TO_SRC, OUTPUT_GROUP, addToSource,ADD_TO_SRC);
}
private void getDialogValues(MultiInputDialog dialog)
{
createLayer = dialog.getBoolean(CREATE_LYR);
updateSource = dialog.getBoolean(UPDATE_SRC);
addToSource = dialog.getBoolean(ADD_TO_SRC);
}
public EnableCheck onlyPolyAndLinestringMayBeSelected(final WorkbenchContext workbenchContext) {
return new EnableCheck() {
public String check(JComponent component) {
Collection selectedItems = ((SelectionManagerProxy) workbenchContext
.getWorkbench()
.getFrame()
.getActiveInternalFrame())
.getSelectionManager()
.getSelectedItems();
int polyCount = 0;
int lsCount = 0;
for (Iterator i = selectedItems.iterator(); i.hasNext();)
{
Geometry geo = (Geometry) i.next();
if (geo instanceof Polygon) polyCount++;
if (geo instanceof LineString) lsCount++;
}
if (polyCount == 1 && lsCount == 1) return null;
return sMustSelect;
}
};
}
public MultiEnableCheck createEnableCheck(final WorkbenchContext workbenchContext) {
EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext);
return new MultiEnableCheck()
.add(checkFactory.createWindowWithLayerViewPanelMustBeActiveCheck())
.add(checkFactory.createExactlyNFeaturesMustBeSelectedCheck(2))
.add(onlyPolyAndLinestringMayBeSelected(workbenchContext));
}
}