Find Intersections custom Java geoprocessing tool
arcgissamples\geoprocessing\customtool\FindIntersections.java
/* Copyright 2010 ESRI
* 
* All rights reserved under the copyright laws of the United States
* and applicable international laws, treaties, and conventions.
* 
* You may freely redistribute and use this sample code, with or
* without modification, provided you include the original copyright
* notice and use restrictions.
* 
* See the use restrictions.
* 
*/
package arcgissamples.geoprocessing.customtool;

import java.io.File;
import java.io.IOException;

import com.esri.arcgis.datasourcesfile.ShapefileWorkspaceFactory;
import com.esri.arcgis.geodatabase.DEFeatureClass;
import com.esri.arcgis.geodatabase.DEFeatureClassType;
import com.esri.arcgis.geodatabase.DENetworkDataset;
import com.esri.arcgis.geodatabase.DENetworkDatasetType;
import com.esri.arcgis.geodatabase.EnumNetworkElement;
import com.esri.arcgis.geodatabase.FeatureClass;
import com.esri.arcgis.geodatabase.FeatureCursor;
import com.esri.arcgis.geodatabase.Field;
import com.esri.arcgis.geodatabase.Fields;
import com.esri.arcgis.geodatabase.GeometryDef;
import com.esri.arcgis.geodatabase.IFeature;
import com.esri.arcgis.geodatabase.IFeatureBuffer;
import com.esri.arcgis.geodatabase.IFeatureClass;
import com.esri.arcgis.geodatabase.IGPMessages;
import com.esri.arcgis.geodatabase.IGPValue;
import com.esri.arcgis.geodatabase.INetworkElement;
import com.esri.arcgis.geodatabase.INetworkJunction;
import com.esri.arcgis.geodatabase.IWorkspaceFactory;
import com.esri.arcgis.geodatabase.NetworkDataset;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriFeatureType;
import com.esri.arcgis.geodatabase.esriFieldType;
import com.esri.arcgis.geodatabase.esriGPMessageSeverity;
import com.esri.arcgis.geodatabase.esriNetworkElementType;
import com.esri.arcgis.geometry.ISpatialReference;
import com.esri.arcgis.geometry.esriGeometryType;
import com.esri.arcgis.geoprocessing.BaseGeoprocessingTool;
import com.esri.arcgis.geoprocessing.GPCompositeDataType;
import com.esri.arcgis.geoprocessing.GPEnvironment;
import com.esri.arcgis.geoprocessing.GPNetworkDatasetLayerType;
import com.esri.arcgis.geoprocessing.GPParameter;
import com.esri.arcgis.geoprocessing.IGPEnvironmentManager;
import com.esri.arcgis.geoprocessing.IGPParameter;
import com.esri.arcgis.geoprocessing.esriGPParameterDirection;
import com.esri.arcgis.geoprocessing.esriGPParameterType;
import com.esri.arcgis.interop.AutomationException;
import com.esri.arcgis.system.AoInitialize;
import com.esri.arcgis.system.Array;
import com.esri.arcgis.system.IArray;
import com.esri.arcgis.system.IName;
import com.esri.arcgis.system.ITrackCancel;
import com.esri.arcgis.system.esriLicenseExtensionCode;
import com.esri.arcgis.system.esriLicenseProductCode;
import com.esri.arcgis.system.esriLicenseStatus;

public class FindIntersections extends BaseGeoprocessingTool {
  
  private static final long serialVersionUID = 1L;
  private String toolName = "FindIntersections";
  private String displayName = "Java Find Intersections";
  private String metadataFileName = "FindIntersections.xml";
  private static String outputFolder = null;

  public FindIntersections() {

  }

  /**
   * Returns name of the tool This name appears when executing the tool at the
   * command line or in scripting. This name should be unique to each toolbox
   * and must not contain spaces.
   */
  public String getName() throws IOException, AutomationException {
    return toolName;
  }

  /**
   * Returns Display Name of the tool, as seen in ArcToolbox.
   */
  public String getDisplayName() throws IOException, AutomationException {
    return displayName;
  }

  /**
   * Returns the full name of the tool
   */
  public IName getFullName() throws IOException, AutomationException {
    return (IName) new FIFunctionFactory().getFunctionName(toolName);
  }

  /**
   * Returns an array of paramInfo This is the location where the parameters to
   * the Function Tool are defined. This property returns an IArray of parameter
   * objects (IGPParameter). These objects define the characteristics of the
   * input and output parameters.
   */
  public IArray getParameterInfo() throws IOException, AutomationException {
    IArray parameters = new Array();

    // Using a composite data type for parameter <name of parameter>;
    GPCompositeDataType compositeDataType1 = new GPCompositeDataType();
    compositeDataType1.addDataType(new DENetworkDatasetType());
    compositeDataType1.addDataType(new GPNetworkDatasetLayerType());

    GPParameter parameter1 = new GPParameter();
    parameter1.setName("in_nds");
    parameter1.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput);
    parameter1.setDisplayName("Input Network Dataset Parameter");
    parameter1.setParameterType(esriGPParameterType.esriGPParameterTypeRequired);
    parameter1.setDataTypeByRef(compositeDataType1);
    parameter1.setValueByRef(new DENetworkDataset());
    parameters.add(parameter1);

    GPParameter parameter2 = new GPParameter();
    parameter2.setName("out_intersections");
    parameter2.setDirection(esriGPParameterDirection.esriGPParameterDirectionOutput);
    parameter2.setDisplayName("Output Intersections Parameter");
    parameter2.setParameterType(esriGPParameterType.esriGPParameterTypeRequired);
    parameter2.setDataTypeByRef(new DEFeatureClassType());
    parameter2.setValueByRef(new DEFeatureClass());
    parameters.add(parameter2);

