/* * Copyright (C) 2014 Alec Dhuse * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package co.foldingmap.actions; import co.foldingmap.map.vector.VectorLayer; import co.foldingmap.map.vector.CoordinateList; import co.foldingmap.map.vector.CoordinateMath; import co.foldingmap.map.vector.VectorObject; import co.foldingmap.map.vector.VectorObjectList; import co.foldingmap.map.vector.Coordinate; import co.foldingmap.map.vector.LineString; import co.foldingmap.map.vector.Polygon; import co.foldingmap.GUISupport.Updateable; import co.foldingmap.Logger; import co.foldingmap.map.DigitalMap; import co.foldingmap.map.MapObject; import co.foldingmap.map.MapObjectList; import co.foldingmap.map.MapView; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.util.ArrayList; /** * Merges one or more objects into a single Polygon. * * @author Alec */ public class MergeToPolygon extends Action { private DigitalMap mapData; private VectorObject newObject; private MapObjectList<MapObject> objectsToMerge; private MapView mapView; private Updateable updateable; public MergeToPolygon(DigitalMap mapData, Updateable updateable) { try { this.mapData = mapData; this.commandDescription = "Merge To Polygon"; this.updateable = updateable; } catch (Exception e) { Logger.log(Logger.ERR, "Error in MergeToPolygon.Constructor(DigitalMap) - " + e); } } /** * Returns if this Action can be undone. * * @return */ @Override public boolean canUndo() { return true; } @Override public void execute() { ArrayList<CoordinateList<Coordinate>> polygons, validPolygons; CoordinateList<Coordinate> coordinates, userSelection; boolean verticesCross; int index; LineString lineString; String newName, newType; VectorLayer parentLayer; VectorObjectList<VectorObject> vectorObjects; objectsToMerge = mapData.getSelectedObjects().clone(); mapView = mapData.getLastMapView(); vectorObjects = new VectorObjectList<VectorObject>(objectsToMerge); index = -1; try { parentLayer = (VectorLayer) objectsToMerge.get(0).getParentLayer(); coordinates = MergeFunctions.getCoordinatesForMerge(vectorObjects); validPolygons = new ArrayList<CoordinateList<Coordinate>>(); newType = "(Unspecified Polygon)"; newName = "New Polygon"; if (coordinates.size() > 2) { //try to use the selection order made by the user userSelection = new CoordinateList<Coordinate>(); if (vectorObjects.getMapPoints().size() == objectsToMerge.size()) { for (int i = 0; i < objectsToMerge.size(); i++) userSelection.add(objectsToMerge.get(i).getCoordinateList().get(0)); verticesCross = testPolygonCoordinates(userSelection); } else if (vectorObjects.getLineStrings().size() == objectsToMerge.size()) { VectorObjectList<VectorObject> c = vectorObjects.getFullCopy(); c.remove(0); VectorObjectList<VectorObject> line = MergeToLineString.mergeLineStringToBestMatch((LineString) objectsToMerge.get(0), c); verticesCross = false; userSelection.addAll(line.get(0).getCoordinateList()); newName = line.get(0).getName(); } else { for (int i = 0; i < objectsToMerge.size(); i++) { userSelection.addAll(objectsToMerge.get(i).getCoordinateList()); } if (userSelection.size() < 25) { verticesCross = testPolygonCoordinates(userSelection); } else { //too many points to try and check verticesCross = false; } } if (verticesCross == false) { newObject = new Polygon(newName, newType, userSelection); } else { //create polygon vertices polygons = getNextCoordinate(coordinates, 0); for (CoordinateList<Coordinate> possiblePolygon: polygons) { verticesCross = testPolygonCoordinates(possiblePolygon); if (verticesCross == false) { validPolygons.add(possiblePolygon); } } //just select the first one for now newObject = new Polygon(newName, newType, validPolygons.get(0)); } for (VectorObject oldObject: vectorObjects) { parentLayer = (VectorLayer) oldObject.getParentLayer(); index = parentLayer.getObjectList().getIndexOf(oldObject); parentLayer.getObjectList().remove(oldObject); } if (index >= 0) { if (index < parentLayer.getObjectList().size()) { parentLayer.addObject(newObject, index); } else { parentLayer.addObject(newObject); } } else { parentLayer.addObject(newObject); } if (objectsToMerge.size() == 1) { VectorObject mObject = (VectorObject) objectsToMerge.get(0); if (mObject instanceof LineString) { lineString = (LineString) mObject; if (lineString.getObjectClass().equals("Coastline")) { newObject.setClass("Lake"); } else if (lineString.getObjectClass().equals("Water Way - Stream")) { newObject.setClass("Lake"); } else if (lineString.getObjectClass().equals("Water Way - River")) { newObject.setClass("Lake"); } } newObject.setName(mObject.getName()); newObject.setCustomDataFields(mObject.getCustomDataFields()); } } //end size check } catch (Exception e) { Logger.log(Logger.ERR, "Error in MergeToPolygon.execute() - " + e); } newObject.updateOutlines(mapData.getTheme()); mapData.deselectObjects(); mapData.setSelected(newObject); if (updateable != null) updateable.update(); } @Override public void undo() { VectorLayer parentLayer; try { parentLayer = (VectorLayer) newObject.getParentLayer(); parentLayer.getObjectList().remove(newObject); for (MapObject oldObject: objectsToMerge) { parentLayer = (VectorLayer) oldObject.getParentLayer(); parentLayer.addObject((VectorObject) oldObject); } mapData.setSelected(objectsToMerge); } catch (Exception e) { Logger.log(Logger.ERR, "Error in CommandMergeToPolygon.execute: " + e); } } private ArrayList<CoordinateList<Coordinate>> getNextCoordinate(CoordinateList<Coordinate> coordinates, int startIndex) { ArrayList<CoordinateList<Coordinate>> returnResult, recursiveResult; CoordinateList<Coordinate> coordinatesCopy, newCoordinates, newCoordinatesCopy; newCoordinates = new CoordinateList<Coordinate>(); returnResult = new ArrayList<CoordinateList<Coordinate>>(); if (startIndex < coordinates.size()) { newCoordinates.add(coordinates.remove(startIndex)); if (coordinates.size() > 1) { for (int i = 0; i < coordinates.size(); i++) { coordinatesCopy = (CoordinateList<Coordinate>) coordinates.clone(); recursiveResult = getNextCoordinate(coordinatesCopy, i); for (CoordinateList<Coordinate> listResult: recursiveResult) { newCoordinatesCopy = (CoordinateList<Coordinate>) newCoordinates.clone(); newCoordinatesCopy.addAll(listResult); returnResult.add(newCoordinatesCopy); } } } else { newCoordinates.add(coordinates.remove(0)); returnResult.add(newCoordinates); } } return returnResult; } /** * Tests to see if vertices cross * @param polygonCoordinates * @return */ private boolean testPolygonCoordinates(CoordinateList<Coordinate> polygonCoordinates) { ArrayList<Line2D> vertices; boolean verticesCross; Coordinate c1, c2; Point2D p1, p2; if (polygonCoordinates.size() > 2) { vertices = new ArrayList<Line2D>(0); for (int i = 0; i < polygonCoordinates.size(); i++) { c1 = polygonCoordinates.get(i); if ((i + 1) < polygonCoordinates.size()) { c2 = polygonCoordinates.get(i+1); } else { c2 = polygonCoordinates.get(0); } p1 = new Point2D.Double(mapView.getX(c1, MapView.NO_WRAP), mapView.getY(c1)); p2 = new Point2D.Double(mapView.getX(c2, MapView.NO_WRAP), mapView.getY(c2)); vertices.add(new Line2D.Double(p1, p2)); } verticesCross = CoordinateMath.testPolygonVertices(vertices); } else { verticesCross = true; } return verticesCross; } }