/**
*
* Copyright 2011 Edgar Soldin
*
* 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
*/
package de.soldin.jumpcore.geomconv;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import com.vividsolutions.jts.algorithm.CGAlgorithms;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryComponentFilter;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.util.GeometryEditor;
import com.vividsolutions.jts.geom.util.LinearComponentExtracter;
import com.vividsolutions.jump.feature.Feature;
import com.vividsolutions.jump.workbench.WorkbenchContext;
import com.vividsolutions.jump.workbench.model.Layer;
import com.vividsolutions.jump.workbench.plugin.EnableCheck;
import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory;
import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck;
import com.vividsolutions.jump.workbench.plugin.PlugInContext;
import com.vividsolutions.jump.workbench.ui.FeatureSelection;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.MenuNames;
import com.vividsolutions.jump.workbench.ui.OKCancelDialog;
import com.vividsolutions.jump.workbench.ui.OKCancelDialog.Validator;
import com.vividsolutions.jump.workbench.ui.SelectionManagerProxy;
import de.soldin.jumpcore.ExtCorePlugIn;
import de.soldin.jumpcore.UndoableSetGeometry;
/**
* A jump plugin for the conversion of geometries into one another. Currently
* supported are geometries of the type
* <ul>
* <li>{@link com.vividsolutions.jts.geom.LineString}</li>
* <li>{@link com.vividsolutions.jts.geom.LinearRing}</li>
* <li>{@link com.vividsolutions.jts.geom.MultiPoint}</li>
* <li>{@link com.vividsolutions.jts.geom.Point}</li>
* <li>{@link com.vividsolutions.jts.geom.Polygon}</li>
* </ul>
*
*
* @see com.vividsolutions.jump.workbench.plugin.PlugIn
*/
public class GCPlugin extends ExtCorePlugIn {
private GeometryFactory factory = new GeometryFactory();
private List items;
private WorkbenchContext wbc;
private String target;
private boolean youvebeenwarned = false;
public GCPlugin() {
this( null, null);
}
public GCPlugin(WorkbenchContext wbc, String target) {
super();
this.wbc = wbc;
this.target = target;
this.i18nPath = "language/geomconv/gc";
// fetch possible geometry keys
items = new ArrayList( this.getCreatableGeoms().keySet( ));
Collections.sort(items);
// add tools
items.add("separator");
items.add("close-lines");
items.add("remove-closing-segment");
}
public void initialize(PlugInContext context) throws Exception {
this.wbc = context.getWorkbenchContext();
// create menu items
String[] menuchain = new String[] { MenuNames.TOOLS,
MenuNames.TOOLS_EDIT_GEOMETRY, getName() };
JPopupMenu popupMenu = LayerViewPanel.popupMenu();
String[] popupchain = new String[] { getName() };
addToMainMenu(wbc, menuchain);
try{
//context.getFeatureInstaller().addPopupMenuSeparator(popupMenu, new String[]{});
//context.getFeatureInstaller().addPopupMenuSeparator(popupMenu, new String[]{ "bla" });
//context.getFeatureInstaller().addPopupMenuSeparator(popupMenu, new String[]{ "foo", "bar"});
addToPopupMenu(wbc, popupMenu, popupchain);
}catch (NoSuchMethodError e) {
System.out.println("update to oj 1.4.1 for popupmenu entries");
}
// old installation routine, keep for reference
/*
// one checker to rule them all
EnableCheck checker = createEnableCheck();
for (Iterator iter = items.iterator(); iter.hasNext();) {
String menuentry = (String) iter.next();
// add one plugin per menuitem
GCPlugin plugin = new GCPlugin(wbc, menuentry);
// add separator
if (menuentry.equals("separator")) {
context.getFeatureInstaller().addMenuSeparator(menuchain);
getSubMenu(popupMenu, getName()).addSeparator();
} else {
context.getFeatureInstaller().addMainMenuItem(
// one plugin per menuentry
plugin, menuchain, _(menuentry.toLowerCase()), false, null, checker);
// the layer popup menu
context.getFeatureInstaller().addPopupMenuItem(popupMenu,
plugin, popupchain, _(menuentry.toLowerCase()), false, null, checker);
}
}
*/
}
// this can be used to attach it to another popup menu as well
public void addToPopupMenu( WorkbenchContext wbc, JPopupMenu popupMenu, String[] popupchain){
PlugInContext context = wbc.createPlugInContext();
// one checker to rule them all
EnableCheck checker = createEnableCheck();
for (Iterator iter = items.iterator(); iter.hasNext();) {
String menuentry = (String) iter.next();
// add one plugin per menuitem
GCPlugin plugin = new GCPlugin(wbc, menuentry);
// add separator
if (menuentry.equals("separator")) {
getSubMenu(popupMenu, popupchain).addSeparator();
} else {
// the layer popup menu
context.getFeatureInstaller().addPopupMenuItem(popupMenu,
plugin, popupchain, _(menuentry.toLowerCase()), false, null, checker);
}
}
}
public void addToMainMenu( WorkbenchContext wbc, String[] menuchain){
PlugInContext context = wbc.createPlugInContext();
// one checker to rule them all
EnableCheck checker = createEnableCheck();
for (Iterator iter = items.iterator(); iter.hasNext();) {
String menuentry = (String) iter.next();
// add one plugin per menuitem
GCPlugin plugin = new GCPlugin(wbc, menuentry);
// add separator
if (menuentry.equals("separator")) {
context.getFeatureInstaller().addMenuSeparator(menuchain);
} else {
// one plugin per menuentry
context.getFeatureInstaller().addMainMenuItem(
plugin, menuchain, _(menuentry.toLowerCase()), false, null, checker);
}
}
}
public boolean execute(PlugInContext context) throws Exception {
if ( layerMode() ){
Collection layers = getLayers();
StringBuffer buf = new StringBuffer();
for (Iterator i = layers.iterator(); i.hasNext();) {
Layer layer = (Layer) i.next();
String name = layer.getName();
buf.append( buf.length()==0?name:", "+name);
}
String message = ( target.equalsIgnoreCase("close-lines") || target.equalsIgnoreCase("remove-closing-segment") ) ?
"treat-all-with-tools" :
"convert-all-to";
if ( !okCancel( _("are-you-sure"), _(message, buf, _(target.toLowerCase()) ) ) )
return false;
}
context.getLayerManager().getUndoableEditReceiver()
.reportNothingToUndoYet();
//Method method = (Method) getCreatableGeoms().get(target);
//Class[] cparams = ( method instanceof Method ) ? method.getParameterTypes() : null;
Collection layers = getLayers();
// undo for all layers
UndoableSetGeometry action = new UndoableSetGeometry(getName());
for (Iterator li = layers.iterator(); li.hasNext();) {
Layer layer = (Layer) li.next();
Collection feats = getFeatures(layer);
// create undo for layer
UndoableSetGeometry layeraction = new UndoableSetGeometry(layer,
getName());
//System.out.println("exec " + target + " on " + layer.getName()
// + " feats#" + feats.size());
for (Iterator i = feats.iterator(); i.hasNext();) {
try {
Feature feat = (Feature) i.next();
// keep for comparision with geom_new
Geometry geom_orig = layeraction.getGeom(feat);
// might be split in lines later
Geometry geom_src = geom_orig;
Geometry geom_new = null;
// reset warning monitor
youvebeenwarned = false;
// TOOLS
if ( target.equalsIgnoreCase("close-lines") || target.equalsIgnoreCase("remove-closing-segment") ) {
// these tools make only sense with line segmented geometries
// that not insist on being closed rings
if (geom_src instanceof LinearRing
|| geom_src instanceof Polygon
|| geom_src instanceof MultiPolygon)
geom_src = factory.createMultiLineString( getLines( geom_src ) );
int count = geom_src.getNumGeometries();
boolean changed = false;
Geometry[] geoms_new = new Geometry[count];
GeometryEditor editor = new GeometryEditor();
for (int j = 0; j < count; j++) {
Geometry geom = geom_src.getGeometryN(j);
// weirdly closed linestrings always end up to be linearrings
if ( geom instanceof LinearRing )
geom = factory.createLineString(geom.getCoordinates());
// get coordinates for check
//Coordinate[] points = geom.getCoordinates();
if (target.equalsIgnoreCase("close-lines") ) {
geoms_new[j] = editor.edit( geom, new CloseRing());
} else if (target.equalsIgnoreCase("remove-closing-segment") ) {
geoms_new[j] = editor.edit( geom, new RemoveClosing());
}
//System.out.println("geom: "+geom);
//System.out.println("new : "+geoms_new[j]);
// did we receive a changed geometry?
if ( !geoms_new[j].equalsExact(geom) ) changed = true;
}
// only create a new geometry if the old was changed
if ( changed && count > 1 ) {
geom_new = factory.createGeometryCollection(geoms_new);
// restore multigeometrytype of geom_src collection
geom_new = convert( geom_new, geom_src.getGeometryType() );
}
else if ( changed && count == 1 )
geom_new = geoms_new[0];
else
warnUser(_("nothing-to-do", feat.getID()));
}
// CONVERSIONS (moved to external method)
else {
geom_new = convert( geom_src, target);
}
//System.out.println(geom_new);
//System.out.println(geom_orig);
if (geom_new != null && !geom_new.equalsExact(geom_orig)) {
layeraction.setGeom(feat, geom_new);
}
}
catch (IllegalArgumentException e) {
warnUser(_e(e.getMessage()));
}
catch (InvocationTargetException ie) {
if (ie.getCause() != null)
warnUser(_e(ie.getCause().getLocalizedMessage()));
}
}
if (!layeraction.isEmpty()) action.add(layeraction);
}
// don't execute & register empty actions
if (!action.isEmpty()) {
action.execute();
wbc.getLayerManager().getUndoableEditReceiver().receive(action);
return true;
}
// don't mask other warnings here
else if (!youvebeenwarned ){
warnUser(_("nothing-changed"));
}
// operation failed
return false;
}
public String getName() {
return _("convert-selected-to");
}
/*private JMenu getSubMenu(JMenu menu, String key) {
for (int i = 0; i < menu.getItemCount(); i++) {
if (menu.getItem(i) == null)
continue;
if (menu.getItem(i).getText().equals(key)) {
return (JMenu) menu.getItem(i);
}
}
return null;
}*/
private JMenu getSubMenu(MenuElement menu, String[] keys) {
MenuElement[] ms = menu.getSubElements();
String key = keys[0];
for (int i = 0; i < ms.length; i++) {
MenuElement m = ms[i];
if (m == null)
continue;
if (m instanceof JMenu && ((JMenu) m).getText().equals(key)) {
// snip first
if (keys.length > 1) {
String[] subkeys = new String[keys.length - 1];
for (int j = 1; j < keys.length; j++) {
subkeys[j] = keys[j];
}
m = getSubMenu(menu, subkeys);
} else {
return (JMenu) m;
}
}
}
return null;
}
public EnableCheck createEnableCheck() {
EnableCheckFactory checkFactory = new EnableCheckFactory(this.wbc);
MultiEnableCheck checker = new MultiEnableCheck();
// taskframe must be active
checker.add(checkFactory
.createWindowWithLayerViewPanelMustBeActiveCheck());
// are there selected layers OR layers with selected features
// are these layers editable?
checker.add(new EnableCheck() {
public String check(JComponent component) {
Collection layers = getLayers();
if (layers == null || layers.isEmpty())
return _("select-geometries-or-layers");
for (Iterator iterator = layers.iterator(); iterator.hasNext();) {
Layer layer = (Layer) iterator.next();
// System.out.println(layer.getName()
// +"->"+(layer.isEditable()?"ja":"nein"));
if (!layer.isEditable()) {
return _("layer-not-editable", layer.getName());
}
}
// reached here? all is well
return null;
}
});
return checker;
}
private boolean layerMode(){
FeatureSelection sel = ((SelectionManagerProxy) wbc.getWorkbench()
.getFrame().getActiveInternalFrame()).getSelectionManager()
.getFeatureSelection();
// user hand picked geometries (features)
if (!sel.getFeaturesWithSelectedItems().isEmpty()) {
return false;
}
// user selected layers
else {
return true;
}
}
private Collection getFeatures(Layer layer) {
FeatureSelection sel = ((SelectionManagerProxy) wbc.getWorkbench()
.getFrame().getActiveInternalFrame()).getSelectionManager()
.getFeatureSelection();
Collection feats;
// user hand picked geometries (features)
if (!sel.getFeaturesWithSelectedItems().isEmpty()) {
feats = sel.getFeaturesWithSelectedItems(layer);
}
// user only selected layers
else {
feats = layer.getFeatureCollectionWrapper().getFeatures();
}
return feats;
}
private Collection getLayers() {
// all layers with selected items (parts of geometries)
Collection layers = ((SelectionManagerProxy) wbc.getWorkbench()
.getFrame().getActiveInternalFrame()).getSelectionManager()
.getFeatureSelection().getLayersWithSelectedItems();
return layers.isEmpty() ? Arrays.asList(wbc.getLayerNamePanel()
.getSelectedLayers()) : layers;
}
private Collection<String> getTypes( GeometryCollection geom ){
Collection types = new Vector();
for (int i = 0; i < geom.getNumGeometries(); i++) {
String type = geom.getGeometryN(i).getGeometryType();
if (!types.contains( type ))
types.add( type );
}
return types;
}
private Map getCreatableGeoms() {
Map geoms = new Hashtable();
Class cfactory = factory.getClass();
Method[] methods = cfactory.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = (Method) methods[i];
if (!method.getName().startsWith("create")) {
continue;
}
// check if parameters match
Class[] cparams = methods[i].getParameterTypes();
boolean wrong = false;
for (int j = 0; j < cparams.length; j++) {
Class cparam = cparams[j];
if (!validType(cparam)) {
wrong = true;
break;
}
}
if (wrong)
continue;
String id = method.getName().replaceFirst("create", "");
// blacklist some
if (id.equals("PointFromInternalCoord") || id.equals("Geometry"))
continue;
geoms.put(id, method);
}
return geoms;
}
private boolean validType(Class clazz) {
String name;
if (clazz.isArray())
name = clazz.getComponentType().getName();
else
name = clazz.getName();
return name.equals("com.vividsolutions.jts.geom.Coordinate")
|| name.equals("com.vividsolutions.jts.geom.LinearRing")
|| name.equals("com.vividsolutions.jts.geom.LineString")
|| name.equals("com.vividsolutions.jts.geom.Polygon")
|| name.equals("com.vividsolutions.jts.geom.Geometry");
}
private void warnUser(final String message) {
this.youvebeenwarned = true;
this.wbc.getLayerViewPanel().getContext().warnUser(message);
}
private Geometry convert( Geometry geom_src, String type ) throws Exception{
Geometry geom_new = null;
Method method = (Method) getCreatableGeoms().get(type);
Class[] cparams = ( method instanceof Method ) ? method.getParameterTypes() : null;
// do we have to && can we convert?
if ( !(method instanceof Method) ) {
// ups we've got ourself no conversion
warnUser(_("no-conversion-method", _(type.toLowerCase()), type));
return null;
}
/*
// Multi*s and polygon always get line segments separated
// Points contain _zero_ line segments
LineString[] lines = null;
if ( (type.toLowerCase().startsWith("multi") ||
type.equalsIgnoreCase("polygon")) ){
lines = getLines( geom_src, false );
if (lines.length > 0)
geom_src = factory.createMultiLineString( lines );
}
*/
boolean isArray = cparams[0].isArray();
String name = isArray ? cparams[0].getComponentType()
.getName() : cparams[0].getName();
// made from one coord, probably point ;)
if (!isArray
&& name.equals("com.vividsolutions.jts.geom.Coordinate")) {
// System.out.println("eine koordinate");
if (geom_src.getCoordinates().length == 1) {
geom_new = (Geometry) method.invoke(factory,
new Object[] { geom_src
.getCoordinates()[0] });
} else {
warnUser(_("only-one-coordinate",type));
}
}
// simple geometries made from coord[]
else if (isArray
&& name.equals("com.vividsolutions.jts.geom.Coordinate")) {
// System.out.println("mehrere koordinaten");
geom_new = (Geometry) method.invoke(factory,
new Object[] { geom_src.getCoordinates() });
}
// multilinestring
else if (isArray
&& name.equals("com.vividsolutions.jts.geom.LineString")) {
Coordinate[] coords = geom_src.getCoordinates();
geom_new = (Geometry) method.invoke(factory,
new Object[] { getLines( geom_src ) });
}
// polygon
else if (cparams.length == 2
&& name.equals("com.vividsolutions.jts.geom.LinearRing")
&& cparams[1].isArray()
&& name.equals(cparams[1].getComponentType()
.getName())) {
geom_new = constructPolygon(geom_src);
}
// multipolygon
else if (isArray
&& name.equals("com.vividsolutions.jts.geom.Polygon")) {
// feed the line separated geom into algorithm
Polygon[] polys = constructPolygons(geom_src);
if ( polys != null )
geom_new = (Geometry) method.invoke(factory,
new Object[] { polys });
}
// geometrycollection
else if (isArray
&& name.equals("com.vividsolutions.jts.geom.Geometry")) {
// get all geoms and feed them to create
Geometry[] geoms = new Geometry[geom_src.getNumGeometries()];
for (int j = 0; j < geom_src.getNumGeometries(); j++) {
geoms[j] = geom_src.getGeometryN(j);
}
geom_new = (Geometry) method.invoke(factory,
new Object[] { geoms });
}
// what ends here is based on parameters that are not implemented yet
else {
warnUser(_("conversion-not-implemented", _(type.toLowerCase()), type));
}
return geom_new;
}
/**
* borrowed from org.geotools.shapefile.PolygonHandler
* try to construct _one_ polygon from the given geometry
*/
private Polygon constructPolygon(Geometry src) {
// multigeometries with just points are tried to form one linearring
// for further processing, else multipoint -> polygon is pointless
if (src instanceof GeometryCollection){
Collection<String> types = getTypes( (GeometryCollection) src );
if ( types.size() == 1 && types.iterator().next().equalsIgnoreCase("point") )
src = factory.createLinearRing( src.getCoordinates() );
}
ArrayList<LinearRing> shells = new ArrayList<LinearRing>();
ArrayList<LinearRing> holes = new ArrayList<LinearRing>();
try {
// split by direction shell are cw, holes ccw
for (int i = 0; i < src.getNumGeometries(); i++) {
Geometry geom = src.getGeometryN(i);
LinearRing ring = factory.createLinearRing(geom
.getCoordinates());
if (CGAlgorithms.isCCW(ring.getCoordinates())) {
holes.add(ring);
} else {
shells.add(ring);
}
}
// something went wrong, detect holes mow
if (shells.size() != 1) {
// throw it all together and let findCWHoles find it
shells.addAll(holes);
holes.clear();
// some shells may be CW holes - esri tolerates this
ArrayList<LinearRing> foundholes = findCWHoles(shells);
if (foundholes.size() > 0) {
shells.removeAll(foundholes);
for (int j = 0; j < foundholes.size(); j++) {
LinearRing hole = (LinearRing) foundholes.get(j);
// reverse cw holes
if (!CGAlgorithms.isCCW(hole.getCoordinates()))
hole = reverseRing(hole);
holes.add(hole);
}
}
}
// ups, don't convert this
if (shells.size() != 1) {
warnUser(_("missing-exactly-one-shell"));
return null;
}
return factory.createPolygon((LinearRing) shells.get(0),
(LinearRing[]) ((ArrayList) holes)
.toArray(new LinearRing[0]));
} catch (IllegalArgumentException e) {
warnUser(_e(e.getMessage()));
}
return null;
}
/**
* borrowed from org.geotools.shapefile.PolygonHandler
* try to construct multiple polygons from the given geometry
*/
public Polygon[] constructPolygons( Geometry src ) {
// split src to ring components early
src = factory.createMultiLineString( getRings(src) );
GeometryFactory geometryFactory = this.factory;
ArrayList<LinearRing> shells = new ArrayList<LinearRing>();
ArrayList<LinearRing> holes = new ArrayList<LinearRing>();
// Bad rings are CCW rings not nested in another ring
// and rings with more than 0 and less than 4 points
//ArrayList<LineString> badRings = new ArrayList<LineString>();
// resulting polygons
Polygon[] polygons;
for (int i = 0; i < src.getNumGeometries(); i++) {
Geometry geom = src.getGeometryN(i);
Coordinate[] points = geom.getCoordinates();
try {
LinearRing ring = geometryFactory.createLinearRing(points);
if (CGAlgorithms.isCCW(points)) {
holes.add(ring);
} else {
shells.add(ring);
}
} catch (IllegalArgumentException e) {
// badrings means loss, means error, means stop here
warnUser(_("bad-rings"));
return null;
// crashes on simple points
/*try {
LineString ring = geometryFactory.createLineString(points);
badRings.add(ring);
} catch (IllegalArgumentException e2) {
warnUser(_(e2.getMessage()));
}*/
}
}
if ((shells.size() > 1) && (holes.size() == 0)) {
// some shells may be CW holes - esri tolerates this
holes = findCWHoles(shells); // find all rings contained in others
if (holes.size() > 0) {
shells.removeAll(holes);
ArrayList ccwHoles = new ArrayList(holes.size());
for (int i = 0; i < holes.size(); i++) {
ccwHoles.add(reverseRing((LinearRing) holes.get(i)));
}
holes = ccwHoles;
}
}
// now we have a list of all shells and all holes
ArrayList holesForShells = new ArrayList(shells.size());
ArrayList holesWithoutShells = new ArrayList();
for (int i = 0; i < shells.size(); i++) {
holesForShells.add(new ArrayList());
}
// find holes
for (int i = 0; i < holes.size(); i++) {
LinearRing testRing = (LinearRing) holes.get(i);
LinearRing minShell = null;
Envelope minEnv = null;
Envelope testEnv = testRing.getEnvelopeInternal();
Coordinate testPt = testRing.getCoordinateN(0);
LinearRing tryRing;
for (int j = 0; j < shells.size(); j++) {
tryRing = (LinearRing) shells.get(j);
Envelope tryEnv = tryRing.getEnvelopeInternal();
if (minShell != null)
minEnv = minShell.getEnvelopeInternal();
boolean isContained = false;
Coordinate[] coordList = tryRing.getCoordinates();
if (tryEnv.contains(testEnv)
&& (CGAlgorithms.isPointInRing(testPt, coordList)))
isContained = true;
// check if this new containing ring is smaller than the current
// minimum ring
if (isContained) {
if (minShell == null || minEnv.contains(tryEnv)) {
minShell = tryRing;
}
}
}
if (minShell == null) {
holesWithoutShells.add(testRing);
} else {
((ArrayList) holesForShells.get(shells.indexOf(minShell)))
.add(testRing);
}
}
polygons = new Polygon[shells.size() + holesWithoutShells.size()];
for (int i = 0; i < shells.size(); i++) {
polygons[i] = geometryFactory.createPolygon((LinearRing) shells
.get(i), (LinearRing[]) ((ArrayList) holesForShells.get(i))
.toArray(new LinearRing[0]));
}
// add ccw holes without shells as cw shells
for (int i = 0; i < holesWithoutShells.size(); i++) {
polygons[shells.size() + i] = geometryFactory.createPolygon(
reverseRing( ((LinearRing) holesWithoutShells.get(i)) ), null);
}
holesForShells = null;
holesWithoutShells = null;
shells = null;
holes = null;
return polygons;
}
/**
* finds lr contained in other lr's (holes), returns all holes
*/
private ArrayList<LinearRing> findCWHoles(ArrayList shells) {
ArrayList holesCW = new ArrayList(shells.size());
LinearRing[] noHole = new LinearRing[0];
for (int i = 0; i < shells.size(); i++) {
LinearRing iRing = (LinearRing) shells.get(i);
Envelope iEnv = iRing.getEnvelopeInternal();
Coordinate[] coordList = iRing.getCoordinates();
LinearRing jRing;
for (int j = 0; j < shells.size(); j++) {
if (i == j)
continue;
jRing = (LinearRing) shells.get(j);
Envelope jEnv = jRing.getEnvelopeInternal();
Coordinate jPt = jRing.getCoordinateN(0);
Coordinate jPt2 = jRing.getCoordinateN(1);
if (iEnv.contains(jEnv)
// && (CGAlgorithms.isPointInRing(jPt, coordList) ||
// pointInList(jPt, coordList))
// && (CGAlgorithms.isPointInRing(jPt2, coordList) ||
// pointInList(jPt2, coordList))) {
&& (CGAlgorithms.isPointInRing(jPt, coordList))
&& (CGAlgorithms.isPointInRing(jPt2, coordList))) {
if (!holesCW.contains(jRing)) {
Polygon iPoly = factory.createPolygon(iRing, noHole);
Polygon jPoly = factory.createPolygon(jRing, noHole);
if (iPoly.contains(jPoly))
holesCW.add(jRing);
}
}
}
}
return holesCW;
}
/**
* reverses the order of points in lr (is CW -> CCW or CCW->CW)
*/
private LinearRing reverseRing(LinearRing lr) {
int numPoints = lr.getNumPoints();
Coordinate[] newCoords = new Coordinate[numPoints];
for (int t = 0; t < numPoints; t++) {
newCoords[t] = lr.getCoordinateN(numPoints - t - 1);
}
return factory.createLinearRing(newCoords);
}
/**
* separate line components in a (multi)geometry
*/
private LineString[] getLines(Geometry geom_src) {
return (LineString[]) getLines( geom_src, false);
}
private LinearRing[] getRings(Geometry geom_src) {
return (LinearRing[]) getLines( geom_src, true);
}
private Geometry[] getLines(Geometry geom_src, boolean asRings) {
// List linescol = new ArrayList();
// geom_src.apply(new LineFilter( linescol ));
List linescol = new ArrayList();
for (int i = 0; i < geom_src.getNumGeometries(); i++) {
Geometry g = geom_src.getGeometryN(i);
//System.out.println(g.getNumGeometries()+","+g.getGeometryType());
if (g.getNumGeometries() > 1){
linescol.addAll( Arrays.asList( getLines( g, asRings) ) );
}
else if (g.getGeometryType().toLowerCase().endsWith("polygon"))
LinearComponentExtracter.getLines(g, linescol, !asRings);
else if (asRings)
linescol.add(factory.createLinearRing(g.getCoordinates()));
else
linescol.add(factory.createLineString(g.getCoordinates()));
}
return asRings ? factory.toLinearRingArray(linescol) :
factory.toLineStringArray(linescol);
}
private boolean okCancel( final String title, final String message ){
JPanel panel = new JPanel();
JLabel label = new JLabel("<html> "+message+" </html>");
Font xx = label.getFont();
int fontHeight = label.getFontMetrics(xx).getHeight();
int stringWidth = label.getFontMetrics(xx).stringWidth(label.getText());
int linesCount = (int) Math.floor(stringWidth / 300);
linesCount = Math.max(1, linesCount + 2);
label.setPreferredSize(new Dimension(300, (fontHeight)*linesCount));
FlowLayout f = new FlowLayout( FlowLayout.CENTER, 10, 10);
panel.setLayout(f);
panel.add(label);
OKCancelDialog dlg = new OKCancelDialog(wbc.getWorkbench().getFrame(),
title, true, panel,
new Validator() {
public String validateInput(Component component) {
return null;
}
});
dlg.setVisible(true);
return dlg.wasOKPressed() ? true : false;
}
private class CloseRing extends GeometryEditor.CoordinateOperation {
public Coordinate[] edit(Coordinate[] coordinates, Geometry geometry) {
// ignore already closed
Coordinate first = coordinates[0];
Coordinate last = coordinates[coordinates.length-1];
if (first.equals(last))
return coordinates;
Coordinate[] coordinates_new = new Coordinate[coordinates.length + 1];
for (int i = 0; i < coordinates.length; i++) {
coordinates_new[i] = coordinates[i];
}
coordinates_new[coordinates_new.length - 1] = coordinates[0];
return coordinates_new;
}
}
private class RemoveClosing extends GeometryEditor.CoordinateOperation {
public Coordinate[] edit(Coordinate[] coordinates, Geometry geometry) {
// ignore points, nothing to remove here
if (coordinates.length<2)
return coordinates;
Coordinate first = coordinates[0];
Coordinate last = coordinates[coordinates.length-1];
while (first.equals(last)){
coordinates = removeLast( coordinates );
first = coordinates[0];
last = coordinates[coordinates.length-1];
}
return coordinates;
}
private Coordinate[] removeLast(Coordinate[] coordinates){
Coordinate[] coordinates_new = new Coordinate[coordinates.length - 1];
for (int i = 0; i < coordinates.length - 1; i++) {
coordinates_new[i] = coordinates[i];
}
return coordinates_new;
}
}
private class LineFilter implements GeometryComponentFilter {
private Collection lines;
public LineFilter(List lines) {
this.lines = lines;
}
public void filter(Geometry geom) {
List linescol = new ArrayList();
// convert each subgeom to linestring, jts throws exceptions on impossibles
for (int i = 0; i < geom.getNumGeometries(); i++) {
LineString line = geom.getFactory().createLineString(
geom.getGeometryN(i).getCoordinates());
lines.add(line);
}
}
}
}