arcgissamples\geoprocessing\customtool\FindLocations.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.IOException; import java.io.File; import com.esri.arcgis.interop.AutomationException; import com.esri.arcgis.system.AoInitialize; import com.esri.arcgis.system.IArray; import com.esri.arcgis.system.IName; import com.esri.arcgis.system.ITrackCancel; import com.esri.arcgis.system.Array; import com.esri.arcgis.system.esriLicenseProductCode; import com.esri.arcgis.system.esriLicenseStatus; import com.esri.arcgis.geometry.esriGeometryType; import com.esri.arcgis.geoprocessing.*; import com.esri.arcgis.datasourcesfile.*; import com.esri.arcgis.geodatabase.*; import com.esri.arcgis.system.*; public class FindLocations extends BaseGeoprocessingTool { private String toolName = "FindLocations"; private String displayName = "Java Location Finder Tool"; private String metadataFileName = "FindLocations.xml"; private GeoProcessor gp = null; private String relevantFCName = null; private FeatureClass relevantFeaturesFC = null; private static String outputFolder = null; IGPMessages messages = null; private static String sw = null; public FindLocations() { try { // initialize GeoProcessor gp = new GeoProcessor(); gp.setOverwriteOutput(true); relevantFCName = "RelevantFeatures.shp"; } catch (Exception e) { e.printStackTrace(); } } /** * 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 FLFunctionFactory().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(); // input features param1 GPCompositeDataType compositeDataType = new GPCompositeDataType(); compositeDataType.addDataType(new GPFeatureLayerType()); compositeDataType.addDataType(new GPFeatureRecordSetLayerType()); GPParameter parameter1 = new GPParameter(); parameter1.setName("in_streetfeature"); parameter1.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput); parameter1.setDisplayName("Enter the Streets feature class"); parameter1.setParameterType(esriGPParameterType.esriGPParameterTypeRequired); parameter1.setDataTypeByRef(compositeDataType); parameter1.setValueByRef(new GPFeatureLayer()); parameters.add(parameter1); // input Food location query data param3 GPParameter parameter2 = new GPParameter(); parameter2.setName("in_streetquery"); parameter2.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput); parameter2.setDisplayName("Enter or build Query to retrieve 1 or more street feature(s)"); parameter2.setParameterType(esriGPParameterType.esriGPParameterTypeRequired); parameter2.setDataTypeByRef(new GPSQLExpressionType()); parameter2.setValueByRef(new GPSQLExpression()); parameter2.addDependency("in_streetfeature"); parameters.add(parameter2); // input distance param2 GPParameter parameter3 = new GPParameter(); parameter3.setName("in_distance"); parameter3.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput); parameter3.setDisplayName("Enter Search Distance (in Feet) from street"); parameter3.setParameterType(esriGPParameterType.esriGPParameterTypeRequired); parameter3.setDataTypeByRef(new GPLinearUnitType()); parameter3.setValueByRef(new GPLinearUnit()); parameters.add(parameter3); // input Food location data param3 GPParameter parameter4 = new GPParameter(); parameter4.setName("in_foodlocationfeatures"); parameter4.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput); parameter4.setDisplayName("Enter the Food Locations feature class"); parameter4.setParameterType(esriGPParameterType.esriGPParameterTypeRequired); parameter4.setDataTypeByRef(compositeDataType); parameter4.setValueByRef(new GPFeatureLayer()); parameters.add(parameter4); // input food type param4 GPParameter parameter5 = new GPParameter(); parameter5.setName("in_foodtype"); parameter5.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput); parameter5.setDisplayName("Select type of food"); parameter5.setParameterType(esriGPParameterType.esriGPParameterTypeRequired); parameter5.setDataTypeByRef(new GPStringType()); parameter5.setValueByRef(new GPString()); GPCodedValueDomain cvDomain = new GPCodedValueDomain(); cvDomain.addStringCode("fastfood", "Fast Food"); cvDomain.addStringCode("lunchdinner", "Lunch or Dinner"); cvDomain.addStringCode("alltypes", "All Types"); parameter5.setDomainByRef(cvDomain); parameters.add(parameter5); // input location type param5 IGPDataType stringType = new GPStringType(); GPValueTable valueTable = new GPValueTable(); valueTable.addDataType(stringType); GPValueTableType valueTableType = new GPValueTableType(); valueTableType.addDataType(stringType, "Type of Food Joint", 260, null); GPParameter parameter6 = new GPParameter(); parameter6.setName("in_locationtypes"); parameter6.setDirection(esriGPParameterDirection.esriGPParameterDirectionInput); parameter6.setDisplayName("These are the types of locations available."); parameter6.setParameterType(esriGPParameterType.esriGPParameterTypeRequired); parameter6.setDataTypeByRef(valueTableType); parameter6.setValueByRef(valueTable); parameters.add(parameter6); // output locations param6 GPParameter parameter7 = new GPParameter(); parameter7.setName("out_locations"); parameter7.setDirection(esriGPParameterDirection.esriGPParameterDirectionOutput); parameter7.setDisplayName("Output Locations Parameter"); parameter7.setParameterType(esriGPParameterType.esriGPParameterTypeRequired); parameter7.setDataTypeByRef(new DEFeatureClassType()); parameter7.setValueByRef(new DEFeatureClass()); parameters.add(parameter7); 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 Input Feature Set parameter IGPParameter inputFeatureSetParameter = (IGPParameter) paramvalues.getElement(0); IGPValue inputFeatureSetValue = gpUtilities.unpackGPValue(inputFeatureSetParameter); String outputFC = outputFolder + File.separator + "findLocationsOutput.shp"; // Retrieve Food Type Parameter IGPParameter foodTypeParameter = (IGPParameter) paramvalues.getElement(4); IGPValue foodTypeValue = gpUtilities.unpackGPValue(foodTypeParameter); String foodType = foodTypeValue.getAsText(); // Retrieve Location Type Parameter IGPParameter locationTypeParameter = (IGPParameter) paramvalues.getElement(5); GPValueTable locationTypeValue = (GPValueTable) gpUtilities.unpackGPValue(locationTypeParameter); /* * If food type is "Fast Food", use Gas Stations and Pizza Parlours only. * If food type is "Lunch or Dinner", use Restaurants and Pizza Parlours * only. If food type is "All", use Gas Stations, Pizza Parlours, and * Restaurants. */ // create Pizza Parlours array GPString pizzaValue = new GPString(); pizzaValue.setValue("Pizza Parlours"); Array pizzaArray = new Array(); pizzaArray.add(pizzaValue); // create Gas Stations array GPString gasStationValue = new GPString(); gasStationValue.setValue("Gas Stations"); Array gasArray = new Array(); gasArray.add(gasStationValue); // create Restaurants array GPString restaurantValue = new GPString(); restaurantValue.setValue("Restaurants"); Array restaurantArray = new Array(); restaurantArray.add(restaurantValue); // assign a value to Location Type param based on food type's value if (foodType.equalsIgnoreCase("fastfood")) { emptyValueTable(locationTypeValue); locationTypeValue.insertRecord(0, pizzaArray); locationTypeValue.insertRecord(1, gasArray); } else if (foodType.equalsIgnoreCase("lunchdinner")) { emptyValueTable(locationTypeValue); locationTypeValue.insertRecord(0, restaurantArray); } else if (foodType.equalsIgnoreCase("alltypes")) { emptyValueTable(locationTypeValue); locationTypeValue.insertRecord(0, restaurantArray); locationTypeValue.insertRecord(1, pizzaArray); locationTypeValue.insertRecord(2, gasArray); } // Retrieve Output Parameter //String outputFC = "findLocationsOutput.shp"; IGPParameter outputLocationsParameter = (IGPParameter) paramvalues.getElement(6); DEFeatureClass outputLocationsValue = (DEFeatureClass) gpUtilities.unpackGPValue(outputLocationsParameter); if (!inputFeatureSetValue.isEmpty()) { if (outputLocationsValue.isEmpty()) { outputLocationsValue.setAsText(outputFC); } gpUtilities.packGPValue(outputLocationsValue, outputLocationsParameter); } } catch (Exception e) { e.printStackTrace(); } } /** * Called after returning from the internal validation routine. You can * examine th e 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 gpMessages) throws IOException, AutomationException { try { messages = gpMessages; // get Input Feature Set parameter messages.addMessage("Reading Input Feature Set parameter..."); IGPParameter inputFeatureSetParameter = (IGPParameter) paramvalues.getElement(0); IGPValue inputFeatureSetValue = gpUtilities.unpackGPValue(inputFeatureSetParameter); String inputFeatureSet = inputFeatureSetValue.getAsText(); messages.addMessage("Input Feature Set: " + inputFeatureSet); // get Street query parameter messages.addMessage("Reading Query parameter..."); IGPParameter inputQueryParameter = (IGPParameter) paramvalues.getElement(1); IGPValue inputQueryValue = gpUtilities.unpackGPValue(inputQueryParameter); String inputQuery = inputQueryValue.getAsText(); messages.addMessage("Input Query: " + inputQuery); IFeatureClass[] streetIFC = new IFeatureClass[1]; gpUtilities.decodeFeatureLayer(inputFeatureSetValue, streetIFC, null); // get Distance parameter messages.addMessage("Reading Distance parameter..."); IGPParameter distanceParameter = (IGPParameter) paramvalues.getElement(2); IGPValue distanceValue = gpUtilities.unpackGPValue(distanceParameter); String distance = distanceValue.getAsText(); messages.addMessage("Distance: " + distance); // get Input Food Locations Feature Set parameter messages.addMessage("Reading Input Food locations Feature Set parameter..."); IGPParameter inputLocationsParameter = (IGPParameter) paramvalues.getElement(3); IGPValue inputLocationsValue = gpUtilities.unpackGPValue(inputLocationsParameter); String inputLocations = inputLocationsValue.getAsText(); messages.addMessage("Input Food locations Set: " + inputLocations); // get Food Type Parameter messages.addMessage("Reading Food types param..."); IGPParameter foodTypeParameter = (IGPParameter) paramvalues.getElement(4); IGPValue foodTypeValue = gpUtilities.unpackGPValue(foodTypeParameter); String foodType = foodTypeValue.getAsText(); messages.addMessage("Food Type: " + foodType); // set Location Type Parameter messages.addMessage("Reading Location types param..."); IGPParameter locationTypeParameter = (IGPParameter) paramvalues.getElement(5); GPValueTable locationTypeValue = (GPValueTable) gpUtilities.unpackGPValue(locationTypeParameter); String locationType = locationTypeValue.getAsText(); messages.addMessage("locationType: " + locationType); // Output Parameter messages.addMessage("Reading Output Parameter..."); IGPParameter outputParameter = (IGPParameter) paramvalues.getElement(6); IGPValue outputValue = gpUtilities.unpackGPValue(outputParameter); if (gpUtilities.exists(outputValue)) { messages.addMessage("Output already exists. Overwriting it..."); gpUtilities.delete(outputValue); } String output = outputValue.getAsText(); messages.addMessage("Output: " + output); // get features from Streets FC, based on user specified query String streetsFCName = "queryStreets.shp"; FeatureClass streetsFC = createShapefile(streetsFCName, sw, esriGeometryType.esriGeometryPolyline, new FeatureClass(streetIFC[0])); // copy features based on query copyFeatures(new FeatureClass(streetIFC[0]), streetsFC, inputQuery); // buffer the features in streets fc String bufferOutput = outputFolder + File.separator + "bufferOutput.shp"; messages.addMessage("Buffering ... input feature set at " + bufferOutput + "."); buffer(streetsFC, bufferOutput, distance); messages.addMessage("Done."); // retrieve relevant features from food locations FC messages.addMessage("Extracting relevant features based on Location Type List..."); IFeatureClass[] locationsFC = new IFeatureClass[1]; gpUtilities.decodeFeatureLayer(inputLocationsValue, locationsFC, null); // obtain relevant Features based on user specified food locations and // return as GPFeatureRecordSetLayer GPFeatureRecordSetLayer relevantFeatures = extractRelevantFeatures(outputFolder, locationType, locationsFC[0], inputLocations); String intersectInput = relevantFeatures.getAsText(); messages.addMessage("Done."); // Intersect GPFeatureRecordSetLayer with buffer output messages.addMessage("Intersecting buffer output with relevant features..."); intersect(bufferOutput + ";" + intersectInput, output); messages.addMessage("Done."); // delete relevant features messages.addMessage("Cleaning up intermediate data..."); relevantFeaturesFC.delete(); messages.addMessage("Done."); // assign output fc to output param messages.addMessage("Assigning output..."); gpUtilities.packGPValue(outputValue, outputParameter); messages.addMessage("Done."); messages.addMessage("Proximity tool execution complete."); } catch (Exception e) { e.printStackTrace(); } } /** * Returns metadata file */ public String getMetadataFile() throws IOException, AutomationException { return metadataFileName; } /** * Returns status of license. If you don't care about which license levels * your tool should work at, just return true */ 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. return status; } /************************************************************************************* * Util methods *************************************************************************************/ /** * Empties specified value table * * @param table * @throws Exception */ private void emptyValueTable(GPValueTable table) { try { int recordCount = table.getRecordCount(); if (recordCount > 0) { for (int i = 0; i < recordCount; i++) { table.removeRecord(i); } } } catch (Exception e) { System.out.println(e.getMessage()); } } /** * Extracts relevant features based on location type and returns as * GPFeatureRecordSetLayer * * @param workspace * @param locationType * @param fc * @param templateFC * @return GPFeatureRecordSetLayer * @throws Exception */ private GPFeatureRecordSetLayer extractRelevantFeatures(String workspace, String locationType, IFeatureClass fc, String templateFC) throws Exception { String[] list = locationType.split(";"); int[] numList = new int[list.length]; for (int i = 0; i < numList.length; i++) { if (list[i].equalsIgnoreCase("'Gas Stations'")) { numList[i] = 1; } else if (list[i].equalsIgnoreCase("'Grocery Stores'")) { numList[i] = 2; } else if (list[i].equalsIgnoreCase("'Pizza Parlours'")) { numList[i] = 3; } else if (list[i].equalsIgnoreCase("Restaurants")) { numList[i] = 4; } } // create query base on types selected by user String query = "\"Type\" = "; for (int j = 0; j < numList.length; j++) { if (j > 0) { query += " OR \"Type\" = "; } query += numList[j]; } messages.addMessage("Query is: " + query); // create an empty fc to hold user's selections messages.addMessage("Creating Relevant Features FC..."); relevantFeaturesFC = createShapefile(relevantFCName, workspace, esriGeometryType.esriGeometryPoint, new FeatureClass(gpUtilities.openFeatureClassFromString(templateFC))); messages.addMessage("Done."); // copy only relevant features into it from PointsOfInterest layer FeatureClass lineFC = new FeatureClass(fc); copyFeatures(lineFC, relevantFeaturesFC, query); // create a recordset from the FC that holds user's selections RecordSet rs = new RecordSet(); rs.setSourceTable(relevantFeaturesFC, null); // convert rs into gpfrs GPFeatureRecordSetLayer gpfrs = new GPFeatureRecordSetLayer(); gpfrs.setRecordSetByRef(rs); return gpfrs; } /** * Copies features from source FC to dest FC using query * * @param sourceFC * @param destFC * @param query * @throws Exception */ private void copyFeatures(FeatureClass sourceFC, FeatureClass destFC, String query) throws Exception { FeatureCursor insertCursor = new FeatureCursor(destFC.insert(true)); IFeatureBuffer featureBuffer = destFC.createFeatureBuffer(); // Loop through all the features in FeatureClassIn QueryFilter queryFilter = new QueryFilter(); queryFilter.setWhereClause(query); IFeatureCursor featureCursor = sourceFC.search(queryFilter, true); IFeature feature = featureCursor.nextFeature(); while (feature != null) { // Add the original feature's geometry to the feature buffer. featureBuffer.setShapeByRef(feature.getShape()); // Add all the original feature's fields to the feature buffer. try { // Copy the attributes of the orig feature the new feature IFields fieldsNew = featureBuffer.getFields(); IFields fields = feature.getFields(); for (int i = 0; i < fields.getFieldCount(); i++) { IField field = fields.getField(i); // check to make sure we are not trying to work with the geometry or // OID field if ((field.getType() != esriFieldType.esriFieldTypeGeometry) && (field.getType() != esriFieldType.esriFieldTypeOID)) { int intFieldIndex = fieldsNew.findField(field.getName()); if (intFieldIndex != -1) { // set the value of the new field equal to the old field value featureBuffer.setValue(intFieldIndex, feature.getValue(i)); } } } } catch (Exception e) { messages.addAbort("Threw an exception while adding fields " + e.getMessage()); e.printStackTrace(); } // Insert the feature into the cursor. insertCursor.insertFeature(featureBuffer); // Get Next Feature feature = featureCursor.nextFeature(); } // Flush the cursor insertCursor.flush(); } /** * Buffers specified input FC * * @param input * @param output * @param distance * @throws Exception */ private void buffer(IFeatureClass input, String output, String distance) throws Exception { VarArray bufferParamArray = new VarArray(); bufferParamArray.add(input); bufferParamArray.add(output); bufferParamArray.add(distance); bufferParamArray.add("FULL"); bufferParamArray.add("ROUND"); bufferParamArray.add("NONE"); bufferParamArray.add("#"); messages.addMessage("\nBuffering Input Feature..."); gp.execute("Buffer_analysis", bufferParamArray, null); messages.addMessage("Done."); } /** * Intersects specified FCs * * @param input * @param output * @throws Exception */ private void intersect(String input, String output) throws Exception { VarArray intersectParamArray = new VarArray(); intersectParamArray.add(input); intersectParamArray.add(output); intersectParamArray.add("ALL"); intersectParamArray.add("#"); intersectParamArray.add("POINT"); messages.addMessage("\nIntersecting Buffer and Relevant Points of Interest..."); gp.execute("Intersect_analysis", intersectParamArray, null); messages.addMessage("Done."); } /** * 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 createShapefile(String name, String path, int geometryType, FeatureClass templateFC) throws Exception { // create a FC IWorkspaceFactory factory = new ShapefileWorkspaceFactory(); Workspace ws = (Workspace) factory.openFromFile(path, 0); // create and return feature class return new FeatureClass(ws.createFeatureClass(name, templateFC.getFields(), null, null, esriFeatureType.esriFTSimple, "shape", "default")); } }