Library dependencies: System, SystemUI, Geometry, Display, Server, Output, Geodatabase, GISClient, DataSourcesFile, DataSourcesGDB, DataSourcesOleDB, DataSourcesRaster, GeodatabaseDistributed, Carto, NetworkAnalyst
The Location library contains objects that support geocoding and linear referencing.
The geocoding objects provide a framework for creating and managing address locators. Locator objects create geometry for non-spatial descriptions of locations. An AddressLocator is a particular type of locator that creates geometries for text representing addresses. AddressLocators can be stored either on disk or in a file, personal, or ArcSDE geodatabase. AddressLocators can also be used to create GeocodeServer objects that are served using ArcGIS Server. The geocoding objects also provide an extensible framework for creating new types of custom locators.
The geocoding objects can logically be grouped as follows:
A locator workspace contains locators and templates for creating new locators, or locator styles. The LocatorWorkspace abstract class allows you to create, store, and retrieve its locators and locator styles. There are three main types of workspaces for locators; each type corresponds to the storage mechanism to be used for its locators:
LocatorManager is an object that retrieves LocatorWorkspace objects from the file system, a geodatabase, or from an ArcGIS Server.
The following examples illustrate how to use a LocatorManager object to open various types of locator workspaces:
try {
ILocatorManager pLocatorMgr = new LocatorManager();
ILocatorWorkspace pLocatorWorkspace = pLocatorMgr.getLocatorWorkspaceFromPath("");
ILocalLocatorWorkspace pLocalLocatorWorkspace = new LocalLocatorWorkspace(pLocatorWorkspace);
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ILocatorManager pLocatorMgr = new LocatorManager();
ILocatorWorkspace pLocatorWorkspace = pLocatorMgr.getLocatorWorkspaceFromPath("C:/Esridata/SanFrancisco");
ILocalLocatorWorkspace pLocalLocatorWorkspace = new LocalLocatorWorkspace(pLocatorWorkspace);
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ILocatorManager pLocatorMgr = new LocatorManager();
IWorkspaceFactory pWksFactory = new FileGDBWorkspaceFactory();
IWorkspace pWks = pWksFactory.openFromFile("C:/Esridata/redlands.gdb", 0);
ILocatorWorkspace pLocatorWorkspace = pLocatorMgr.getLocatorWorkspace(pWks);
IDatabaseLocatorWorkspace pDatabaseLocatorWks = new DatabaseLocatorWorkspace(pLocatorWorkspace);
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ILocatorManager pLocatorMgr = new LocatorManager();
IWorkspaceFactory pWksFactory = new AccessWorkspaceFactory();
IWorkspace pWks = pWksFactory.openFromFile("C:/Esridata/redlands.mdb", 0);
ILocatorWorkspace pLocatorWorkspace = pLocatorMgr.getLocatorWorkspace(pWks);
IDatabaseLocatorWorkspace pDatabaseLocatorWks = new DatabaseLocatorWorkspace(pLocatorWorkspace);
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ILocatorManager pLocatorMgr = new LocatorManager();
IWorkspaceFactory pWksFactory = new SdeWorkspaceFactory();
IPropertySet pPropertySet = new PropertySet();
pPropertySet.setProperty("server", "mendota");
pPropertySet.setProperty("instance", "esri_sde");
pPropertySet.setProperty("database", "arcobjects");
pPropertySet.setProperty("user", "sde");
pPropertySet.setProperty("password", "sde");
pPropertySet.setProperty("version", "SDE.Default");
IWorkspace pWks = pWksFactory.open(pPropertySet, 0);
ILocatorWorkspace pLocatorWorkspace = pLocatorMgr.getLocatorWorkspace(pWks);
IDatabaseLocatorWorkspace pDatabaseLocatorWks = new DatabaseLocatorWorkspace(pLocatorWorkspace);
} catch (IOException ex) {
ex.printStackTrace();
}
try {
ILocatorManager pLocatorMgr = new LocatorManager();
ILocatorManager2 pLocatorMgr2 = new ILocatorManager2Proxy(pLocatorMgr);
IAGSServerConnectionFactory pWksFactory = new AGSServerConnectionFactory();
IPropertySet pPropertySet = new PropertySet();
pPropertySet.setProperty("machine", "xiancity");
IAGSServerConnection pWks = pWksFactory.open(pPropertySet, 0);
IAGSServerConnectionName pAGSName = new IAGSServerConnectionNameProxy(pWks.getFullName());
ILocatorWorkspace pLocatorWorkspace = pLocatorMgr2.getAGSLocatorWorkspace(pAGSName);
IDatabaseLocatorWorkspace pDatabaseLocatorWks = new DatabaseLocatorWorkspace(pLocatorWorkspace);
} catch (IOException ex) {
ex.printStackTrace();
}
Use the ILocatorWorkspace interface to manage the locators and locator styles in a locator workspace. You can use this interface to retrieve Locator and LocatorStyle objects from the locator workspace, as well as create, modify, and delete locators contained in the workspace. Use the GetLocator and GetLocatorStyle methods to retrieve locators and locator styles, respectively, by name from a LocatorWorkspace. The getLocators and getLocatorNames methods return enumerations of Locator and LocatorName objects, respectively, from the LocatorWorkspace, while the addLocator method adds a new locator to the workspace. Use rebuildLocator in ILocatorWorkspace2 to rebuild an address locator, particularly if the reference data was modified.
When using the methods on ILocatorWorkspace to refer to locators in an ArcSDE database and styles by name, you must specify a user name as a part of the Locator or LocatorStyle name. The names of ArcSDE locators and locator styles stored in a DatabaseLocatorWorkspace are prefixed with the name of the ArcSDE user who owns them.
Also note that some of the methods on ILocatorWorkspace require you to pass a locator category as a parameter. AddressLocators and AddressLocatorStyles used with ArcGIS have a category of "Address".
As mentioned earlier, a locator style is a template for creating a new locator. ArcGIS includes the implementation of the LocatorStyle abstract class on which to base new address locators: the ESRIFDOAddressLocatorStyle class is a template for creating locators that use Feature-Data-Object (FDO) reference data, such as shapefiles or geodatabase feature classes.
Each locator style class supports the interfaces required to specify the reference data and geocoding options for a Locator object. To create a new locator, a developer retrieves the LocatorStyle on which the new locator will be based from a LocatorWorkspace, specifies all of the properties on the style as required to define the new Locator, and stores the modified LocatorStyle in the LocatorWorkspace using the AddLocator method on the ILocatorWorkspace interface.
The following example illustrates how to use the LocatorWorkspace and Address Locator Style objects to create a new ArcSDE Locator:
try {
PropertySet pPropertySet = new PropertySet();
pPropertySet.setProperty("server", "mendota");
pPropertySet.setProperty("instance", "esri_sde");
pPropertySet.setProperty("database", "arcobjects");
pPropertySet.setProperty("user", "sde");
pPropertySet.setProperty("password", "sde");
pPropertySet.setProperty("version", "SDE.Default");
IWorkspaceFactory pWksFactory = new SdeWorkspaceFactory();
IWorkspace pWks = pWksFactory.open(pPropertySet,0);
IFeatureWorkspace pFeatureWorkspace = new IFeatureWorkspaceProxy(pWks);
//open the DatabaseLocatorWorkspace for the ArcSDE Workspace
ILocatorManager pLocatorManager = new LocatorManager();
ILocatorWorkspace pLocatorWorkspace = pLocatorManager.getLocatorWorkspace(pWks);
//get the US Streets LocatorStyle
ILocatorStyle pLocatorStyle = pLocatorWorkspace.getLocatorStyle("SDE.US Streets");
//open the feature workspace containing the reference data
//set the reference data on the new locator
IFeatureClass pFeatureClass = pFeatureWorkspace.openFeatureClass("sde.SDE.altanta_st");
com.esri.arcgis.geodatabase.IDataset pDataSet =
new com.esri.arcgis.geodatabase.IDatasetProxy(pFeatureClass);
IReferenceDataTables pReferenceDataTables = new IReferenceDataTablesProxy(pLocatorStyle);
IEnumReferenceDataTable pEnumReferenceDataTable = pReferenceDataTables.getTables();
pEnumReferenceDataTable.reset();
IReferenceDataTable pReferenceDataTable = pEnumReferenceDataTable.next();
IReferenceDataTableEdit pReferenceDataTableEdit = new IReferenceDataTableEditProxy(pReferenceDataTable);
pReferenceDataTableEdit.setNameByRef(new ITableNameProxy(pDataSet.getFullName()));
//store the locator if the reference data are properly specified
if (pReferenceDataTables.isHasEnoughInfo()){
ILocator pLocator = pLocatorWorkspace.addLocator("My New SDE Locator", new ILocatorProxy(pLocatorStyle), "", null);
}
} catch (IOException ex) {
ex.printStackTrace();
}
Locators and locator styles support the ISimpleStandardization interface, which can be used to standardize single addresses or tables and feature classes containing address information. The most common usage for this interface is to use the SimpleStandardizeTable method to standardize the address information in a feature class that will be used as reference data for a locator. It's a good idea to standardize the address information in reference data feature classes, so that address locators created using those reference data feature classes will standardize addresses in the same way that they are standardized in the reference data.
The InputTable parameter is a reference to the table or feature class containing the address information that you want to standardize.
The inputFieldsToConcatenate parameter is a comma-delimited string containing the names of the fields in the input dataset that, when concatenated, form an address that can be standardized. Note that this set of field names should include a numeric field that can represent a house number. When standardizing a dataset, you must first create the table or feature class that will contain the standardized records, and pass it to the simpleStandardizeTable method using the OutputTable parameter. At a minimum, the standardized dataset must contain an ObjectID field, and all of the standardization fields defined by the SimpleStandardizeFields property. It's also a good idea to create a Shape field in the output dataset (that is, create an output feature class) if you are standardizing the address information in a feature class, and to copy the shapes from the input feature class to the output feature class.
The outputFieldNames parameter is a comma-delimited string that contains the names of the standardization fields in the standardized dataset. The names of the fields in this string must be specified in the same order as the order of the Field objects in the Fields collection returned by the SimpleStandardizeFields property.
The fieldsToCopy parameter is a PropertySet defining which fields from the input dataset to copy to the output dataset. The names of the properties are the names of the fields in the output dataset, and the names of the properties are the names of the corresponding fields in the input dataset.
The following example illustrates how to standardize the address information in a feature class using the ISimpleStandardization interface:
try {
IPropertySet pPropertySet = new PropertySet();
pPropertySet.setProperty("server", "mendota");
pPropertySet.setProperty("instance", "esri_sde");
pPropertySet.setProperty("database", "arcobjects");
pPropertySet.setProperty("user", "sde");
pPropertySet.setProperty("password", "sde");
pPropertySet.setProperty("version", "SDE.Default");
IWorkspaceFactory pWksFactory = new SdeWorkspaceFactory();
IWorkspace pWks = pWksFactory.open(pPropertySet, 0);
IFeatureWorkspace pFeatureWorkspace = new IFeatureWorkspaceProxy(pWks);
// open the FeatureClass to standardize
IFeatureClass pFeatureClass = pFeatureWorkspace.openFeatureClass("arcobjects.SDE.Redlands_Street");
//get the LocatorStyle to use to standardize the FeatureClass
ILocatorManager pLocatorMgr = new LocatorManager();
ILocatorWorkspace pLocatorWks = pLocatorMgr.getLocatorWorkspace(new IWorkspaceProxy(pFeatureWorkspace));
ILocatorStyle pLocatorStyle = pLocatorWks.getLocatorStyle("SDE.US Streets");
//create the FeatureClass to which to write the standardization results
//the FeatureClass must contain an ObjectID Field,
//a shape Field, and the standardization Fields
IFieldsEdit pFieldsEdit = new Fields();
IFieldEdit pFieldEdit = new Field();
pFieldEdit.setName("ObjectID");
pFieldEdit.setType(com.esri.arcgis.geodatabase.esriFieldType.esriFieldTypeOID);
pFieldsEdit.addField(pFieldEdit);
int shpInt = pFeatureClass.getFields().findField(pFeatureClass.getShapeFieldName());
pFieldsEdit.addField(pFeatureClass.getFields().getField(shpInt));
ISimpleStandardization pSimpleStandardization = new ISimpleStandardizationProxy(pLocatorStyle);
IFields pStdFields = pSimpleStandardization.getSimpleStandardizeFields();
String[] stdFieldNames = new String[pStdFields.getFieldCount()-1];
for (int i = 0; i < pStdFields.getFieldCount()-1; i++) {
pFieldsEdit.addField(pStdFields.getField(i));
stdFieldNames[i] = pStdFields.getField(i).getName();
}
UID pUID = new UID();
pUID.setValue("esriGeodatabase.Feature");
IFeatureClass pStandardizedFeatureClass = pFeatureWorkspace.createFeatureClass("Redlands_Standardized",
pFieldsEdit, pUID, null, com.esri.arcgis.geodatabase.esriFeatureType.esriFTSimple, pFeatureClass.getShapeFieldName(), "");
//standardize the FeatureClass
IPropertySet pFieldsToCopy = new PropertySet();
pFieldsToCopy.setProperties(pFeatureClass.getShapeFieldName(), pFeatureClass.getShapeFieldName());
StringBuffer outputFieldNames = new StringBuffer();
for (int i = 0; i < stdFieldNames.length-1; i++) {
outputFieldNames.append(stdFieldNames[i]);
if (i < stdFieldNames.length-1 ) {
outputFieldNames.append(",");
}
}
pSimpleStandardization.simpleStandardizeTable(
new ITableProxy(pFeatureClass),
"OBJECTID,FULL_NAME", "",
new ITableProxy(pStandardizedFeatureClass), outputFieldNames.toString(), pFieldsToCopy, null);
} catch (IOException ex) {
ex.printStackTrace();
}
The reference data objects are used to specify and manage the reference data used by ESRIFDOAddressLocators.
Each ESRIFDOAddressLocator has a ReferenceDataTableEnumerator, which is an enumeration of ReferenceDataTable objects used by the locator.
Use the Tables property on the IReferenceDataTables interface on the locator or locator style to get the ReferenceDataTableEnumerator.
ReferenceDataTable objects are used to specify the FDO data sources that an ESRIFDOAddressLocator uses. The Name property on the IReferenceDataTable interface returns a reference to a TableName object that represents the FDO data source used as reference data by the locator. Each ReferenceDataTable has an enumeration of ReferenceDataIndex objects, which specify the geocoding indexes that an ESRIFDOAddressLocator uses to quickly find potential candidates for addresses. ReferenceDataTable objects also have enumerations of ReferenceDataField objects, which are used to specify which fields in the FDO data source contain address information.
The following example illustrates how to use the Locator Workspace, Address Locator Style, and Address Locator Reference Data objects to create a new ESRIFDOAddressLocator:
try {
//open the default LocalLocatorWorkspace to get the LocatorStyle
ILocatorManager pLocatorManager0 = new LocatorManager();
ILocatorManager2 pLocatorManager = new ILocatorManager2Proxy(pLocatorManager0);
ILocatorWorkspace pLocatorWorkspace = pLocatorManager.getLocatorWorkspaceFromPath("");
//get the LocatorStyle on which to base the new Locator
ILocatorStyle pLocatorStyle = pLocatorWorkspace.getLocatorStyle("US Streets with Zone");
//open the FeatureClass to use as reference data
IWorkspaceFactory pWorkspaceFactory = new AccessWorkspaceFactory();
IWorkspace pWorkspace = pWorkspaceFactory.openFromFile("C:/Esridata/redlands.mdb", 0);
IFeatureWorkspace pFeatureWorkspace = new IFeatureWorkspaceProxy(pWorkspace);
IFeatureClass pFeatureClass = pFeatureWorkspace.openFeatureClass("Streets");
com.esri.arcgis.geodatabase.IDataset pDataSet =
new com.esri.arcgis.geodatabase.IDatasetProxy(pFeatureClass);
//set the FeatureClass as the primary ReferenceDataTable for the Locator
IReferenceDataTables pReferenceDataTables =new IReferenceDataTablesProxy(pLocatorStyle);
IEnumReferenceDataTable pEnumReferenceDataTable = pReferenceDataTables.getTables();
pEnumReferenceDataTable.reset();
IReferenceDataTable pReferenceDataTable = pEnumReferenceDataTable.next();
IReferenceDataTableEdit pReferenceDataTableEdit = new IReferenceDataTableEditProxy(pReferenceDataTable);
pReferenceDataTableEdit.setNameByRef(new ITableNameProxy(pDataSet.getFullName()));
//store the new Locator in the same workspace of the reference data
if (pReferenceDataTables.isHasEnoughInfo()){
pLocatorWorkspace = pLocatorManager.getLocatorWorkspaceFromPath("D:/EsriData/redlands.mdb");
ILocator pLocator = pLocatorWorkspace.addLocator("New Redlands Locator",new ILocatorProxy(pLocatorStyle), "", null);
}
}catch (IOException ex) {
ex.printStackTrace();
}
Locator is an abstract class that performs the work of creating geometry for non-spatial descriptions of locations. All types of locators, including address locators and route locators, are subclasses of the Locator abstract class.
AddressLocator objects are a particular type of locator that creates geometry for text descriptions of addresses. This process is commonly called geocoding. Address locators do the work of finding candidates for addresses, geocoding tables of addresses, and finding single addresses.
ESRIAddressLocator objects are address locators that use the ESRI geocoding engine. The ESRIAddressLocator class is also an abstract class, and there are two main types of ESRIAddressLocator:
The IAddressGeocoding interface is the primary interface used to geocode addresses.
Use the MatchAddress method to geocode a single address. The address parameter is a PropertySet containing properties that represent the input address components used by the Locator.
Use the AddressFields property on the IAddressInputs interface to determine the input address components used by the Locator.
The MatchFields property returns a Fields collection in which the names of the Field objects correspond to the names of the properties in the PropertySet returned by the MatchAddress method.
The following example illustrates how to use the IAddressGeocoding interface to geocode a single address:
try {
IPropertySet pPropertySet = new PropertySet();
pPropertySet.setProperty("machine", "mendota");
IAGSServerConnectionFactory pAGSServerConnectionFactory = new AGSServerConnectionFactory();
IAGSServerConnection pAGSServerConnection = pAGSServerConnectionFactory.open(pPropertySet, 0);
//open the AGSLocatorWorkspace
ILocatorManager pLocatorManager = new LocatorManager();
ILocatorManager2 pLocatorManager2 = new ILocatorManager2Proxy(pLocatorManager);
IAGSServerConnectionName pAGSServerConnectionName = new IAGSServerConnectionNameProxy(pAGSServerConnection);
ILocatorWorkspace pLocatorWorkspace = pLocatorManager2.getAGSLocatorWorkspace(pAGSServerConnectionName);
// get a Locator from the AGSLocatorWorkspace
ILocator pLocator = pLocatorWorkspace.getLocator("USA Streets");
IAddressGeocoding pAddressGeocoding = new IAddressGeocodingProxy(pLocator);
PropertySet pAddressProperties = new PropertySet();
pAddressProperties.setProperty("Street", "380 New York St.");
pAddressProperties.setProperty("City", "Redlands");
pAddressProperties.setProperty("State", "CA");
pAddressProperties.setProperty("ZIP", "92373");
IPropertySet pMatchProperties = pAddressGeocoding.matchAddress(pAddressProperties);
// print the match properties
IFields pMatchFields = pAddressGeocoding.getMatchFields();
for (int i = 0; i < pMatchFields.getFieldCount(); i++) {
IField pMatchField = pMatchFields.getField(i);
if( pMatchField.getType() == com.esri.arcgis.geodatabase.esriFieldType.esriFieldTypeGeometry) {
IPoint pPoint = new IPointProxy(pMatchProperties.getProperty(pMatchField.getName()));
if (! pPoint.isEmpty()) {
System.out.println("X:"+pPoint.getX() +" Y:"+pPoint.getY());
}
} else {
System.out.println(pMatchField.getAliasName() +" " +
pMatchProperties.getProperty(pMatchField.getName()));
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
Use the matchTable method to geocode a table of addresses. The AddressTable parameter is a reference to the Table object that contains the addresses to geocode. The addressFieldNames parameter is a comma-delimited string containing the names of the fields in the address table that contain the address information. The names of the fields in this string must be specified in the same order as the order of the Field objects in the Fields collection returned by the AddressFields property on the IAddressInputs interface. When geocoding a table of addresses, you must first create the feature class that will contain the geocoded features, and pass it to the matchTable method using the outputFeatureClass parameter. At a minimum, the geocoded feature class must contain an ObjectID field, all of the match fields defined by the MatchFields property, as well as a copy of all of the fields from the address table that contain the address information. The outputFieldNames parameter is a comma-delimited string that contains the names of the match fields in the geocoded feature class. The names of the fields in this string must be specified in the same order as the order of the Field objects in the Field collection returned by the MatchFields property. The fieldsToCopy parameter is a PropertySet defining which fields from the address table to copy to the output feature class. The names of the properties are the names of the fields in the output feature class, and the names of the properties are the names of the corresponding fields in the address table.
Once you have used the matchTable method to geocode a table of addresses, use the attachLocator method on the ILocatorAttach2 on the LocatorWorkspace to attach a copy of the locator to the geocoded feature class.
The following example illustrates how to use the IAddressGeocoding interface to geocode a table of addresses in ArcSDE:
try{
PropertySet pPropertySet = new PropertySet();
pPropertySet.setProperty("server", "mendota");
pPropertySet.setProperty("instance", "esri_sde");
pPropertySet.setProperty("database", "arcobjects");
pPropertySet.setProperty("user", "sde");
pPropertySet.setProperty("password", "sde");
pPropertySet.setProperty("version", "SDE.Default");
IWorkspaceFactory pWorkspaceFactory = new SdeWorkspaceFactory();
IWorkspace pWorkspace = pWorkspaceFactory.open(pPropertySet, 0);
IFeatureWorkspace pFeatureWorkspace = new IFeatureWorkspaceProxy(pWorkspace);
//get a Locator from the DatabaseLocatorWorkspace
ILocatorManager pLocatorManager = new LocatorManager();
ILocatorWorkspace pLocatorWorkspace = pLocatorManager.getLocatorWorkspace(pWorkspace);
ILocator pLocator = pLocatorWorkspace.getLocator("SDE.Redlands_ZIP");
IAddressGeocoding pAddressGeocoding = new IAddressGeocodingProxy(pLocator);
//open the Table to geocode
ITable pAddressTable = pFeatureWorkspace.openTable("arcobjects.sde.REDLANDS_ADDRESS");
//create a FeatureClass to contain the geocoding results
//the FeatureClass must contain an ObjectID Field, all of the MatchFields returned by the Locator,
//and Fields that contain the address input values
IFieldsEdit pFieldsEdit = new Fields();
IFieldEdit pFieldEdit = new Field();
//add an ObjectID Field
pFieldEdit.setName("OBJECTID");
pFieldEdit.setType(com.esri.arcgis.geodatabase.esriFieldType.esriFieldTypeOID);
pFieldsEdit.addField(pFieldEdit);
//add the MatchFields
IFields pMatchFields = pAddressGeocoding.getMatchFields();
for (int i = 0; i < pMatchFields.getFieldCount()-1; i++) {
pFieldsEdit.addField(pMatchFields.getField(i));
}
//add the address fields
IFields pAddressFields = pAddressTable.getFields();
PropertySet pFieldsToCopy = new PropertySet();
IField pField = pAddressFields.getField(pAddressFields.findField("ADDRESS"));
pFieldEdit = new IFieldEditProxy(pField);
pFieldEdit.setName("ARC_ADDRESS");
pFieldsEdit.addField(pField);
pFieldsToCopy.setProperty("ARC_ADDRESS", "ADDRESS");
pField = pAddressFields.getField(pAddressFields.findField("ZIP"));
pFieldEdit = new IFieldEditProxy(pField);
pFieldEdit.setName("ARC_ZIP");
pFieldsEdit.addField(pField);
pFieldsToCopy.setProperty("ARC_ZIP","ZIP");
UID pUID = new UID();
pUID.setValue("esriGeodatabase.Feature");
//geocode the Table into the FeatureClass
IFeatureClass pFeatureClass = pFeatureWorkspace.createFeatureClass("REDLANDS_LOCATION",
new IFieldsProxy(pFieldsEdit), pUID, null,
com.esri.arcgis.geodatabase.esriFeatureType.esriFTSimple, "Shape", "");
//geocode the Table into the FeatureClass
pAddressGeocoding.matchTable(pAddressTable, "ADDRESS,ZIP", "", pFeatureClass,
"Shape,Status,Score,Side", pFieldsToCopy, null);
//attach the Locator to the geocoded FeatureClass
ILocatorAttach2 pLocatorAttach = new ILocatorAttach2Proxy(pLocatorWorkspace);
pLocatorAttach.attachLocator(new ILocatorProxy(pAddressGeocoding),new ITableProxy(pFeatureClass),
"ARC_ADDRESS,ARC_ZIP", "Shape,Status,Score,Side");
} catch (IOException ex) {
ex.printStackTrace();
}
The following example illustrates how to use the IAdvancedGeocoding interface to rematch a geocoded feature class:
try {
PropertySet pConnectionProperties = new PropertySet();
pConnectionProperties.setProperty("server", "mendota");
pConnectionProperties.setProperty("instance", "esri_sde");
pConnectionProperties.setProperty("database", "arcobjects");
pConnectionProperties.setProperty("user", "sde");
pConnectionProperties.setProperty("password", "sde");
pConnectionProperties.setProperty("version", "SDE.Default");
IWorkspaceFactory pWorkspaceFactory = new SdeWorkspaceFactory();
IWorkspace pWorkspace = pWorkspaceFactory.open(pConnectionProperties,0);
IFeatureWorkspace pFeatureWorkspace = new IFeatureWorkspaceProxy(pWorkspace);
//open the FeatureClass to rematch
IFeatureClass pFeatureClass = pFeatureWorkspace.openFeatureClass("arcobjects.SDE.REDLANDS_LOCATION");
com.esri.arcgis.geodatabase.IDataset pDataset =
new com.esri.arcgis.geodatabase.IDatasetProxy(pFeatureClass);
//get the Locator that is attached to the FeatureClass
ILocatorManager pLocatorManager = new LocatorManager();
IAttachedLocator pAttachedLocator = pLocatorManager.getLocatorFromDataset(pDataset);
IAdvancedGeocoding pAdvancedGeocoding = new IAdvancedGeocodingProxy(pAttachedLocator.getLocator());
//modify the geocoding properties on the Locator and rematch the FeatureClass
IGeocodingProperties pGeocodingProperties = new IGeocodingPropertiesProxy(pAdvancedGeocoding);
pGeocodingProperties.setMinimumMatchScore(100);
//create a feature class to store, codes omitted.
IFeatureClass pResultTable = null;
pAdvancedGeocoding.rematchTable(
pAttachedLocator.getInputTable(),
pAttachedLocator.getInputFieldNamesList(),
pAttachedLocator.getInputJoinFieldName(),
new IFeatureClassProxy(pAttachedLocator.getOutputTable()),
pAttachedLocator.getOutputFieldNamesList(),
pAttachedLocator.getOutputJoinFieldName(),
"",
null );
} catch (IOException ex) {
ex.printStackTrace();
}
The IReverseGeocoding and IReverseGeocodingProperties interfaces provide access to members for finding the address closest to a Point.
Use the SearchDistance property to define the maximum search tolerance, in the units specified by the SearchDistanceUnits property, to use when searching for an address represented by a Point.
The reverseGeocode method returns a PropertySet representing the address closest to the Point passed as the location parameter. Note that the Point object passed to the location parameter must be projected into the spatial reference used by the locator before it's passed to the reverseGeocode parameter. Use the bReturnIntersection parameter to indicate whether the reverseGeocode method should return an intersection address. The names of the properties contained in the PropertySet correspond to the names of the Field objects in the Fields collection returned by the AddressFields property on the IAddressInputs interface on the locator.
The following example illustrates how to use the IReverseGeocoding and IReverseGeocodingProperties interfaces to find the address closest to a point:
try {
//open an AGSServerConnection
PropertySet pConnectionProperties = new PropertySet();
pConnectionProperties.setProperty("machine", "mendota");
IAGSServerConnectionFactory pAGSServerConnectionFactory = new AGSServerConnectionFactory();
IAGSServerConnection pAGSServerConnection = pAGSServerConnectionFactory.open(pConnectionProperties, 0);
//get a Locator from the AGSLocatorWorkspace
ILocatorManager pLocatorManager = new LocatorManager();
ILocatorManager2 pLocatorManager2 = new ILocatorManager2Proxy(pLocatorManager);
ILocatorWorkspace pLocatorWorkspace =
pLocatorManager2.getAGSLocatorWorkspace(
new IAGSServerConnectionNameProxy(pAGSServerConnection.getFullName()));
IReverseGeocoding pReverseGeocoding =
new IReverseGeocodingProxy(pLocatorWorkspace.getLocator("USA Streets"));
//create a Point at which to find the address
IAddressGeocoding pAddressGeocoding = new IAddressGeocodingProxy(pReverseGeocoding);
IFields pMatchFields = pAddressGeocoding.getMatchFields();
IField pShapeField = pMatchFields.getField(pMatchFields.findField("Shape"));
IPoint pPoint = new com.esri.arcgis.geometry.Point();
pPoint.setSpatialReferenceByRef(pShapeField.getGeometryDef().getSpatialReference());
pPoint.setX(-117.2);
pPoint.setY(34.06);
//set the search tolerance for reverse geocoding
IReverseGeocodingProperties pReverseGeocodingProperties =
new IReverseGeocodingPropertiesProxy(pReverseGeocoding);
pReverseGeocodingProperties.setSearchDistance(100);
pReverseGeocodingProperties.setSearchDistanceUnits(com.esri.arcgis.system.esriUnits.esriMeters);
//find the address nearest the Point
IPropertySet pAddressProperties = pReverseGeocoding.reverseGeocode(pPoint, false);
//print the address properties
IAddressInputs pAddressInputs = new IAddressInputsProxy(pReverseGeocoding);
IFields pAddressFields = pAddressInputs.getAddressFields();
for (int i = 0; i < pAddressFields.getFieldCount()-1; i++) {
IField pAddressField = pAddressFields.getField(i);
System.out.println(pAddressField.getAliasName() + ": " +
pAddressProperties.getProperty(pAddressField.getName()));
}
} catch (IOException ex) {
ex.printStackTrace();
}
A GeocodeServer is a ServerObject served by an ArcGIS Server that can be used to geocode addresses. Internally, a GeocodeServer uses an address locator to do the work of geocoding, and exposes the high-level functionality of the address locator using the IGeocodeServer interface. Generally, GeocodeServer objects will be used by ArcGIS Server developers to create server applications that include geocoding functionality. ArcGIS Engine developers may, however, wish to use GeocodeServer objects to be able to include geocoding functionality in custom applications using only the high-level geocoding functionality exposed by a GeocodeServer.
The following example illustrates how to use a GeocodeServer to geocode a single address:
try {
//open a GISServerConnection
IGISServerConnection pGISServerConnection = new GISServerConnection();
pGISServerConnection.connect("xiancity");
//get a GeocodeServer from the GISServerConnection
IServerObjectManager pServerObjectManager = pGISServerConnection.getServerObjectManager();
IServerContext pServerContext = pServerObjectManager.createServerContext("redlands-geocode", "GeocodeServer");
IServerObject pServerObject= pServerContext.getServerObject();
IGeocodeServer pGeocodeServer = new IGeocodeServerProxy(pServerObject);
//construct an address PropertySet to geocode
Object object = pServerContext.createObject("esriSystem.PropertySet");
IPropertySet pAddressProperties = new IPropertySetProxy(object);
pAddressProperties.setProperty("Street", "380 New York St.");
pAddressProperties.setProperty("City", "Redlands");
pAddressProperties.setProperty("State", "CA");
pAddressProperties.setProperty("ZIP", "92373");
//set the geocoding properties to use to geocode the address
IPropertySet pLocatorProperties = pGeocodeServer.getLocatorProperties();
pLocatorProperties.setProperty("MinimumMatchScore", "100");
pLocatorProperties.setProperty("SpellingSensitivity", "100");
//geocode the address
IPropertySet pMatchProperties = pGeocodeServer.geocodeAddress(pAddressProperties, pLocatorProperties);
//print the match properties
IFields pMatchFields = pGeocodeServer.getResultFields(null);
for (int i = 0; i < pMatchFields.getFieldCount(); i++) {
IField pMatchField = pMatchFields.getField(i);
if (pMatchField.getType() == com.esri.arcgis.geodatabase.esriFieldType.esriFieldTypeGeometry){
IPoint pPoint = new IPointProxy( pMatchProperties.getProperty(pMatchField.getName()));
if (!pPoint.isEmpty()) {
System.out.println("X:"+pPoint.getX()+" Y:"+pPoint.getY());
}
} else {
System.out.println(pMatchField.getAliasName()+ ":"+
pMatchProperties.getProperty(pMatchField.getName()));
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
The linear referencing objects provide a framework for the creation, management, and display of linearly referenced data. These objects allow you to find and identify route locations, as well as dynamically segment route events so that they can be displayed on a map. Additional objects are provided for both route and event geoprocessing, as well as for the enhanced cartographic display of routes and events.
The linear referencing objects can logically be grouped as follows:
Locator is an abstract class that specifies the interfaces common to all types of locator objects. Types of locators include addresses, x,y coordinates, routes, and place names. Locators combine reference data and a location method.
The ILocator interface provides access to the properties of a locator.
The ILocatorFullName interface provides access to the name property of a Locator so that it may be persisted.
RouteLocator is an abstract class. A RouteLocator knows how to transform a route location into a shape that can be displayed on a map.
Route locations describe either a precise location along a route or a portion of a route between a from- and to- measure (see the discussion on RouteMeasureLocator).
The IRouteLocator2 interface inherits from IRouteLocator and is useful for retrieving the properties of a RouteLocator object and for determining the shape of route locations and events (see example following the discussion on route locations). Furthermore, the identify method is used to identify route locations using an envelope. A good way to create this envelope is to use the envelope of the map document's current location expanded by the search tolerance. For example:
try {
//get a MapBean from somewhere;
MapBean mapBean = null;
IEnvelope pEnvelope = mapBean.getExtent();
//Get search tolerance from somewhere, typically from
//ISelectionEnvironment object
double searchTolerance = 0.1;
pEnvelope.expand(searchTolerance, searchTolerance, false);
} catch(Exception e) {
e.printStackTrace();
}
For route locators, the RouteFeatureClass class can be a coverage route system, a PolyLineM shapefile, or a PolyLine feature class (with m-values) in a Personal, File or ArcSDE geodatabase. This means routes are stored in a feature class where IGeometryDef.getGeometryType = esriGeometryPolyLine and IGeometryDef.isHasM = True.
A RouteMeasureLocator is one type of RouteLocator. It determines the shape of a route location by matching the route location's measure values to those stored in a route feature. A RouteMeasureLocator is created via its Name object counterpart, RouteMeasureLocatorName.
The IRouteLocatorName interface is used to retrieve the properties of a RouteLocator object.
LocatorName is an abstract class that can be used to refer to a Locator object.
RouteLocatorName is an abstract class that can be used to refer to a RouteLocator object.
RouteMeasureLocatorName is a class that can be used to refer to a RouteMeasureLocator object. It is a specific implementation of LocatorName and RouteLocatorName.
All route locator name classes implement the IRouteLocatorName interface. This interface is used for setting and retrieving the properties of a RouteLocatorName object. Some things to note about this interface:
The following example shows how to create a RouteMeasureLocator via a RouteMeasureLocatorName.
try {
//get A PolyLineM feature class from somewhere;
IFeatureClass pRouteFC = null;
IDataset pDS = new IDatasetProxy(pRouteFC);
IRouteLocatorName pRtLocatorName = new RouteMeasureLocatorName();
pRtLocatorName.setRouteFeatureClassNameByRef(pDS.getFullName());
pRtLocatorName.setRouteIDFieldName("");
pRtLocatorName.setRouteMeasureUnit(com.esri.arcgis.system.esriUnits.esriMeters);
IName pName = new INameProxy(pRtLocatorName);
pName.open();
} catch (IOException ex) {
ex.printStackTrace();
}
A RouteMeasureLocation describes a portion of a route or a single position along a route.
The IRouteLocation interface lets you define the properties of a route location. For example, route locations occur along a single route, so you set that value here. Additionally, you identify the units in which the route location was collected and specify whether you want the route location's shape offset from its route when it is located.
Offsets are in the spatial reference units of the route feature class (and not necessarily the same units as the route feature class' measures). Therefore, an offset on route data stored in geographic units might produce inconsistent results. Offsets are used for rendering purposes only.
Setting the IRouteLocation.measureUnit property (get/set) enables you to do on-the-fly measure conversion. This property corresponds to IRouteLocator:RouteMeasureUnit. For example, you may know the position of a route location in miles, but your route feature class has its measures stored in meters. By setting these values accordingly, you can achieve measure conversion.
RouteMeasureLineLocation is a class that describes portions of a route using from and to measure locations.
The IRouteMeasureLineLocation interface is where you set the route location's from- and to-measure values. For example, say you wanted to find a location from 2,500 meters to 3,500 meters along route 10. Furthermore, you want this location to be offset 25 meters from the route. Your code would look like this:
try {
IRouteLocation pRouteLoc = new RouteMeasureLineLocation();
pRouteLoc.setMeasureUnit(com.esri.arcgis.system.esriUnits.esriMeters);
pRouteLoc.setRouteID(10);
pRouteLoc.setLateralOffset(25);
IRouteMeasureLineLocation pRMLineLoc = new IRouteMeasureLineLocationProxy(pRouteLoc);
pRMLineLoc.setFromMeasure(2500);
pRMLineLoc.setToMeasure(3500);
} catch (IOException ex) {
ex.printStackTrace();
}
A RouteMeasurePointLocation is a class that uses a single m-value to describe a single position along a route.
The IRouteMeasurePointLocation interface is where you set the route location's m-value. For example, if you wanted to find a location 565.5 meters along route 10, your code would look like this:
try {
IRouteLocation pRouteLocation = new RouteMeasurePointLocation();
pRouteLocation.setMeasureUnit(com.esri.arcgis.system.esriUnits.esriMeters);
pRouteLocation.setRouteID(10);
pRouteLocation.setLateralOffset(0);
IRouteMeasurePointLocation pRMPointLoc =new IRouteMeasurePointLocationProxy(pRouteLocation);
pRMPointLoc.setMeasure(565.5);
} catch (IOException ex) {
ex.printStackTrace();
}
Once you have created a route location, determine its geometry by calling the IRouteLocator.locate method (refer to the examples above to see how the RouteLocator object was created).
try {
IGeometry[] pGeom = null;
IRouteLocator pRtLocator = null;
IRouteLocation pRMPointLoc = null;
int locError[] = null;
pRtLocator.locate(pRMPointLoc, pGeom, locError);
//locError is a sinple element array with
//com.esri.arcgis.geodatabase.esriLocatingError constant
} catch (IOException ex) {
ex.printStackTrace();
}
An event table is a table that stores route locations and associated attributes. An event, therefore, is a row from an event table. For example, an event may be a speed limit of 110 km/h on route 50 from km 92 to 138. In this case, the route location information of route 50 between km 92 and 138 is used to reference an attribute to a particular portion of a route in a route feature class.
You need to create RouteEventProperties in order to identify certain characteristics of the table so that it can be recognized as an event table. RouteEventProperties are helper objects for a RouteEventSource.
The IRouteEventProperties2 interface inherits from IRouteEventProperties and establishes the route key field, the measure units the events were collected in, and (optionally) the lateral offset field.
The AddErrorField method indicates whether you want an additional field added to your RouteEventSource (see below) for storing event-locating errors.
Note that the route key (EventRouteIDFieldName) defined on this interface is related to the RouteIDFieldName property on both IRouteLocator and IRouteLocatorName. This is how events are located along their respective routes.
The EventRouteIDFieldName does not have to have the same name as the RouteIDFieldName, but it must store similar data.
RouteMeasureLineProperties is a class used to specify the characteristics of a line event table.
The IRouteMeasureLineProperties interface is where you identify the line event table's from- and to-measure fields. Each line event's measures reflect the distance from the lowest measure along its route. To set up line event properties where your table has an offset field, your code would look like this:
try {
IRouteMeasureLineProperties pRMLP = new RouteMeasureLineProperties();
IRouteEventProperties2 pRtProp = new IRouteEventProperties2Proxy(pRMLP);
pRtProp.setAddErrorField(true);
pRtProp.setErrorFieldName("LOC_ERROR");
pRtProp.setEventMeasureUnit(com.esri.arcgis.system.esriUnits.esriMeters);
pRtProp.setEventRouteIDFieldName("rKey");
pRtProp.setLateralOffsetFieldName("Offset");
IRouteMeasureLineProperties pRMLineProp = new IRouteMeasureLinePropertiesProxy(pRtProp);
pRMLineProp.setFromMeasureFieldName("fmp");
pRMLineProp.setToMeasureFieldName("tmp");
} catch (IOException ex) {
ex.printStackTrace();
}
RouteMeasurePointProperties is a class used to specify the characteristics of a point event table.
The IRouteMeasurePointProperties2 interface inherits from IRouteMeasurePointProperties2 and is where you identify the point event table's measure field. Each point event's measure reflects the distance from the lowest measure along its route.
The addAngleField method indicates whether you want a field added to your RouteEventSource for storing the angle of the route where the point event is placed. This is useful for things like rotating marker symbols. The normal (perpendicular) or tangent angle can be calculated.
By default, when a point event is located along a route, a point feature is created. In some applications, however, route measures are not unique. The asPointFeature method provides the ability to create Multipoint features.
Code to set up point event properties on a table might look like this:
try {
IRouteMeasurePointProperties pRtProp1 = new RouteMeasurePointProperties();
IRouteEventProperties2 pRtProp = new IRouteEventProperties2Proxy(pRtProp1);
pRtProp.setAddErrorField(true);
pRtProp.setErrorFieldName("LOC_ERROR");
pRtProp.setEventMeasureUnit(com.esri.arcgis.system.esriUnits.esriMeters);
pRtProp.setEventRouteIDFieldName("rKey");
IRouteMeasurePointProperties2 pRMPointProp = new IRouteMeasurePointProperties2Proxy(pRtProp);
pRMPointProp.setMeasureFieldName("mile");
pRMPointProp.setAddAngleField(true);
pRMPointProp.setAngleFieldName("LOC_ANGLE");
}catch (IOException ex) {
ex.printStackTrace();
}
A RouteEventSource serves an event table as a 'dynamic' feature class. Every row in the table is served as a feature whose shape is calculated on the fly every time it is asked for. This is dynamic segmentation.
In order to serve an event table as a feature class, a RouteEventSource needs to know things such as the event table, the RouteEventProperties, and the RouteLocator. The IRouteEventSource interface retrieves this information.
Just like the locator objects outlined earlier, a RouteEventSource is created via its name object counterpart, RouteEventSourceName.
A RouteEventSourceName specifies a RouteEventSource object and can be used to instantiate it.
The IRouteEventSourceName interface sets the event table, the RouteEventProperties, and the RouteLocator. The following example shows how to create a RouteEventSource via a RouteEventSourceName. Here, the RouteMeasurePointProperties and the RouteLocatorName are already created. These values are set using IRouteEventSourceName.
try {
//get the objects from somewhere.
IName pName = null;
IRouteEventProperties pREProperties = null;
IRouteLocatorName pRtLocatorName = null;
IRouteEventSourceName pRESN = new RouteEventSourceName();
pRESN.setEventTableNameByRef(pName);
pRESN.setEventPropertiesByRef(pREProperties);
pRESN.setRouteLocatorNameByRef(pRtLocatorName);
IName pName1 = new INameProxy(pRESN);
IRouteEventSource pRES = new IRouteEventSourceProxy(pName1.open()) ;
}catch (IOException ex) {
ex.printStackTrace();
}
Because a RouteEventSource is a subclass of a feature class, it can be used anywhere a feature class can be. For example, a RouteEventSource can act as the basis of a feature layer in ArcMap, and its attributes can be edited directly with the editing tools in ArcMap.
There may be some limitations imposed by the event table, however. For example, you will not be able to directly edit a feature class created from a delimited text file table since the Editor does not allow text files to be edited directly.
In a RouteEventSource, there is one feature for every row of the original event table. In some cases, however, the features have empty shapes. This is because there was some reason the event could not be properly located. Other times, an event can only be partially located (this happens for line events only).
The IEventSourceErrors interface exposes some methods that allow you to determine the locating errors of events. The following example uses IEventSourceErrors.getErrors to create an enumeration of the event rows that did not locate properly.
public void testGetErrors(IRouteEventSource pRES) throws IOException {
IRow[] pRow = null;
int[] LocError = null;
IEventSourceErrors pESErrors = new IEventSourceErrorsProxy(pRES);
IEnumEventError pEnum = pESErrors.getErrors();
pEnum.next(pRow, LocError);
do {
switch (LocError[0]) {
case 0 :
System.out.println(pRow[0].getOID() +
":No error");
break;
case 1 :
System.out.println(pRow[0].getOID() +
":Invalid route ID");
break;
case 2 :
System.out.println(pRow[0].getOID() +
":Invalid measure");
break;
case 3 :
System.out.println(pRow[0].getOID() +
":Can't find route");
break;
case 4 :
System.out.println(pRow[0].getOID() +
":Route's shape is empty");
break;
case 5 :
System.out.println(pRow[0].getOID() +
":Can't find location along route");
break;
case 6 :
System.out.println(pRow[0].getOID() +
":Can't find extent along route");
break;
case 7 :
System.out.println(pRow[0].getOID() +
":From-measure partial match");
break;
case 8 :
System.out.println(pRow[0].getOID() +
":To-measure partial match");
break;
case 9 :
System.out.println(pRow[0].getOID() +
":Route's measures are not set");
break;
case 10 :
System.out.println(pRow[0].getOID() +
":Route is not capable of storing measures");
break;
case 11 :
System.out.println(pRow[0].getOID() +
":From-measure and to-measure partial match");
break;
default:
System.out.println(pRow[0].getOID() +
":Non-typical locating error - "+LocError[0]);
}
pEnum.next(pRow, LocError);
} while (pRow != null);
}
The RouteLayerExtension class is worth mentioning, as it is a helper class for dynamic segmentation in the ArcMap user interface.
Every feature layer has zero or one RouteLayerExtension objects associated with it. Whenever a new feature layer is added in ArcMap, its associated feature class' geometry definition is inspected to see whether IGeometryDef.getGeometryType = esriGeometryPolyLine and IGeometryDef.isHasM = True. If both of these criteria are met (and the layer is not based on linear route events), a RouteLayerExtension is attached to the layer. If you are creating feature layers that will not be added to an ArcMap document, however, you are responsible for attaching a RouteLayerExtension if you want to make use of its services later in your application.
The IRouteLayerExtension interface is where you set the route identifier field name for the route feature class. The other methods on this interface are used by the Add Route Events and the Identify Route Locations dialogs to automatically populate certain parameters.
Before embarking on any linear referencing project, you will need route data. This data may not exist, may exist but without the appropriate measure system defined, or may exist in a format you do not wish to use.
A route is simply any linear feature that has a unique identifier and measurement system stored with it. Routes can be stored in coverages, shapefiles, and Personal, File, and ArcSDE geodatabases.
A RouteMeasureCreator is used to create routes that are stored in a shapefile or in a Personal, File, or ArcSDE geodatabase. A RouteMeasureCreator merges linear features that share a common identifier and sets the measure values.
The IRouteMeasureCreator2 interface inherits from IRouteMeasureCreator. It exposes the methods and properties necessary for creating route feature classes. One important thing to note is that you can specify either an InputFeatureClass or an InputFeatureSelection. The latter gives you the ability to create routes from only a portion of the features in your input feature class. The input line features are merged on InputRouteIDFieldName.
The createUsingCoordinatePriority2 method is used when the route measures on the input line features are not known. This method will generate the measures either by accumulating the digitized length or by accumulating a numeric attribute value of the input features. If you choose to use digitized length, the units of the output route measures will be the same as the coordinate system of the output geometry definition's spatial reference (feet, meters, etc.). To use the digitized length, simply pass an empty string for the lengthFieldName parameter.
With createUsingCoordinatePriority2, you control the direction measures are assigned to the routes by specifying the coordinate priority of the starting measure. The coordinate priority can be set using the values in the esriMSeedingCorner enumeration: esriMSeedingUpperLeft, esriMSeedingBottomLeft, esriMSeedingUpperRight or esriMSeedingBottomRight. These options are determined by placing the minimum-bounding rectangle around the input features that are going to be merged to create one route.
Routes with multiple, disjointed parts are supported. A route representing a road, for example, might have the same name on either side of a river. For situations like this, you can choose to ignore spatial gaps between parts when using createUsingCoordinatePriority2. If you choose to ignore spatial gaps, route measures will be continuous when a disjointed route is created. If you want the spatial gap incorporated in the measures, the gap distance is the straight-line distance between the endpoints of the parts. The units of the gap will be that of the coordinate system of the output geometry definition's spatial reference, which may or may not be the same as the measure units.
The createUsing2Fields2 method is used when measure values already exist as attributes of the input linear features. That is, two attributes exist that represent from- and to-measure information for the input lines. When using this method, it is important to orient each input linear feature in the direction of increasing measure to prevent routes that have measures that do not always increase.
When route measures are inaccurate, events will not be located properly. It is possible to adjust route measures to correspond with known measure locations using a RouteMeasureCalibrator. A RouteMeasureCalibrator adjusts route measures by reading measure information stored as an attribute in a point feature class. Each point falls on the particular route it calibrates or within a given tolerance. Many points may be used to calibrate a single route.
During the calibration process, a new vertex is created where the calibration points intersect the route. The measure value on these new vertices corresponds to the measure value stored as a point attribute. The measure values on other preexisting route vertices can be interpolated and/or extrapolated.
The IRouteMeasureCalibrator2 interface inherits from IRouteMeasureCalibrator. It exposes the methods and properties necessary for calibrating routes. One important thing to note is that you can specify either an InputFeatureClass or an InputFeatureSelection. The latter gives you the ability to limit the number of points used to calibrate. The RouteLocator property should not be used. Specifying a RouteFeatureClass and RouteIDFieldName or RouteFeatureSelection and RouteIDFieldName is the recommended usage of this interface. A RouteFeatureSelection allows you to limit the routes that will be considered for calibration.
In order for the measure value on a vertex to be interpolated or extrapolated, a calibration ratio is needed. There are two ways this ratio can be determined. The calibrateRoutesByDistance method uses the shortest path distance between the input points.
The calibrateRoutesByMs method uses the existing measure distance between the input points. This method is useful when the length to measure ratio on the input route is not consistent and you are using the calibration process to fine-tune a route's measures.
Either whole or partial routes can be calibrated. You can choose to interpolate between the input points, extrapolate before the input points, extrapolate after the input points, or use any combination of these three methods. The updateHow parameter is given as a combination of esriGeometryUpdateMEnum values. When combining multiple values, the bitwise OR operator should always be used. This assures an error-free combination of the values (as long as the attempted combination is valid). Do not use the addition operator (+) to combine the values as unexpected results may occur. For example, to interpolate between the input points and to extrapolate before and after the input points, you would use 7, which equates to: esriGeometryInterpolate OR esriGeometryExtrapolateBefore OR esriGeometryExtrapolateAfter. A value of 0 will only split the input polyline and assign the Ms value to the new created vertex.
When calibrating disjointed routes, you may choose to ignore the spatial gap between the parts. If you choose to ignore spatial gaps, route measures will be continuous. If you want the spatial gap incorporated in the measures, the gap distance is the straight-line distance between the endpoints of the parts. The units of the gap will be that of the coordinate system of the output geometry definition's spatial reference, which may or may not be the same as the measure units. Ignoring spatial gaps is only a valid choice when using calibrateRoutesByDistance.
The types of event processing operations include dissolving events, concatenating events, and event overlay (line on line and point on line). Event geoprocessing is exposed via the RouteMeasureGeoprocessor class.
The IRouteMeasureEventGeoprocessor2 interface inherits from IRouteMeasureEventGeoprocessor and provides access to the event geoprocessing operations.
Event dissolving and event concatenating both involve combining records in line event tables if they are on the same route and have the same value for specified fields. The results are written to a new line event table. The difference between dissolving and concatenating is that concatenating only combines events in situations where the to-measure of one event matches the from-measure of the next event. Dissolving events will combine events when there is measure overlap.
A line-on-line overlay involves the overlay of two line event tables to produce a line event table. For example, you might want to take an event table that describes pavement cracking and overlay it with pavement resurfacing dates. The results of such an overlay could be used to find the characteristics of the oldest paved sections.
When performing a line-on-line overlay, the results may contain events that have no length (for example, the from- and to-measure values are the same). The IRouteMeasureEventGeoprocessor2.keepZeroLengthLineEvents property can be used to indicate whether you want such events in your result set.
A line-on-point overlay involves the overlay of a point event table with a line event table to produce either a point or line event table. The intersection of a point and a line event table produces a point event table. The union of a point and a line event table produces a line event table.
The RouteLocatorOperations class computes the intersection of input features (point, line or polygon) and route features and writes the route and measure information to a new event table. The new table can be dBASE or Personal, File or ArcSDE geodatabase.
The IRouteLocatorOperations2 interface inherits from IRouteLocatorOperations and provides access to the methods that allow features to be located along routes. Note that you can specify either an InputFeatureClass or an InputFeatureSelection. The latter gives you the ability to limit the number of features to be located. The RouteLocator property should not be used. Specifying a RouteFeatureClass and RouteIDFieldName or RouteFeatureSelection and RouteIDFieldName is the recommended usage of this interface. A RouteFeatureSelection allows you to limit the routes that will be considered for locating.
The locateLineFeatures method determines the route and measure information where the lines intersect with routes. This intersection is based on a specified cluster tolerance that represents the maximum tolerated distance between the input lines and the target routes. This method should not be thought of as a conflation tool and large cluster tolerances should be avoided. Always use the smallest cluster tolerance possible to achieve the best results. When using this method, the outputProperties should be for line events (RouteMeasureLineProperties).
The locatePointFeatures method determines the route and measure information where the points intersect with routes. This intersection is based on a search radius, which defines how far around each point a search will be done to find a target route. When using this method, the outputProperties should be for point events (RouteMeasurePointProperties).
The locatePointEvents method represents a variation of the locatePointFeatures algorithm and is designed specifically to work on point events. When using this method, the outputProperties should be for point events (RouteMeasurePointProperties).
The locatePolygonFeatures method determines the route and measure information where the polygons intersect with routes. When using this method, the outputProperties should be for line events (RouteMeasureLineProperties).
Hatching is a type of labeling that is designed to post and label hatch marks or symbols at a regular interval along measured linear features. Hatching can be used for both distance-based and non distance-based measures. Distance-based measures include kilometers, miles, feet, and meters. Non distance-based measures include seismic shot point numbers where measure values generally increase in even intervals based on some nominal distance.
Hatching is exposed via the HatchLayerExtension class. Every feature layer has zero or one HatchLayerExtension objects associated with it. Whenever a new feature layer is added in ArcMap, its associated feature class' geometry definition is inspected to see whether IGeometryDef.getGeometryType = esriGeometryPolyLine and IGeometryDef.isHasM = True (this includes RouteEventSource objects based on linear event tables). If both of these criteria are met, a HatchLayerExtension is attached to the layer. If you are creating feature layers that will not be added to an ArcMap document, however, you are responsible for attaching a HatchLayerExtension.
The IHatchLayerExtension interface is where you control the properties of the HatchLayerExtension. To display hatches on a layer, there must be at least one HatchClass associated with the HatchLayerExtension. A HatchLayerExtension has one HatchClass associated with it when it is created. You may choose to manipulate the properties of this HatchClass, or you may choose to remove it (removeClass or removeAll) and add another (addClass).
The following code demonstrates how to attach a HatchLayerExtension to a new feature layer:
try {
IWorkspaceFactory pWorkspaceFactory = new ShapefileWorkspaceFactory();
IWorkspace pWorkspace = pWorkspaceFactory.openFromFile("d:/data/dyndata",0);
IFeatureWorkspace pFeatureWorkspace = new IFeatureWorkspaceProxy(pWorkspace);
IFeatureLayer pFeatureLayer = new FeatureLayer();
IFeatureClass pFeatureClass = pFeatureWorkspace.openFeatureClass("roads_hwy");
pFeatureLayer.setFeatureClassByRef(pFeatureClass);
pFeatureLayer.setName(pFeatureLayer.getFeatureClass().getAliasName());
ILayerExtensions pLayerExt = new ILayerExtensionsProxy(pFeatureLayer);
IHatchLayerExtension pHatchExt = new HatchLayerExtension();
//pHatchClass is created elsewhere
IHatchClass pHatchClass = null;
pHatchExt.addClass("", pHatchClass);
pLayerExt.addExtension(pHatchExt);
} catch (Exception e) {
e.printStackTrace();
}
The reason a HatchLayerExtension can have more than one HatchClass is because it is useful to be able to hatch different features in different ways (IHatchClass.getFilter). For example, you might want to hatch only major highways and leave local roads, collector roads un-hatched. It is also useful to be able hatch the same features in different ways, depending on the map's scale (IHatchClass.getMaximumScale and IHatchClass.getMinimumScale). For example, you might want to leave features un-hatched when zoomed out full extent, and have them hatched when zoomed in to a certain scale threshold.
A HatchClass has a HatchTemplate. Each HatchTemplate is composed of one or more HatchDefinition objects. A HatchDefinition defines the properties of the hatches themselves. There are two kinds of HatchDefinition: HatchMarkerDefinition and HatchLineDefinition.
The easiest way to understand the HatchClass and HatchDefinition classes is to use a ruler analogy. On a ruler, there is a series of vertical lines, or hatches, separated by a regular interval. For example, on a centimeter (cm) ruler, the hatches are typically spaced every millimeter (mm). One millimeter is 1/10 of a centimeter, so the hatch interval is 0.1.
Not all the hatches on a ruler are the same. Some are longer than others. Further, some have text, while others do not. On a centimeter ruler, the hatches placed at every millimeter (0.1 cm) are the shortest. The hatches placed at every 5 millimeters (0.5 cm) are a bit longer. The hatches placed every 10 millimeters (1 cm) are the longest. The longest hatches typically have text to indicate the measure value.
In this example, the ruler is a HatchClass. It is a container for three HatchDefinition objects. Each HatchDefinition is placed at a multiple of the hatch interval (IHatchClass.setHatchIntervalByRef). The longest hatches are placed at every 0.1 x 10 measure units. The second longest hatches are placed at every 0.1 x 5 measure units. The shortest hatches are placed at every 0.1 x 1 measure units. When placed on a map, hatch definitions within a single hatch class will not draw on top of one another.
Imagine that you have linear features whose measures are in miles. You want to place a hatch every quarter of a mile. This is a hatch interval of 0.25. Further, you want the hatches placed at every 0.25, 0.5, and 1 mile to look different (different length, different color). You want the hatches every mile to have text. Lastly, you want hatches at the ends of the feature. These end hatches are to look different than all other hatches and will have text. The following example demonstrates conceptually how hatching works in this scenario.
There are many ways in which you can control how hatches appear on a map. To do this, you will use the methods on the IHatchClass, IHatchTemplate, IHatchDefinition and IHatchLineDefinition interfaces. The following illustrate some of the ways hatches can be manipulated.
The IHatchClass interface is where you set properties that are specific to the data being hatched. As such, many of the properties on this interface allow data to be derived from a field or can be a specific value (see the discussion on the HatchInputValue class below).
The most important property on IHatchClass is HatchInterval. This specifies, in measure units, where the hatches will appear on the line. Note, however, that hatches can also be placed only at the route ends. In this case, simply do not specify a hatch interval.
All of the hatch definitions in a hatch class can be offset by the same amount using the LateralOffset property. Offsets are specified in the units of line feature class's coordinate system (e.g. feet or meters), or by the settings IHatchTemplate.setDisplayUnits and IHatchTemplate.setConvertUnits. Note, however, that each individual hatch definition can also have its own offset (IHatchDefinition.setOffset).
Hatching can start at a measure location other than the low measure (StartRange) of a route. Further, hatching can finish at a location other than the high measure (EndRange) of a route.
Before giving a code example for HatchClass, it is important to talk about the HatchInputValue class. A HatchInputValue is a helper class for many of the methods on the IHatchClass interface. For many hatching properties, it may be desirable to retrieve values from one of the feature's attributes, or it may be desirable to simply use pre-defined values. The following code shows how to create a new HatchClass and set some of its more important properties.
try {
IHatchClass pHatchClass = new HatchClass();
IHatchInputValue pHatchInput1 = new HatchInputValue();
pHatchInput1.setValue(new Integer(100));
pHatchClass.setHatchIntervalByRef(pHatchInput1);
IHatchInputValue pHatchInput2 = new HatchInputValue();
pHatchInput2.setField("MMIN");
pHatchClass.setStartRangeByRef(pHatchInput2);
IHatchInputValue pHatchInput3 = new HatchInputValue();
pHatchInput3.setField("MMAX");
pHatchClass.setEndRangeByRef(pHatchInput3);
IHatchInputValue pHatchInput4 = new HatchInputValue();
pHatchInput4.setValue(new Integer(50));
pHatchClass.setLateralOffsetByRef(pHatchInput4);
}catch(Exception e) {
e.printStackTrace();
}
Note that hatching templates can be stored in a esriLocationUI.HatchStyleGalleryClass. Because the properties on IHatchClass can be data specific, however, they are not saved when you add items to a HatchStyleGalleryClass.
The IHatchTemplate interface is where you set properties that are generic to all hatches in a HatchClass. These properties are not data specific. As such, these are the properties that do get saved when you store hatches in a HatchStyleGalleryClass.
In the case where a feature's geometry has multiple parts, it is possible to apply hatches to the feature as a whole or to each part individually by setting setHatchByPart to TRUE.
By default, the placement of hatches is adjusted to the hatch interval. This means that in cases where a line's low measure is not divisible by the interval, the first hatch will be placed at the first measure value that is divisible by the hatch interval. For example, a line whose measures range from 1.1 to 5.2 will have its first hatch placed at 1.25 when the hatch interval is 0.25. This behavior can be turned off, however, by setting setStartAtIntervalMultiple to FALSE. Note that end hatch definitions are not affected by this property and are not shown in the following example.
In cases in where a feature's high measure is not divisible by the hatch interval and an end hatch definition has been defined, it is possible to get two hatches that are very close to or on top of one another. To avoid this, it is possible to specify an EndHatchDrawingTolerance, which informs the hatching algorithm to not place hatches when they fall within the tolerance of the end hatch. The end hatch tolerance is specified in route measure units, and its value is typically set to a value that is less than the hatch interval.
As noted previously, a HatchDefinition defines the properties of the hatches themselves. There are two kinds of HatchDefinition: HatchMarkerDefinition and HatchLineDefinition.
The IHatchDefinition interface is where you specify the properties that are common to both marker and line hatches.
Each hatch definition can be displayed to the left, centered on, or to the right of a feature by setting the Alignment property. When set to center, hatches will be labeled to the left of the feature.
Hatch text is flipped as the direction of the feature changes. This is to make the text more readable. This can be turned off using AdjustTextOrientation, so the text is always oriented in the direction of increasing measure.
A hatch definition will be labeled with the measure value when the TextSymbol property has been set. The number of decimal places is controlled by the DisplayPrecision property. It is possible, however to add a prefix and/or a suffix to the measure value. The following code adds the suffix 'km' to every hatch in the HatchDefinition. In the following code example, pay particular attention to the TextDisplay and Suffix properties.
try {
IHatchDefinition pHatchDef = new HatchLineDefinition();
//get or set the symbols from somewhere
ILineSymbol pLineSymbol = null;
ITextSymbol pTextSymbol = null;
pHatchDef.setHatchSymbolByRef(new ISymbolProxy(pLineSymbol));
pHatchDef.setTextDisplay(com.esri.arcgis.location.esriHatchTextDisplay.esriHatchTDPrefixSuffix);
pHatchDef.setTextSymbolByRef(pTextSymbol);
pHatchDef.setSuffix("km");
pHatchDef.setDisplayPrecision(0);
}catch(Exception e) {
e.printStackTrace();
}
For certain applications, however, the ability to add a prefix and/or suffix is not enough. For these applications, it is possible to use VBScript or JScript to specify a function that will be evaluated. This function must be called FindLabel and, at the very least, the esri__measure value must be passed in (note that there are two underscore characters in esri__measure). Fields from the layer being hatched can also be passed to the FindLabel function, and are enclosed in square brackets [], regardless of the data type. In the following code example, the FindLabel function sets a hatch's label to be an empty string when the measure value passed in (esri__measure) is within 25 measure units of the value stored in the MMax field. Otherwise, it appends the 'km' suffix to the measure value. Pay particular attention to the TextDisplay, ExpressionParserEngine, ExpressionSimple and Expression properties.
try {
IHatchDefinition pHatchDef = new HatchLineDefinition();
//get or set the symbols from somewhere
ILineSymbol pLineSymbol = null;
ITextSymbol pTextSymbol = null;
pHatchDef.setHatchSymbolByRef(new ISymbolProxy(pLineSymbol));
pHatchDef.setTextSymbolByRef(pTextSymbol);
pHatchDef.setTextDisplay(com.esri.arcgis.location.esriHatchTextDisplay.esriHatchTDExpression);
pHatchDef.setExpressionSimple(false);
pHatchDef.setExpressionParserEngine(
com.esri.arcgis.location.esriHatchExpressionEngine.esriHatchVBScriptEngine);
// pHatchDef.setExpressionParserEngine(
// com.esri.arcgis.location.esriHatchExpressionEngine.esriHatchJScriptEngine);
StringBuffer sb = new StringBuffer();
sb.append("Function FindLabel (esri__measure, [MMax]) & vbCrLf &");
sb.append("mmax = [MMax] & vbCrLf &");
sb.append("m = esri__measure & vbCrLf &");
sb.append("If (mmax - m) <= 25 Then & vbCrLf &");
sb.append("FindLabel = \"\"\"\" & vbCrLf &");
sb.append("Else & vbCrLf &");
sb.append("FindLabel = CStr(Round(m, 1)) & \"\" km\"\" & vbCrLf &");
sb.append("End If & vbCrLf &");
sb.append("End Function");
pHatchDef.setExpression(sb.toString());
}catch(Exception e) {
e.printStackTrace();
}
Each HatchTemplate can have an EndHatchDefinition. An end hatch definition pays no attention to the specified hatch interval. Rather, it simply draws hatch marks at the low and high measure of a linear feature. For cases in which hatches get placed too close together near the end of a feature, it is possible to specify an EndHatchDrawingTolerance, which prevents certain hatches from drawing if they are within the tolerance (specified in measure units) of an end hatch. A HatchTemplate can only have one end hatch definition.
The IHatchLineDefinition interface is where you specify the properties that are specific to line hatches. At the very least, you should always set the Length property when dealing with line hatches. Note that the Length is specified in the units of the coordinate system of the route feature class's spatial reference, or by the settings IHatchTemplate.setDisplayUnits and IHatchTemplate.setConvertUnits.
Note that by default, line hatches are drawn perpendicular to the feature. It is possible to specify a SupplementalAngle that gets added to the calculated angle.