/** * LineTypeDensities.java * * --Copyright notice-- * * Copyright (c) School of Geography, University of Leeds. * http://www.geog.leeds.ac.uk/ * This software is licensed under 'The Artistic License' which can be found at * the Open Source Initiative website at... * http://www.opensource.org/licenses/artistic-license.php * Please note that the optional Clause 8 does not apply to this code. * * The Standard Version source code, and associated documentation can be found at... * [online] http://www.ccg.leeds.ac.uk/software/ * * This code contains Progress Bar code by Marc Mettes: * http://inversionconsulting.blogspot.com/2008/03/java-jdialog-and-jprogressbar-example.html * licenced under the Creative Commons Attribution License 3.0: * http://creativecommons.org/licenses/by/3.0/us/ * * --End of Copyright notice-- * **/ package uk.ac.leeds.ccg.boundarytypeanalyst; import java.awt.BorderLayout; import java.awt.Dimension; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.DateFormat; import java.util.Date; import com.esri.arcgis.addins.desktop.Button; import com.esri.arcgis.arcmapui.IMxDocument; import com.esri.arcgis.carto.IAttributeTable; import com.esri.arcgis.carto.IGeoFeatureLayer; import com.esri.arcgis.carto.IMap; import com.esri.arcgis.carto.TopologyLayer; import com.esri.arcgis.framework.IApplication; import com.esri.arcgis.framework.IDocument; import com.esri.arcgis.geodatabase.IEnumTopologyEdge; import com.esri.arcgis.geodatabase.IFeature; import com.esri.arcgis.geodatabase.IFeatureClassProxy; import com.esri.arcgis.geodatabase.IFeatureCursor; import com.esri.arcgis.geodatabase.IFeatureDatasetProxy; import com.esri.arcgis.geodatabase.IFeatureWorkspace; import com.esri.arcgis.geodatabase.IQueryFilter; import com.esri.arcgis.geodatabase.ITopology; import com.esri.arcgis.geodatabase.ITopologyEdge; import com.esri.arcgis.geodatabase.ITopologyGraph; import com.esri.arcgis.geodatabase.IWorkspace; import com.esri.arcgis.geodatabase.IWorkspaceProxy; import com.esri.arcgis.geodatabase.QueryFilter; import com.esri.arcgis.geometry.IGeometry; import com.esri.arcgis.geometry.Polygon; import com.esri.arcgis.geometry.Polyline; import com.esri.arcgis.interop.AutomationException; import com.esri.arcgis.interop.Cleaner; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; /** * ArcGIS button addin to calculate edge density statistics for each boundary type and totals. * Densities are calculated for the area the individual edge types fall in, and for the total area occupied by all edges. * Note the areas taken are square and also the convex hull, either of which may or may not skew statistics. * * * * * * * * * * * * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * This code is broken. Convex hulls seem to be produced as polylines and therefore attempts to get their areas break. * This is not as in API, which states they are polygons. * Use LineTypeLengths instead, which just generates rectangular boxes. * * TODO: Use Convex Hull generating Geoprocessing tool instead of Geometry calls. * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * * * * * * * * * * * * * * * * * * * * * @author Andrew Evans * @version 0.1 */ public class LineTypeDensities extends Button { private IApplication app; private IMxDocument mxDoc; private IMap map; private IGeoFeatureLayer[] iGeoFeaturelayers; private TopologyLayer topologyLayer; private IFeatureDatasetProxy ifeatureDatasetProxyGot; private IWorkspaceProxy iWorkspaceProxy; private IWorkspace iWorkspace; private IFeatureWorkspace ifeatureWorkspace; private IFeatureDatasetProxy ifeatureDatasetProxyOpened; private IFeatureClassProxy[] iFeatureClassProxys; private ITopology it; private ITopologyGraph iTopologyGraph; private boolean debug = true; // Change in BoundaryTypeAnalysis. private boolean select = false; // Change in BoundaryTypeAnalysis. private BoundaryTypeAnalyst extension; private JProgressBar pb = new JProgressBar(0,100); private javax.swing.JDialog dialog; // Initialisation methods ------------------------------------------------------- /** * Called when the button is clicked. * * @exception java.io.IOException if there are interop problems. * @exception com.esri.arcgis.interop.AutomationException if the component throws an ArcObjects exception. */ @Override public void onClick() throws IOException, AutomationException { extension = BoundaryTypeAnalyst.getInstance(); initialiseMapVariables(); initProgressBar(); lineTypeDensities(); } /** * Sets up progress bar. */ public void initProgressBar () { pb.setPreferredSize(new Dimension(175,20)); pb.setString("Working"); pb.setStringPainted(true); pb.setValue(0); JLabel label = new JLabel("Progress: "); JPanel center_panel = new JPanel(); center_panel.add(label); center_panel.add(pb); dialog = new javax.swing.JDialog((java.awt.Frame)null, "Line Type Densities"); dialog.getContentPane().add(center_panel, BorderLayout.CENTER); dialog.pack(); dialog.setVisible(true); dialog.setLocationRelativeTo(null); // centre on screen dialog.toFront(); // raise above other java windows } /** * Sets up map objects, feature class and layer arrays. */ private void initialiseMapVariables(){ Object[] variables = extension.getVariables(); app = (IApplication) variables[0]; mxDoc = (IMxDocument) variables[1]; map = (IMap) variables[2]; iGeoFeaturelayers = (IGeoFeatureLayer[]) variables[3]; topologyLayer = (TopologyLayer) variables[4]; ifeatureDatasetProxyGot = (IFeatureDatasetProxy) variables[5]; iWorkspaceProxy = (IWorkspaceProxy) variables[6]; iWorkspace = (IWorkspace) variables[7]; ifeatureWorkspace = (IFeatureWorkspace) variables[8]; ifeatureDatasetProxyOpened = (IFeatureDatasetProxy) variables[9]; iFeatureClassProxys = (IFeatureClassProxy[]) variables[10]; it = (ITopology) variables[11]; iTopologyGraph = (ITopologyGraph) variables[12]; debug = ((Boolean) variables[13]).booleanValue(); select = ((Boolean) variables[14]).booleanValue(); } /** * Method for listing density statistics associated with polylines. * Densities are calculated for the area the individual edge types fall in, and for the total area occupied by all edges. * Note the areas taken are square and the convex hull, either of which will skew statistics one way or another. * Reports Type, number of polylines of that type, total length of polylines of that type, rectangular area encompassing that type, * total area encompassing all types, hull area for that type, total hull area for all types, density in rectangle for that type, density for whole * rectangular area, density for the local hull, density for the hull of all types, total density for all edges in maximum rectangle, total density for all edges in * hull.XXXX * @todo Need to think about double counting and statistics - unlikely to skew minimums, as minimum distances can be less for neighbour, but maximums? */ private void lineTypeDensities() { try { // Init necessary storage variables. pb.setValue(0); System.out.println("Starting lineTypeDensity analysis"); String[] names = new String[iGeoFeaturelayers.length]; double[] lengths = new double[iGeoFeaturelayers.length]; double[] areas = new double[iGeoFeaturelayers.length]; double[] hullAreas = new double[iGeoFeaturelayers.length]; IGeometry hullUnion = null; IGeometry layerUnion = null; double topoLength = 0; int[] n = new int[iGeoFeaturelayers.length]; double maxArea = iTopologyGraph.getBuildExtent().getHeight() * iTopologyGraph.getBuildExtent().getWidth(); IQueryFilter queryFilter = new QueryFilter(); IFeature feature = null; IFeatureCursor cursor = null; // Calc topolength IEnumTopologyEdge iEnumTopologyEdges = iTopologyGraph.getEdges(); iEnumTopologyEdges.reset(); ITopologyEdge iTopologyEdge = iEnumTopologyEdges.next(); while (iTopologyEdge != null) { topoLength = topoLength + ((Polyline)iTopologyEdge.getGeometry()).getLength(); iTopologyEdge = iEnumTopologyEdges.next(); } System.out.println("topolength = " + topoLength); // Run through layers. for (int i = 0; i < iGeoFeaturelayers.length; i++) { int countDone = 0; int countNotDone = 0; pb.setValue((int)((100.0 / iGeoFeaturelayers.length) * i)); names[i] = iGeoFeaturelayers[i].getFeatureClass().getAliasName(); System.out.println("Processing " + names[i]); areas[i] = iGeoFeaturelayers[i].getAreaOfInterest().getHeight() * iGeoFeaturelayers[i].getAreaOfInterest().getWidth(); n[i] = ((IAttributeTable)iGeoFeaturelayers[i]).getAttributeTable().rowCount(queryFilter); cursor = iGeoFeaturelayers[i].search (queryFilter, false); feature = cursor.nextFeature(); //layerUnions[i] = feature.getShape(); if (feature != null) { layerUnion = feature.getShape(); } else { layerUnion = null; } // Total up lengths for each feature, and union them so the convex hulls can be made. while (feature != null) { lengths[i] = lengths[i] + ((Polyline)feature.getShape()).getLength(); try { layerUnion = ((Polyline)layerUnion).union(feature.getShape()); countDone++; } catch (AutomationException ae) { countNotDone++; } feature = cursor.nextFeature(); } System.out.println("skipped " + countNotDone); System.out.println("done " + countDone); System.out.println("a"); Cleaner.release(cursor); System.out.println("b"); IGeometry hull = null; if (layerUnion != null) { hull = ((Polyline)layerUnion).convexHull(); } else { hull = null; } if (hull != null) { if (hullUnion == null) { hullUnion = hull; } else { IGeometry union = null; try { if (hullUnion instanceof Polygon) { union = ((Polygon)hullUnion).union(hull); hullUnion = ((Polygon)union).convexHull(); } } catch (AutomationException e) { System.out.println("skipping " + names[i]); } } System.out.println("c"); Polygon hp = null; if (hull instanceof Polygon) { hp = (Polygon) hull; hullAreas[i] = hp.getArea(); } else { hullAreas[i] = 0; } System.out.println("d"); } else { hullAreas[i] = 0; } } double totalLengths = 0; for (int i = 0; i < lengths.length; i++) { totalLengths = totalLengths + lengths[i]; } double totalHullArea = 0; if ((hullUnion != null) && (hullUnion instanceof Polygon)) { totalHullArea = ((Polygon)hullUnion).getArea(); } else { totalHullArea = -1; } // Print results. /* if (debug) { for (int i = 0; i < iGeoFeaturelayers.length; i++) { System.out.print(names[i] + " n = " + n[i]); System.out.print(" length = " + lengths[i] + " rectArea = " + areas[i] + " totalArea = " + maxArea); System.out.print(" hullArea = " + ((Polygon)hullUnions[i]).getArea() + " wholeHullArea = " + ((Polygon)layersUnion).getArea()); System.out.print(" rectDensity = " + lengths[i] / areas[i]); System.out.print(" rectDensity for whole area = " + lengths[i] / maxArea); System.out.print(" hullDensity = " + lengths[i] / ((Polygon)hullUnions[i]).getArea()); System.out.print(" hullDensity for whole area = " + lengths[i] / totalHullArea); System.out.println("rectDensity for all types = " + totalLengths / maxArea + " hullDensity for all = " + totalLengths / totalHullArea); } } */ // Write results File directory = ChangeOutputDirectory.getInstance().getDirectory(); if (debug) System.out.println("Proposed Directory = " + directory); Date now = new Date(System.currentTimeMillis()); String dateString = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(now); String title = ((IDocument)mxDoc).getTitle(); dateString = dateString.replaceAll("[ :]","-"); // Remove some of the date format not allowed in filenames. title = title.replaceAll("[\\/:*?<>|]", ""); // Remove any illegal filename characters from map name. String filepath = directory.getAbsolutePath() + File.separator + title.substring(0,title.lastIndexOf(".")) + "-LineTypeDensities-" + dateString + ".csv"; if (debug) System.out.println("Directory = " + filepath); FileWriter fw = new FileWriter(new File (filepath)); BufferedWriter bw = new BufferedWriter(fw); bw.write("Type,n,totalLengthAll,topologyLength,rectArea,totalRectArea,hullArea,totalHullArea,rectDensity,rectDensityWholeArea,hullDensity,hullDensityWholeArea,rectDensityAll,hullDensityAll"); bw.newLine(); for (int i = 0; i < iGeoFeaturelayers.length; i++) { bw.write(names[i] + "," + n[i] + ","); bw.write(lengths[i] + "," + topoLength + "," + areas[i] + "," + maxArea + "," + hullAreas[i] + "," + totalHullArea + ","); bw.write(lengths[i] / areas[i] + "," + lengths[i] / maxArea + ","); bw.write(lengths[i] / hullAreas[i] + "," + lengths[i] / totalHullArea + ","); bw.write(totalLengths / maxArea + "," + totalLengths / totalHullArea); bw.newLine(); } bw.flush(); bw.close(); dialog.dispose(); } catch (AutomationException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }