Building a command-line C++ application


Summary This topic steps through the complete process for creating a command-line C++ application using ArcGIS Engine.

Click here to get the sample associated with this walkthrough.

In this topic


This scenario is designed to introduce the ArcGIS Engine C++ API for cross-platform applications. To get the most out of this scenario, you should understand basic C/C++ programming concepts such as the preprocessor, functions, and memory management. Some familiarity with ArcObjects will also be helpful, although not required. Although this scenario does require conceptual knowledge of the C++ language, it does not require a lot of programming experience. The code used in this example provides an easy entry point to learn about the C++ API to ArcGIS Engine on a small and simple scale.

The purpose of this scenario is not to teach how to set up a C++ environment or how to compile on each supported operating system. Throughout this scenario it is assumed that you have a functional C++ environment and know how to compile a C++ program in that environment. What this scenario does provide is the steps to take and the code to write to create a command-line application that computes the slope of a given digital elevation model.
Rather than walk through this scenario, you can get the completed application from the samples installation location. The sample is installed as part of the ArcGIS developer samples.
If you do not have the developer samples installed, rerun the ArcObjects Software Development Kit (SDK) for Cross Platform C++ install wizard, select Modify, and click the samples feature.

Project description

This scenario covers some aspects of the ArcGIS Engine C++ API. The goal of the RasterSlope scenario is to create a standalone command-line application with the ArcGIS C++ API. The application will take as input a digital elevation model (DEM) and will build and persist the slope map raster dataset. Once you have completed this scenario, you will understand the techniques required to work with the ArcGIS Engine C++ API, including using the Spatial extension. In particular, the scenario covers the following techniques:
  • Programming with the ArcObjects SDK for Cross Platform C++ in a standard text editor.
  • Parsing command-line arguments
  • Enabling extensions—in particular, the Spatial extension.
  • Performing the calculation of slope on a raster dataset.
  • Persisting the resultant raster dataset.
  • Deploying the application on all platforms supported by the ArcGIS Engine C++ API.

Concepts

Slope datasets are often used as inputs in physical process models. Slope is commonly expressed in degrees or as a percentage. In the slope calculation a parameter (zFactor) can specify the number of ground x,y units in one z unit. This allows you to create a slope dataset with different z units from the input surface. To build the slope dataset, you will use the RasterSurfaceOp class and the ISurfaceOp interface it implements. You will also use the Raster class and the IRasterBandCollection interface it implements to persist the resulting raster dataset.
For a more in-depth explanation of slope, see the Burrough and McDonnell reference listed in the Additional references section at the end of this scenario.
The role of the software is to calculate the slope of a given raster dataset. The user's job is to provide a console at which to run the scenario, as well as a digital elevation model on which to run the scenario. Since this is a cross-platform application, the console can be on any supported platform.

Design

The application will be written entirely in the C++ language. This allows you, as the developer, to write code once on any supported platform and deploy the application on all supported ArcGIS Engine platforms. This scenario uses Microsoft Windows XP as the developer platform and nmake to compile and run the application from the command line, using the .NET 2008 compiler. However, the same code will work on any other supported platform if built accordingly. A Visual Studio 2008 project, as well as makefiles for both Solaris and Linux, are included with the solution code available in the developer kit.
For more detailed information, see the Programming against ArcObjects with C++ section.
In the design, some safeguards were taken to ensure that the application remained cross-platform. They include avoiding function calls and data types defined outside the ArcGIS Engine and the C++ API as well as platform-independent path processing. For example, it should not matter whether the user has "/" or "\" as the path separator in the arguments to the application as long as the path separated is used by the operating system on which the application is being executed. In addition, C++ standards need to be followed to avoid compiler dependencies.
For cross-platform compatibility, the data and pathnames used must be lowercased.

Requirements

To successfully follow this scenario you need the following:
  • An installation of the ArcObjects SDK for Cross Platform C++ with an authorization file enabling it for development use.
  • A text editor, such as Notepad or Microsoft Visual C++.
  • A supported C/C++ compiler. This scenario uses the Microsoft Visual C++ Compiler .NET 2008 (9.0).
  • A configured ArcObjects environment.
  • ArcSDK.h: the ArcGIS Engine C++ API header file.
  • A familiarity with the operating system you have chosen to work on and a basic foundation in C++ programming.
  • While no experience with other ESRI software is required, previous experience with ArcObjects and a basic understanding of maps is advantageous.
  • Access to the solution code and makefiles that come with this scenario. This is located at:
    <install_location>\DeveloperKit10.0\Samples\ArcObjectsCPP\Rasters\Computing_the_Slope_of_a_Raster_DatasetCpp.zip
If you do not have the developer samples installed, rerun the ArcObjects Software Development Kit (SDK) for Cross Platform C++ install wizard, select Modify, and click the samples feature.
  • An ArcGIS Spatial Analyst or ArcGIS Engine Runtime with Spatial extension license is required for the application to run once deployed.
  • To run the application, you will need a raster dataset.

Implementation

The implementation below provides you with all the code you will need to successfully complete the scenario. It does not provide step-by-step instructions to compile C++ applications, as it assumes that you already have a working knowledge of your chosen development environment. Error checking has been left out to increase code readability.
See the topic on Error handling for recommendations on error checking within your code.
This scenario's sample application demonstrates ArcGIS Engine Spatial extension functionality; the full source code is available in the samples included in the ArcObjects SDK for Cross Platform C++. Here are the files discussed in this scenario:
  • RasterSlope.cpp—Main C++ source file.
  • RasterSlope.h—Main C++ header file.
  • Makefile.Windows.template—nmake utility file template. During this exercise, you will copy this file from the solution code and rename it Makefile.Windows. You will then update it while following the scenario.
If you are unfamiliar with the nmake utility, see the Microsoft Developer Network for further information.
  • Makefile.Windows—nmake utility file that specifies compiler settings and rules, file dependencies, input arguments, and an execution rule for our application. In the solution code, this file is the completed makefile for the scenario. As you work through the scenario, the name will refer to the makefile you are building.
  • PathUtilities.cpp—Platform-independent path processing helper function implementation file.
  • PathUtilities.h—Platform-independent path processing helper function header file.
The following files are provided for your use if you choose another compilation option: Makefile.Solaris and Makefile.Solaris.Template, Makefile.Linux and Makefile.Linux.Template, and vs2008/vs2008.sln and vs2008/vs2008.vcproj.

Creating your build environment

This scenario uses nmake to build and deploy its application. To utilize nmake, you must write a makefile for it to execute. This scenario is not designed to teach you the basics of project management with the nmake utility. However, this scenario will step you through the parts of the makefile that must be customized for each application you build.
A template copy of Makefile.Windows, like the one provided in this scenario's solution code, is included in the ArcObjects SDK for Cross Platform C++ and can be accessed from the help system under Development Environments > C++ > Makefiles as Makefile.Windows.
First, copy Makefile.Windows.template from this scenario's solution code to your coding directory. Once you have the file copied to your coding directory, follow the steps outlined below to prepare it for use:
  1. Rename your copy of Makefile.Windows.template to 'Makefile.Windows'.
  2. Open the newly renamed Makefile.Windows.
Replacing all instances of basic_sample in your Makefile.Windows file with RasterSlope will complete steps 3 and 5 through 7 listed below.
  1. Update PROGRAM to be RasterSlope.exe.
  2. Update INCLUDEDIRS macro, which contains the include directories to pass to the compiler, to reflect where you installed ArcGIS Engine.
  3. Update CPPSOURCES to be RasterSlope.cpp.
  4. Update CPPOBJECTS to be RasterSlope.obj.
  5. Update the dependencies line from basic_sample.obj to be for RasterSlope.obj and to depend on RasterSlope.cpp and RasterSlope.h.
You are now ready to compile with nmake. When this scenario directs you to do so, you will need to use the "f" flag to specify the name of the makefile that should be used. At the command line, you will type:
nmake /f Makefile.Windows.
To compile with another development environment, use one of the other makefiles provided with the SDK.

Setting execution parameters

To use the makefile to facilitate running the scenario, the parameters need to be stored within it and a target must be set to run the application. To set up your makefile to do this, you need to update the parameters to match the data you wish to process and the name you want the output to be given.
  1. Near the beginning of Makefile.Windows, find the lines:

          #
          # Command line parameters: Edit these parameters so that you can
          # easily run the sample by typing "nmake /f Makefile.Windows run".
          #
          # You will need to:
          # (1) Describe parameters here. ex: IN_SHAPEFILE is the input shapefile
          # (2) Define parameters below this comment box.
          #       ex: IN_SHAPEFILE = "c:\data\shapefile.shp"
          # (3) Add the parameters to the run target at the end of this file
          #       ex: $(PROGRAM) $(IN_SHAPEFILE)
          #
Equivalent lines will be found in the makefiles for each supported platform.
  1. Below it, add parameters for running this sample. For example, if your input raster is "C:\MyComputer\Rasters\RasterDataset" and your output dataset is going to be named "tempslope", you will add the following lines:

    IN_RASTER = "C:\MyComputer\Rasters\RasterDataset"
    OUT_RASTER = "tempslope"
  1. At the end of Makefile.Windows there is a run target that currently only executes the program. Update that run target to also pass in the input parameters. If you used the variable names IN_RASTER and OUT_RASTER as shown in the example above, the run target should now look as follows:

          #
          # Run target: "nmake /f Makefile.Windows run" to execute the application
          # 
          run:
                    $(PROGRAM) $(IN_RASTER) $(OUT_RASTER)
The command-line build tools of Visual Studio (nmake, cl, link, for example) are not available by default. However, a batch file provided by Microsoft makes them available in Windows. This batch file, called vcvars32.bat, must be run each time you open a new command prompt. You can automate this process by either creating a batch file that runs the Visual Studio 2005 version of vcvars32.bat and opens a command prompt that is ready for development or by using the Visual Studio 2005 Command Prompt, which runs vcvars32.bat for you.
You are now ready to run the application with nmake. When this scenario directs you to do so, you will need to use the "f" flag to specify the name of the makefile that should be used. On a Windows system, you will type:
nmake /f Makefile.Windows run
This will have the same effect as typing "RasterSlope.exe C:\MyComputer\Rasters\RasterDataset tempslope" with command-line arguments at the command line.

Processing the arguments

The user provides the input and output file information for this application at runtime (either through the makefile or at the command line). To get the specifics of that information to use it in the program, some argument processing must be done.
  1. Create a new file, RasterSlope.h, in your text editor. Place the contents of the file in a #ifndef and #define section. Include a standard C++ header file so that information, such as a usage message, can be displayed to the user.

    #ifndef __RASTERSLOPE_ESRISCENARIO_h__
    #define __RASTERSLOPE_ESRISCENARIO_h__
    #include <iostream>
    #endif // __RASTERSLOPE_ESRISCENARIO_h__
  2. In another new file, RasterSlope.cpp, begin implementing your slope application. First include the header file you created in the last step. Then start writing the main function. For now, just process the arguments in it. Make sure the correct number of arguments was entered, else print out a usage message and exit. Since the first argument will be the program name, it can be ignored. The second argument is the input data, and the third is the resulting slope file. You will check if the arguments passed are valid later in the program's execution.
[C++]
          #include
          "RasterSlope.h"
          int main(int argc, char *argv[])
{
  if (argc != 3)
  {
    std::cerr << "Usage: RasterSlope [sourceFile] [outputFile]" << std::endl;
    return 0;
  }
  char *source = argv[1];
  char *result = argv[2];
  return 0;
}
There are three ways to scope members in a namespace. The following are examples of each using cerr, a member of namespace std:
  1. using namespace std;
  2. using std::cerr;
  3. std::cerr << "Prepend namespace";
The third method is used throughout this scenario.
  1. However, you will need to have the pathname and filename of the input in separate locations. To get this information, you will create a new file, in which you will place the path parsing utility functions. Start a new file, PathUtilities.h, and declare a helper function to get the parent directory and another to get the filename.
[C++]
          #ifndef __PATHUTILITIES_ESRISCENARIO_H__
  #define __PATHUTILITIES_ESRISCENARIO_H__

  #include <iostream>
  #include <ArcSDK.h>

  // Extract the shapefile name from the full path of the file.
  HRESULT GetFileFromFullPath(constchar *inFullPath, BSTR *outFileName);

  // Remove the filename from the full path and return the directory.
  HRESULT GetParentDirFromFullPath(constchar *inFullPath, BSTR *outFilePath);

#endif// __PATHUTILITIES_ESRISCENARIO_H__
  1. Implement the path utility functions in the new file PathUtilities.cpp.
[C++]
          #include
          "PathUtilities.h"
          // Function to remove the filename from the full path and return the
          // path to the directory. Caller is responsible for freeing the memory
          // in outFilePath (or pass in a CComBSTR, which has been cast to a BSTR
          // to let the CComBSTR handle memory management).
HRESULT GetParentDirFromFullPath(constchar *inFullPath, BSTR *outFilePath)
{
  if (!inFullPath || !outFilePath)
    return E_POINTER;
  // Initialize output.
  *outFilePath = 0;
  constchar *pathEnd = strrchr(inFullPath, '/'); // UNIXif (pathEnd == 0)
    pathEnd = strrchr(inFullPath, '\\');
  // Windows
  if (pathEnd == 0)
    return E_FAIL;
  int size = strlen(inFullPath) - strlen(pathEnd);
  char *tmp = new char[size + 1];
  strncpy(tmp, inFullPath, size);
  *(tmp + size) = '\0';
  CComBSTR bsTmp(tmp);
  delete [] tmp;
  if (!bsTmp)
    return E_OUTOFMEMORY;
  *outFilePath = bsTmp.Detach();
  return S_OK;
}

// Function to extract the file (or directory) name from the full path
// of the file. Caller is responsible for freeing the memory in
// outFileName (or pass in a CComBSTR, which has been cast to a BSTR
// to let the CComBSTR handle memory management).
HRESULT GetFileFromFullPath(const char *inFullPath, BSTR *outFileName)
{
  if (!inFullPath || !outFileName)
    return E_POINTER;
  *outFileName = 0;
  const char *name = strrchr(inFullPath, '/'); // UNIX
  if (name == 0)
    name = strrchr(inFullPath, '\\');
  // Windowsif (name == 0)
    return E_FAIL;
  name++;
  char *tmp = newchar[strlen(name) + 1];
  strcpy(tmp, name);
  CComBSTR bsTmp(tmp);
  delete [] tmp;
  if (!bsTmp)
    return E_OUTOFMEMORY;
  *outFileName = bsTmp.Detach();
  return S_OK;
}
  1. Use the functions in PathUtilities.h to parse the input.
    1. Update RasterSlope.h to include PathUtilities.h so that you can use the functions you wrote above. You will add this include statement after the one for iostream, as shown below:
[C++]
          #include <iostream>
#include"PathUtilities.h"
    1. In RasterSlope.cpp's main program, parse the input to get the path and the filename. You will add this code after the existing line setting the value of result and before the return line, as shown below:
[C++]
          char *result = argv[2];

// Parse path.
CComBSTR sourceFilePath;
CComBSTR sourceFileName;
HRESULT hr = GetParentDirFromFullPath(source, &sourceFilePath);
if (FAILED(hr) || sourceFilePath.Length() <= 0)
{
  std::cerr << "Couldn't parse source file path." << std::endl;
  return 0;
}

hr = GetFileFromFullPath(source, &sourceFileName);
if (FAILED(hr) || sourceFileName.Length() <= 0)
{
  std::cerr << "Couldn't parse source file name." << std::endl;
  return 0;
}

return 0;
  1. Update the makefile to reflect the new PathUtilities.cpp and PathUtilities.h files, including RasterSlope's dependency on it.
  2. Compile and run the application. It should simply exit and not appear to do anything although it is parsing the arguments.

Accessing ArcGIS Engine

To use ArcGIS Engine, it must be initialized and the proper files included. When done using ArcGIS Engine, it must be uninitialized.
Some of the code included below has already been entered in previous steps. It is given here to illustrate the accurate placement of the code you are adding in this step.
  1. At the top of RasterSlope.h, but below the inclusions for iostream and PathUtilities.h, add an inclusion for ArcSDK.h. It should now appear as follows:
[C++]
          #include <iostream>
#include"PathUtilities.h"#include <ArcSDK.h>
  1. AoExit() must be called before the application is exited. This allows portability to supported operating systems that require AoExit() to correctly clean up various ArcGIS Engine and COM elements. Update RasterSlope.cpp's main to use this function instead of return.
[C++]
          int main(int argc, char *argv[])
{
  if (argc != 3)
  {
    std::cerr << "Usage: RasterSlope [sourceFile] [outputFile]" << std::endl;
    AoExit(0);
    //return 0;
  }
  char *source = argv[1];
  char *result = argv[2];

  /* additional code will be in here */

  {
    std::cerr << "Couldn't parse source file path." << std::endl;
    AoExit(0);
    //return 0;
  }
  hr = GetFileFromFullPath(source, &sourceFileName);
  if (FAILED(hr) || sourceFileName.Length() <= 0)
  {
    std::cerr << "Couldn't parse source file name." << std::endl;
    AoExit(0);
    //return 0;
  }
  AoExit(0);
  //return 0;
}
  1. Write helper functions that initialize and shut down the engine. These are general functions that you can use in any command-line application. In RasterSlope.h, add the following function definitions following the ArcSDK.h include, as shown:
[C++]
          #include <ArcSDK.h>
bool InitializeWithExtension(esriLicenseProductCode product,
  esriLicenseExtensionCode extension);
void ShutdownApp(esriLicenseExtensionCode license);
  1. Implement the functions at the bottom of RasterSlope.cpp as follows:
[C++]
bool InitializeWithExtension(esriLicenseProductCode product,
  esriLicenseExtensionCode extension)
{
  ::AoInitialize(0);
  IAoInitializePtr ipInit(CLSID_AoInitialize);
  esriLicenseStatus licenseStatus = esriLicenseFailure;
  ipInit->IsExtensionCodeAvailable(product, extension, &licenseStatus);
  if (licenseStatus == esriLicenseAvailable)
  {
    ipInit->Initialize(product, &licenseStatus);
    if (licenseStatus == esriLicenseCheckedOut)
      ipInit->CheckOutExtension(extension, &licenseStatus);
  }
  return (licenseStatus == esriLicenseCheckedOut);
}

void ShutdownApp(esriLicenseExtensionCode license)
{
  // Scope ipInit so released before AoUninitialize call
  {
    IAoInitializePtr ipInit(CLSID_AoInitialize);
    esriLicenseStatus status;
    ipInit->CheckInExtension(license, &status);
    ipInit->Shutdown();
  }
  ::AoUninitialize();
}
It appears that a new instance of AoInitialize is created in ShutdownApp(). However, it is a singleton object and so returns a pointer to the AoInitialize object that was previously created.
  1. Command-line applications can be run against any ArcGIS Engine installation—Runtime or developer kit—or any installation of the ArcGIS Desktop products (ArcView, ArcEditor, or ArcInfo). However, this particular application requires a Spatial license in addition to the core license. Depending on the core product license being used, Engine or Desktop, either the Spatial extension for ArcGIS Engine Runtime or an ArcGIS Spatial Analyst extension must also be available. Your application must confirm the availability of, then check out, the necessary licenses as required.

    In RasterSlope.cpp's main(), initialize ArcGIS Engine and set up the licensing for the product. Next, shut down and uninitialize ArcGIS Engine. These lines of code can be placed after the command-line arguments have been processed (as shown below); that part of the application does not need access to ArcGIS Engine and, if the arguments are invalid, there is no reason to start ArcGIS Engine.
Any additional extension functionality must use an extension license that matches the core license being used at that time. If the application initially accesses an ArcGIS Engine Runtime license, it must use the 3D or Spatial extensions for ArcGIS Engine if required. If the application initially accesses an ArcGIS Desktop license (ArcView, ArcEditor, or ArcInfo), it must use ArcGIS 3D Analyst or ArcGIS Spatial Analyst extension licenses if required.
[C++]
          if (FAILED(hr) || sourceFileName.Length() <= 0)
{
  std::cerr << "Couldn't parse source file name." << std::endl;
  AoExit(0);
}

if (!InitializeWithExtension(esriLicenseProductCodeEngine,
  esriLicenseExtensionCodeSpatialAnalyst))
  if (!InitializeWithExtension(esriLicenseProductCodeArcView,
    esriLicenseExtensionCodeSpatialAnalyst))
    if (!InitializeWithExtension(esriLicenseProductCodeArcEditor,
      esriLicenseExtensionCodeSpatialAnalyst))
if (!InitializeWithExtension(esriLicenseProductCodeArcInfo,
  esriLicenseExtensionCodeSpatialAnalyst))
{
  std::cerr << 
    "Exiting Application: Engine Initialization failed. No suitable license found." << std::endl;
  ShutdownApp(esriLicenseExtensionCodeSpatialAnalyst);
  AoExit(0);
}

//This is a placeholder comment to indicate where additional code will be placed later in the exercise.

ShutdownApp(esriLicenseExtensionCodeSpatialAnalyst);

AoExit(0);
  1. Compile the application using the nmake utility as you did earlier in the scenario.
  2. Run the application. It still appears not to do anything; however, it is now performing the license checking.

Computing the slope

At this point, you've determined the dataset on which to compute the slope (from the arguments passed in) and accessed ArcGIS Engine. Now, the slope calculation can be performed. This action is done in a separate function, CalculateSlope(), which is called from the main function.
  1. Place a declaration for CalculateSlope() in RasterSlope.h after the declaration of ShutdownApp. Give it an HRESULT return type so that it can be used for error checking.
[C++]
HRESULT CalculateSlope(BSTR inPath, BSTR inName, BSTR outFile);
  1. After the ShutdownApp() function in RasterSlope.cpp, add the implementation for CalculateSlope(). Place the function only in this step. Upcoming steps will continue to place code into the CalculateSlope() function, unless otherwise indicated.
[C++]
HRESULT CalculateSlope(BSTR inPath, BSTR inName, BSTR outFile){

}
  1. Open the input raster workspace in the CalculateSlope function.
[C++]
HRESULT CalculateSlope(BSTR inPath, BSTR inName, BSTR outFile)
{
  // Open the workspace.
  IWorkspaceFactoryPtr ipWorkspaceFactory(CLSID_RasterWorkspaceFactory);
  IWorkspacePtr ipWorkspace;
  HRESULT hr = ipWorkspaceFactory->OpenFromFile(inPath, 0, &ipWorkspace);
  if (FAILED(hr) || ipWorkspace == 0)
  {
    std::cerr << "Could not open the workspace factory." << std::endl;
    return E_FAIL;
  }
}
  1. Query interface to get access to the raster-specific workspace functionality and open the input raster dataset. This code will be placed at the end of the CalculateSlope function.
[C++]
          // Open the raster dataset.
IRasterWorkspacePtr ipRastWork(ipWorkspace);
IRasterDatasetPtr ipRastDataset;
hr = ipRastWork->OpenRasterDataset(inName, &ipRastDataset);
if (FAILED(hr) || ipRastDataset == 0)
{
  std::cerr << "Could not open the raster dataset." << std::endl;
  return E_FAIL;
}
  1. To perform the slope calculation, use the ISurfaceOp interface's Slope() function. To do this you need to access the ISurfaceOp interface on the workspace. To set up that workspace, query interface to IRasterAnalysisEnvironment. This code will be placed at the end of the CalculateSlope function.
[C++]
          // Set up the ISurfaceOp interface to calculate slope.
IRasterAnalysisEnvironmentPtr ipRastAnalEnv(CLSID_RasterSurfaceOp);
ipRastAnalEnv->putref_OutWorkspace(ipWorkspace);
ISurfaceOpPtr ipSurfOp(ipRastAnalEnv);
  1. You are now ready to perform the slope calculation and return the HRESULT returned by that function to indicate if the calculation was successful. This code will go at the end of the CalculateSlope function.
[C++]
IGeoDatasetPtr ipGeoDataIn(ipRastDataset);
IGeoDatasetPtr ipGeoDataOut;
HRESULT slopeHR = ipSurfOp->Slope(ipGeoDataIn, esriGeoAnalysisSlopeDegrees, 0, 
  &ipGeoDataOut);
if (FAILED(slopeHR) || ipGeoDataOut == 0)
{
  std::cerr << "slopeHR = " << slopeHR << std::endl;
  return slopeHR;
}

return slopeHR;
  1. CalculateSlope() has now been completely implemented and is ready for use. In the main function, after the engine has been initialized, call the CalculateSlope() function. This call should replace the placeholder comment we put in earlier, and will come right before the call to ShutdownApp.
[C++]
hr = CalculateSlope(sourceFilePath, sourceFileName, CComBSTR(result));
if (FAILED(hr))
  std::cerr << "The slope calculation failed." << std::endl;
else
  std::wcerr << L "The slope of " << (BSTR)sourceFileName << L 
    " has been calculated." << std::endl;
  1. Compile the application, then run it. Notice that the output data's path information is never used and that the result of the slope calculation is not stored anywhere.

Persisting the result

When the slope is computed, the result is only created in memory. To save it, you must programmatically persist it to disk.
  1. Since you cannot create a new raster dataset where one already exists, make sure that the output slope file does not exist yet. This can be done in the CalculateSlope() function by trying to open a workspace with the desired name. If such an open is successful, then there is already a dataset with that name.

    To perform this check, place the following code in CalculateSlope(). It should follow the call to OpenRasterDataset() and the if statement that checks the result of opening the dataset.
[C++]
          // Check for existence of a dataset with the desired output name.
IRasterDatasetPtr ipExistsCheck;
hr = ipRastWork->OpenRasterDataset(outFile, &ipExistsCheck);
if (SUCCEEDED(hr))
{
  std::cerr << "A dataset with the output name already exists!" << std::endl;
  return E_FAIL;
}
  1. Once its been determined that no such dataset exists, you can save the one created by the slope operation. The save is done through the IRasterBandCollection interface after the slope is computed and before the slope calculation's HRESULT is returned in CalculateSlope.
[C++]
          // Persist the result.
IRasterBandCollectionPtr ipRastBandColl(ipGeoDataOut);
IDatasetPtr ipOutDataset;
ipRastBandColl->SaveAs(outFile, ipWorkspace, CComBSTR(L "GRID"), &ipOutDataset);
  1. Compile and run the application. Browse to where the slope data was created. A new ESRI grid was generated with your output name.

Deployment

The final part of the development process is your application's successful deployment to an end user's machine. Doing so requires the following:
  • An installation of ArcGIS Engine Runtime with Spatial extension on the user machine.
  • The user's machine will need to have its ArcGIS Engine Runtime initialized.
  • A copy of the application's executable, created at compile time, residing on the end user's machine.
The output slope file is created in the same directory as its parent raster file.
Once these requirements are in place, your end user will be able to create a slope file for any dataset just by typing the following at the command line:
RasterSlope <inputRaster> <outputRaster>
where inputRaster is the full path (including filename) to the raster datafile and outputRaster is the name of the output slope file to be created.
Your end user will have the option of compiling with any supported compiler, as you did during this scenario.
Alternatively, your end user can use the nmake utility to run the sample. To do so, an appropriate makefile with the correct arguments must be made and the following entered at the command line:
nmake /f Makefile.Windows run

Additional References

The following references may help you understand and apply the concepts and techniques presented in this scenario.
  • Additional documentation available in the ArcObjects SDK for Cross Platform C++ including ArcGIS Developer Help, component help, object model diagrams, and samples to help you get started.
  • ESRI Developer Network—Web site providing the most up-to-date information for ArcGIS developers including updated samples and technical documents. Go to http://edn.esri.com.
  • ESRI online discussion forums—Web sites providing invaluable assistance from other ArcGIS developers. Go to http://support.esri.com and click the User Forums tab.
  • Burrough, Peter A., and Rachel A. McDonnell. Principles of Geographical Information Systems. Oxford University Press. 1998.