    return parameters;
  }

  /**
   * Called each time the user changes a parameter in the tool dialog or Command
   * Line. This updates the output data of the tool, which extremely useful for
   * building models. After returning from UpdateParameters(), the GP framework
   * calls its internal validation routine to check that a given set of
   * parameter values are of the appropriate number, DataType, and value.
   */
  public void updateParameters(IArray paramvalues, IGPEnvironmentManager envMgr) {
    try {
      // retrieve the environment value for scratch workspace
      GPEnvironment scratchWorkspaceEnv = (GPEnvironment) envMgr.findEnvironment("scratchWorkspace");
      IGPValue swValue = scratchWorkspaceEnv.getValue();
      if(!swValue.isEmpty())
      {  
        outputFolder = swValue.getAsText();
      }
      
      // Retrieve parameter 1
      IGPParameter inputNDParameter = (IGPParameter) paramvalues.getElement(0);
      IGPValue inputNDParameterValue = gpUtilities.unpackGPValue(inputNDParameter);

      // Retrieve parameter 2
      IGPParameter outputFCParameter = (IGPParameter) paramvalues.getElement(1);
      IGPValue outputFCParameterValue = gpUtilities.unpackGPValue(outputFCParameter);

      if (!inputNDParameterValue.isEmpty()) {
        String outputFCName = outputFolder + File.separator + "intersections.shp";
        outputFCParameterValue.setAsText(outputFCName);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Called after returning from the internal validation routine. You can
   * examine the messages created from internal validation and change them if
   * desired.
   */
  public void updateMessages(IArray paramvalues, IGPEnvironmentManager envMgr, IGPMessages gpMessages) {
    try {
      if (gpMessages.getMaxSeverity() == esriGPMessageSeverity.esriGPMessageSeverityError) {
        for (int i = 0; i < gpMessages.getCount(); i++) {
          System.out.println(gpMessages.getMessage(i).getDescription());
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Executes the tool
   */
  public void execute(IArray paramvalues, ITrackCancel trackcancel, IGPEnvironmentManager envMgr, IGPMessages messages)
    throws IOException, AutomationException {
    // Retrieve parameter 1
    IGPParameter inputNDParameter = (IGPParameter) paramvalues.getElement(0);
    IGPValue inputNDParameterValue = gpUtilities.unpackGPValue(inputNDParameter);

    String nd = inputNDParameterValue.getAsText();
    String ndPath = nd.substring(0, nd.lastIndexOf(File.separator));

    messages.addMessage("Reading input Network Dataset...");
    NetworkDataset nds = new NetworkDataset(gpUtilities.openDataset(inputNDParameterValue));

    // Create a new point feature class and get a reference to it.
    messages.addMessage("Creating Point feature class to hold intersections...");
    FeatureClass intersectionsFC = createIntersectionsFC("intersections", outputFolder, nds.getSpatialReference());

    // Get the field index for the fields that will be used to set their values
    int indexSourceID = intersectionsFC.findField("SourceID");
    int indexSourceOID = intersectionsFC.findField("SourceOID");
    int indexSourceName = intersectionsFC.findField("SourceName");

    // Open an insert cursor
    FeatureCursor featureCursor = new FeatureCursor(intersectionsFC.insert(true));
    IFeatureBuffer junctionFeature = intersectionsFC.createFeatureBuffer();

    // Get the INetworkQuery and figure out number of edges coincident at every
    // junction
    messages.addMessage("Reading intersections from input Network Dataset...");
    EnumNetworkElement allJunctions = new EnumNetworkElement(nds.getElements(esriNetworkElementType.esriNETJunction));
    allJunctions.reset();

    INetworkElement aJunction = allJunctions.next();

    INetworkJunction netJunction = null;
    while (aJunction != null) {
      netJunction = (INetworkJunction) aJunction;
      if (netJunction.getEdgeCount() >= 3) {
        int srcID = netJunction.getSourceID();
        int srcOID = netJunction.getOID();
        String srcName = nds.getSourceByID(srcID).getName();

        // Get the workspace for the NDS
        // Open the network source feature class
        Workspace ws = new Workspace(nds.getWorkspace());
        IFeatureClass srcFC = ws.openFeatureClass(srcName);
        // Get the geometry for the junction
        IFeature srcFeature = srcFC.getFeature(srcOID);

        // Insert the junction feature into the new feature class.
        junctionFeature.setShapeByRef(srcFeature.getShape());
        junctionFeature.setValue(indexSourceID, srcID);
        junctionFeature.setValue(indexSourceOID, srcOID);
        junctionFeature.setValue(indexSourceName, srcName);
        featureCursor.insertFeature(junctionFeature);
      }

      aJunction = allJunctions.next();
    }

    // Clear the buffer
    featureCursor.flush();
    messages.addMessage("Done reading intersections and updating Point feature class...");

    // Retrieve parameter 2
    IGPParameter outputFCParameter = (IGPParameter) paramvalues.getElement(1);
    DEFeatureClass outputFCParameterValue = (DEFeatureClass) gpUtilities.unpackGPValue(outputFCParameter);
    outputFCParameterValue.setAsText(intersectionsFC.getName());
    messages.addMessage("Packing output param...");
    gpUtilities.packGPValue(outputFCParameterValue, outputFCParameter);
    messages.addMessage("Done.");
  }

  /**
   * Returns metadata file
   */
  public String getMetadataFile() throws IOException, AutomationException {
    return metadataFileName;
  }

  /**
   * Returns status of license
   */
  public boolean isLicensed() throws IOException, AutomationException {
    AoInitialize ao = new AoInitialize();
    int available = esriLicenseStatus.esriLicenseAvailable;
    boolean status = false;

    // check which license level is available
    if (ao.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeEngine) == available
      || ao.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeEngineGeoDB) == available
      || ao.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeArcView) == available) {
      status = true;
    }

    // check out extensions, if applicable.
    if (status) {
      ao.checkOutExtension(esriLicenseExtensionCode.esriLicenseExtensionCodeNetwork);
    }

    return status;
  }

  /***************************************************************************************************************
   * Util methods
   ****************************************************************************************************************/
  /**
   * Creates a Point FC based on specified name, path, geometryType and fields
   * from templateFC.
   * 
   * @param name
   * @param path
   * @param geometryType
   * @param templateFC
   * @return
   * @throws Exception
   */
  private FeatureClass createIntersectionsFC(String name, String path, ISpatialReference sr) {
    FeatureClass fc = null;
    try {
      IWorkspaceFactory factory = new ShapefileWorkspaceFactory();
      String wsPath = null;
      
      // create a FC
      Workspace ws = (Workspace) factory.openFromFile(wsPath, 0);

      Fields fields = new Fields();

      // iod field
      Field field = new Field();
      field.setName("OBJECTID");
      field.setType(esriFieldType.esriFieldTypeOID);
      fields.addField(field);
      field = null;

      // shape field
      field = new Field();

      GeometryDef gdef = new GeometryDef();
      gdef.setGeometryType(esriGeometryType.esriGeometryPoint);
      gdef.setHasM(false);
      gdef.setHasZ(false);
      gdef.setSpatialReferenceByRef(sr);

      field.setName("shape");
      field.setType(esriFieldType.esriFieldTypeGeometry);
      field.setGeometryDefByRef(gdef);
      fields.addField(field);
      field = null;

      // source id field
      field = new Field();
      field.setName("SourceID");
      field.setType(esriFieldType.esriFieldTypeSmallInteger);
      fields.addField(field);
      field = null;

      // source oid field
      field = new Field();
      field.setName("SourceOID");
      field.setType(esriFieldType.esriFieldTypeInteger);
      fields.addField(field);
      field = null;

      // source name field
      field = new Field();
      field.setName("SourceName");
      field.setType(esriFieldType.esriFieldTypeString);
      fields.addField(field);
      field = null;

      // create and return feature class
      fc = new FeatureClass(ws.createFeatureClass(name, fields, null, null, esriFeatureType.esriFTSimple, "shape",
        "default"));
    } catch (Exception e) {
      e.printStackTrace();
    }

    return fc;
  }
}