///* // * Copyright (C) 2011-2013 Dr. John Lindsay <jlindsay@uoguelph.ca> // * // * 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 plugins; // //import java.util.List; //import whitebox.geospatialfiles.ShapeFile; //import whitebox.geospatialfiles.shapefile.*; //import static whitebox.geospatialfiles.shapefile.ShapeType.POLYGON; //import static whitebox.geospatialfiles.shapefile.ShapeType.POLYGONM; //import static whitebox.geospatialfiles.shapefile.ShapeType.POLYGONZ; //import static whitebox.geospatialfiles.shapefile.ShapeType.POLYLINE; //import static whitebox.geospatialfiles.shapefile.ShapeType.POLYLINEM; //import static whitebox.geospatialfiles.shapefile.ShapeType.POLYLINEZ; //import whitebox.geospatialfiles.shapefile.attributes.DBFField; //import whitebox.interfaces.WhiteboxPlugin; //import whitebox.interfaces.WhiteboxPluginHost; //import whitebox.structures.KdTree; //import java.util.Arrays; //import java.util.ArrayList; //import whitebox.utilities.AxialData; // ///** // * WhiteboxPlugin is used to define a plugin tool for Whitebox GIS. // * // * @author Dr. John Lindsay <jlindsay@uoguelph.ca> // */ //public class GroupPolygonsByOrientation implements WhiteboxPlugin { // // private WhiteboxPluginHost myHost = null; // private String[] args; // // /** // * Used to retrieve the plugin tool's name. This is a short, unique name // * containing no spaces. // * // * @return String containing plugin name. // */ // @Override // public String getName() { // return "GroupPolygonsByOrientation"; // } // // /** // * Used to retrieve the plugin tool's descriptive name. This can be a longer // * name (containing spaces) and is used in the interface to list the tool. // * // * @return String containing the plugin descriptive name. // */ // @Override // public String getDescriptiveName() { // return "Group Polygons By Orientation"; // } // // /** // * Used to retrieve a short description of what the plugin tool does. // * // * @return String containing the plugin's description. // */ // @Override // public String getToolDescription() { // return "Groups polygons of similar orientation."; // } // // /** // * Used to identify which toolboxes this plugin tool should be listed in. // * // * @return Array of Strings. // */ // @Override // public String[] getToolbox() { // String[] ret = {"VectorTools"}; // return ret; // } // // /** // * Sets the WhiteboxPluginHost to which the plugin tool is tied. This is the // * class that the plugin will send all feedback messages, progress updates, // * and return objects. // * // * @param host The WhiteboxPluginHost that called the plugin tool. // */ // @Override // public void setPluginHost(WhiteboxPluginHost host) { // myHost = host; // } // // /** // * Used to communicate feedback pop-up messages between a plugin tool and // * the main Whitebox user-interface. // * // * @param feedback String containing the text to display. // */ // private void showFeedback(String message) { // if (myHost != null) { // myHost.showFeedback(message); // } else { // System.out.println(message); // } // } // // /** // * Used to communicate a return object from a plugin tool to the main // * Whitebox user-interface. // * // * @return Object, such as an output WhiteboxRaster. // */ // private void returnData(Object ret) { // if (myHost != null) { // myHost.returnData(ret); // } // } // private int previousProgress = 0; // private String previousProgressLabel = ""; // // /** // * Used to communicate a progress update between a plugin tool and the main // * Whitebox user interface. // * // * @param progressLabel A String to use for the progress label. // * @param progress Float containing the progress value (between 0 and 100). // */ // private void updateProgress(String progressLabel, int progress) { // if (myHost != null && ((progress != previousProgress) // || (!progressLabel.equals(previousProgressLabel)))) { // myHost.updateProgress(progressLabel, progress); // } else { // System.out.println("Progress: " + progress + "%"); // } // previousProgress = progress; // previousProgressLabel = progressLabel; // } // // /** // * Used to communicate a progress update between a plugin tool and the main // * Whitebox user interface. // * // * @param progress Float containing the progress value (between 0 and 100). // */ // private void updateProgress(int progress) { // if (myHost != null && progress != previousProgress) { // myHost.updateProgress(progress); // } else { // System.out.println("Progress: " + progress + "%"); // } // } // // /** // * Sets the arguments (parameters) used by the plugin. // * // * @param args // */ // @Override // public void setArgs(String[] args) { // this.args = args.clone(); // } // private boolean cancelOp = false; // // /** // * Used to communicate a cancel operation from the Whitebox GUI. // * // * @param cancel Set to true if the plugin should be canceled. // */ // @Override // public void setCancelOp(boolean cancel) { // cancelOp = cancel; // } // // private void cancelOperation() { // showFeedback("Operation cancelled."); // updateProgress("Progress: ", 0); // } // private boolean amIActive = false; // // /** // * Used by the Whitebox GUI to tell if this plugin is still running. // * // * @return a boolean describing whether or not the plugin is actively being // * used. // */ // @Override // public boolean isActive() { // return amIActive; // } // // @Override // public void run() { // // amIActive = true; // String inputFile; // double x, y, x1, x2, y1, y2; // int progress; // int oldProgress; // int i, n; // double[][] vertices = null; // int pointNum = 0; // int numPoints = 0; // int numPolys = 0; // int numFeatures; // double neighbourhoodRadius = 0; // double maxAngularDeviation; // ShapeType shapeType, outputShapeType; // List<KdTree.Entry<EndPointInfo>> results; // double[] entry; // int[] parts = {0}; // double psi = 0; // Object[] rowData; // double DegreeToRad = Math.PI / 180; // double[] axes = new double[2]; // double newXAxis = 0; // double newYAxis = 0; // double longAxis; // double shortAxis; // final double rightAngle = Math.toRadians(90); // double midX, midY; // double[] newBoundingBox = new double[4]; // double slope; // double boxCentreX, boxCentreY; // int pointQuadrant1, pointQuadrant2; // double elongation; // double elongationThreshold = 0.25; // final double radiansToDegrees = 180 / Math.PI; // double[][] points; // Geometry poly; // double dist; // double maxDist; // double[] weights; // // if (args.length <= 0) { // showFeedback("Plugin parameters have not been set."); // return; // } // // inputFile = args[0]; // maxAngularDeviation = Double.parseDouble(args[1]); // String outputFile = args[2]; // // // check to see that the inputHeader and outputHeader are not null. // if ((inputFile == null)) { // showFeedback("One or more of the input parameters have not been set properly."); // return; // } // // try { // // set up the input shapefile. // ShapeFile input = new ShapeFile(inputFile); // shapeType = input.getShapeType(); // numPolys = input.getNumberOfRecords(); // // // make sure that the shapetype is a flavour of polygon. // if (shapeType.getBaseType() != ShapeType.POLYGON) { // showFeedback("This tool only works with shapefiles of a polygon base shape type."); // return; // } // // FeatureInfo[] featureInfo = new FeatureInfo[numPolys]; // // DBFField fields[] = new DBFField[2]; // // fields[0] = new DBFField(); // fields[0].setName("PARENT_ID"); // fields[0].setDataType(DBFField.FIELD_TYPE_N); // fields[0].setFieldLength(10); // fields[0].setDecimalCount(0); // // fields[1] = new DBFField(); // fields[1].setName("TOP_BOT"); // fields[1].setDataType(DBFField.FIELD_TYPE_N); // fields[1].setFieldLength(10); // fields[1].setDecimalCount(0); // // //ShapeFile output = new ShapeFile(outputFile, ShapeType.POINT, fields); // ShapeFile output = new ShapeFile(outputFile, ShapeType.POLYLINE, fields); // // KdTree<EndPointInfo> pointsTree = new KdTree.SqrEuclid(2, new Integer(numPolys * 2)); // // // // find the minimum bounding box of each shape and locate points at the top and bottom centres // int recordNum; // oldProgress = -1; // for (ShapeFileRecord record : input.records) { // recordNum = record.getRecordNumber(); // vertices = record.getGeometry().getPoints(); // int numVertices = vertices.length; // double east = Double.NEGATIVE_INFINITY; // double west = Double.POSITIVE_INFINITY; // double north = Double.NEGATIVE_INFINITY; // double south = Double.POSITIVE_INFINITY; // // for (i = 0; i < numVertices; i++) { // if (vertices[i][0] > east) { // east = vertices[i][0]; // } // if (vertices[i][0] < west) { // west = vertices[i][0]; // } // if (vertices[i][1] > north) { // north = vertices[i][1]; // } // if (vertices[i][1] < south) { // south = vertices[i][1]; // } // // } // // midX = west + (east - west) / 2.0; // midY = south + (north - south) / 2.0; // // // double[][] verticesRotated = new double[numVertices][2]; // axes[0] = 9999999; // axes[1] = 9999999; // slope = 0; // boxCentreX = 0; // boxCentreY = 0; // // Rotate the edge cells in 0.5 degree increments. // for (int m = 0; m <= 180; m++) { // psi = -m * 0.5 * DegreeToRad; // rotation in clockwise direction // // Rotate each edge cell in the array by m degrees. // for (n = 0; n < numVertices; n++) { // x = vertices[n][0] - midX; // y = vertices[n][1] - midY; // verticesRotated[n][0] = (x * Math.cos(psi)) - (y * Math.sin(psi)); // verticesRotated[n][1] = (x * Math.sin(psi)) + (y * Math.cos(psi)); // } // // calculate the minimum bounding box in this coordinate // // system and see if it is less // newBoundingBox[0] = Double.MAX_VALUE; // west // newBoundingBox[1] = Double.MIN_VALUE; // east // newBoundingBox[2] = Double.MAX_VALUE; // north // newBoundingBox[3] = Double.MIN_VALUE; // south // for (n = 0; n < numVertices; n++) { // x = verticesRotated[n][0]; // y = verticesRotated[n][1]; // if (x < newBoundingBox[0]) { // newBoundingBox[0] = x; // } // if (x > newBoundingBox[1]) { // newBoundingBox[1] = x; // } // if (y < newBoundingBox[2]) { // newBoundingBox[2] = y; // } // if (y > newBoundingBox[3]) { // newBoundingBox[3] = y; // } // } // newXAxis = newBoundingBox[1] - newBoundingBox[0]; // newYAxis = newBoundingBox[3] - newBoundingBox[2]; // // if ((newXAxis * newYAxis) < (axes[0] * axes[1])) { // minimize the area of the bounding box. // axes[0] = newXAxis; // axes[1] = newYAxis; // // if (axes[0] > axes[1]) { // slope = -psi; // } else { // slope = -(rightAngle + psi); // } // x = newBoundingBox[0] + newXAxis / 2; // y = newBoundingBox[2] + newYAxis / 2; // boxCentreX = midX + (x * Math.cos(-psi)) - (y * Math.sin(-psi)); // boxCentreY = midY + (x * Math.sin(-psi)) + (y * Math.cos(-psi)); // } // } // longAxis = Math.max(axes[0], axes[1]); // shortAxis = Math.min(axes[0], axes[1]); // // slope = AxialData.rationalizeAxialAngle(slope); // // // major axis end points // x1 = boxCentreX + longAxis / 2.0 * Math.cos(slope); // y1 = boxCentreY + longAxis / 2.0 * Math.sin(slope); // // x2 = boxCentreX - longAxis / 2.0 * Math.cos(slope); // y2 = boxCentreY - longAxis / 2.0 * Math.sin(slope); // // if (x1 >= x2 && y1 >= y2) { // pointQuadrant1 = 1; // pointQuadrant2 = 3; // } else if (x1 >= x2 && y1 < y2) { // pointQuadrant1 = 4; // pointQuadrant2 = 2; // } else if (x1 < x2 && y1 >= y2) { // pointQuadrant1 = 2; // pointQuadrant2 = 4; // } else { // if (x1 < x2 && y1 < y2) { // pointQuadrant1 = 3; // pointQuadrant2 = 1; // } // // featureInfo[recordNum - 1] = new FeatureInfo(recordNum, shortAxis, longAxis, slope, // new EndPoint(x1, y1, pointQuadrant1), new EndPoint(x2, y2, pointQuadrant2)); // // elongation = featureInfo[recordNum - 1].getElongation(); // // if (elongation > elongationThreshold) { // // pointsTree.addPoint(new double[]{y1, x1}, new EndPointInfo(recordNum, 1, pointQuadrant1)); // // pointsTree.addPoint(new double[]{y2, x2}, new EndPointInfo(recordNum, 2, pointQuadrant2)); // // } // // //// rowData = new Object[2]; //// rowData[0] = new Double(recordNum); //// rowData[1] = new Double(pointQuadrant1); //// output.addRecord(new whitebox.geospatialfiles.shapefile.Point(x1, y1), rowData); //// //// rowData = new Object[2]; //// rowData[0] = new Double(recordNum); //// rowData[1] = new Double(pointQuadrant2); //// output.addRecord(new whitebox.geospatialfiles.shapefile.Point(x2, y2), rowData); // // if (cancelOp) { // cancelOperation(); // return; // } // progress = (int) ((recordNum * 100.0) / numPolys); // if (progress > oldProgress) { // updateProgress(progress); // } // oldProgress = progress; // // } // // int currentGroupID = 1; // int numFeaturesInString; // oldProgress = -1; // n = 0; // for (FeatureInfo fi : featureInfo) { // n++; // if (fi.getElongation() > elongationThreshold && fi.getGroupID() < 0) { // numFeaturesInString = 1; // EndPoint ep1 = fi.getEndPoint1(); // // int myQuad = ep1.getQuadrantNumber(); // // x1 = ep1.getX(); // y1 = ep1.getY(); // slope = fi.getSlope(); //AxialData.rationalizeAxialAngle(fi.getSlope()); // // longAxis = fi.getLongAxisLength(); // shortAxis = fi.getShortAxisLength(); // double semiShortAxis = shortAxis / 2.0; // // maxDist = longAxis * 1.25; // // x = x1 + Math.cos(slope) * longAxis; // y = y1 + Math.sin(slope) * longAxis; // // double maxAbsAngle = Math.atan((semiShortAxis * 1.5) / longAxis) * radiansToDegrees; // // results = pointsTree.neighborsWithinRange(new double[]{y1, x1}, longAxis); // //// weights = new double[results.size()]; // double weight; // double maxWeight; // EndPoint maxWeightedEndPoint = new EndPoint(); // boolean foundMaxWeight; // // maxWeight = 0; // foundMaxWeight = false; // for (KdTree.Entry entry2 : results) { // EndPointInfo epi = (EndPointInfo) (entry2.value); // if (epi.getQuadrantNumber() != myQuad) { // int otherPoly = epi.getFeatureNum() - 1; // // how close is this poly's axial angle to the current poly's axial angle? // //double otherSlope = AxialData.rationalizeAxialAngle(featureInfo[otherPoly].slope); // double alignmentAngle = radiansToDegrees * (Math.abs(slope - featureInfo[otherPoly].slope)); // // if (alignmentAngle < maxAngularDeviation) { // EndPoint ep3; // if (epi.getPointNum() == 1) { // ep3 = featureInfo[otherPoly].getEndPoint1(); // } else { // ep3 = featureInfo[otherPoly].getEndPoint2(); // } // x2 = ep3.getX(); // y2 = ep3.getY(); // // dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)); // // // what is the angle between this point and the long-axis? // double angle = AxialData.rationalizeAxialAngle(Math.atan2(y - y2, x - x2)); // // double angleToAxis = radiansToDegrees * Math.abs(slope - angle); // // if (dist <= maxDist && angleToAxis <= maxAbsAngle) { //// points = new double[2][2]; //// points[0][0] = x1; //// points[0][1] = y1; //// points[1][0] = x2; //// points[1][1] = y2; //// //// rowData = new Object[2]; //// rowData[0] = new Double(fi.getFeatureNum()); //// rowData[1] = new Double(1); //// //// poly = new PolyLine(parts, points); //// output.addRecord(poly, rowData); // // weight = 4 - angleToAxis / maxAbsAngle - // alignmentAngle / maxAngularDeviation - // dist / longAxis - // Math.min(fi.getBoxArea(), featureInfo[otherPoly].getBoxArea()) // / Math.max(fi.getBoxArea(), featureInfo[otherPoly].getBoxArea()); // if (weight > maxWeight) { // maxWeight = weight; // maxWeightedEndPoint = ep3; // foundMaxWeight = true; // } // } // } // } // } // // if (foundMaxWeight) { // points = new double[2][2]; // points[0][0] = x1; // points[0][1] = y1; // points[1][0] = maxWeightedEndPoint.getX(); // points[1][1] = maxWeightedEndPoint.getY(); // // rowData = new Object[2]; // rowData[0] = new Double(fi.getFeatureNum()); // rowData[1] = new Double(1); // // poly = new PolyLine(parts, points); // output.addRecord(poly, rowData); // } // // EndPoint ep2 = fi.getEndPoint2(); // // myQuad = ep2.getQuadrantNumber(); // // x1 = ep2.getX(); // y1 = ep2.getY(); // // x = x1 - Math.cos(slope) * longAxis; // y = y1 - Math.sin(slope) * longAxis; // // results = pointsTree.neighborsWithinRange(new double[]{y1, x1}, longAxis); // // maxWeight = 0; // foundMaxWeight = false; // for (KdTree.Entry entry2 : results) { // EndPointInfo epi = (EndPointInfo) (entry2.value); // if (epi.getQuadrantNumber() != myQuad) { // int otherPoly = epi.getFeatureNum() - 1; // // how close is this poly's axial angle to the current poly's axial angle? // //double otherSlope = AxialData.rationalizeAxialAngle(featureInfo[otherPoly].slope); // double alignmentAngle = radiansToDegrees * (Math.abs(slope - featureInfo[otherPoly].slope)); // // if (alignmentAngle < maxAngularDeviation) { // EndPoint ep3; // if (epi.getPointNum() == 1) { // ep3 = featureInfo[otherPoly].getEndPoint1(); // } else { // ep3 = featureInfo[otherPoly].getEndPoint2(); // } // x2 = ep3.getX(); // y2 = ep3.getY(); // // // what is the angle between this point and the long-axis? // dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)); // // double angle = AxialData.rationalizeAxialAngle(Math.atan2(y - y2, x - x2)); // // double angleToAxis = radiansToDegrees * Math.abs(slope - angle); // // if (dist <= maxDist && angleToAxis <= maxAbsAngle) { //// points = new double[2][2]; //// points[0][0] = x1; //// points[0][1] = y1; //// points[1][0] = x2; //// points[1][1] = y2; //// //// rowData = new Object[2]; //// rowData[0] = new Double(fi.getFeatureNum()); //// rowData[1] = new Double(2.0); //// //// poly = new PolyLine(parts, points); //// output.addRecord(poly, rowData); // // weight = 4 - angleToAxis / maxAbsAngle - // alignmentAngle / maxAngularDeviation - // dist / longAxis - // Math.min(fi.getBoxArea(), featureInfo[otherPoly].getBoxArea()) // / Math.max(fi.getBoxArea(), featureInfo[otherPoly].getBoxArea()); // if (weight > maxWeight) { // maxWeight = weight; // maxWeightedEndPoint = ep3; // foundMaxWeight = true; // } // } // } // } // } // // // if (foundMaxWeight) { // points = new double[2][2]; // points[0][0] = x1; // points[0][1] = y1; // points[1][0] = maxWeightedEndPoint.getX(); // points[1][1] = maxWeightedEndPoint.getY(); // // rowData = new Object[2]; // rowData[0] = new Double(fi.getFeatureNum()); // rowData[1] = new Double(2); // // poly = new PolyLine(parts, points); // output.addRecord(poly, rowData); // } // // } // // if (cancelOp) { // cancelOperation(); // return; // } // progress = (int) ((n * 100.0) / numPolys); // if (progress > oldProgress) { // updateProgress(progress); // } // oldProgress = progress; // } // // output.write(); // //// // returning a header file string displays the image. //// updateProgress("Displaying vector: ", 0); //// returnData(outputFile); // // // } catch (Exception e) { // showFeedback(e.getMessage()); // } finally { // updateProgress("Progress: ", 0); // // tells the main application that this process is completed. // amIActive = false; // myHost.pluginComplete(); // } // // } // // //This method is only used during testing. // public static void main(String[] args) { // args = new String[3]; // args[0] = "/Users/johnlindsay/Documents/Research/Contracts/NRCan 2012/Data/MediumLakes/medium lakes2.shp"; // args[1] = "30.0"; // args[2] = "/Users/johnlindsay/Documents/Research/Contracts/NRCan 2012/Data/MediumLakes/tmp1.shp"; // // GroupPolygonsByOrientation gpbo = new GroupPolygonsByOrientation(); // gpbo.setArgs(args); // gpbo.run(); // } // // private class FeatureInfo { // // private int featureNum; // private double shortAxisLength; // private double longAxisLength; // private double elongation = -1; // private double slope; // private EndPoint endPoint1; // private EndPoint endPoint2; // private int groupID = -1; // // public FeatureInfo() { // } // // public FeatureInfo(int featureNum, double shortAxisLength, double longAxisLength, // double slope, EndPoint ep1, EndPoint ep2) { // this.featureNum = featureNum; // this.shortAxisLength = shortAxisLength; // this.longAxisLength = longAxisLength; // this.slope = slope; // this.endPoint1 = ep1; // this.endPoint2 = ep2; // } // // public int getFeatureNum() { // return featureNum; // } // // public void setFeatureNum(int featureNum) { // this.featureNum = featureNum; // } // // public double getShortAxisLength() { // return shortAxisLength; // } // // public void setShortAxisLength(double shortAxisLength) { // this.shortAxisLength = shortAxisLength; // } // // public double getLongAxisLength() { // return longAxisLength; // } // // public void setLongAxisLength(double longAxisLength) { // this.longAxisLength = longAxisLength; // } // // public double getSlope() { // return slope; // } // // public void setSlope(double slope) { // this.slope = slope; // } // // public EndPoint getEndPoint1() { // return endPoint1; // } // // public void setEndPoint1(EndPoint endPoint1) { // this.endPoint1 = endPoint1; // } // // public EndPoint getEndPoint2() { // return endPoint2; // } // // public void setEndPoint2(EndPoint endPoint2) { // this.endPoint2 = endPoint2; // } // // public double getElongation() { // if (elongation < 0) { // elongation = 1 - shortAxisLength / longAxisLength; // } // return elongation; // } // // public int getGroupID() { // return groupID; // } // // public void setGroupID(int groupID) { // this.groupID = groupID; // } // // public double getBoxArea() { // return longAxisLength * shortAxisLength; // } // } // // private class EndPoint { // // private double x; // private double y; // private int quadrantNumber; // // public EndPoint() { // } // // public EndPoint(double x, double y, int quadrantNumber) { // this.x = x; // this.y = y; // this.quadrantNumber = quadrantNumber; // } // // public double getX() { // return x; // } // // public void setX(double x) { // this.x = x; // } // // public double getY() { // return y; // } // // public void setY(double y) { // this.y = y; // } // // public int getQuadrantNumber() { // return quadrantNumber; // } // // public void setQuadrantNumber(int quadrantNumber) { // this.quadrantNumber = quadrantNumber; // } // } // // private class EndPointInfo { // // int featureNum; // int pointNum; // int quadrantNumber; // // public EndPointInfo() { // } // // public EndPointInfo(int featureNum, int endPointNum, int quandrantNumber) { // this.featureNum = featureNum; // this.quadrantNumber = quandrantNumber; // this.pointNum = endPointNum; // } // // public int getFeatureNum() { // return featureNum; // } // // public void setFeatureNum(int featureNum) { // this.featureNum = featureNum; // } // // public int getQuadrantNumber() { // return quadrantNumber; // } // // public void setQuadrantNumber(int quadrantNumber) { // this.quadrantNumber = quadrantNumber; // } // // public int getPointNum() { // return pointNum; // } // // public void setPointNum(int endPointNum) { // this.pointNum = endPointNum; // } // } //}