Developers Knowledge Base

Read articles and post questions in this comprehensive developer community and support forum.
Showing results for 
Search instead for 
Do you mean 

Example 1: Building Spatial Models in C++

by Technical Evangelist ‎10-06-2015 10:41 AM - edited ‎07-18-2016 07:54 AM (1,103 Views)

Spatial Models can be built dynamically in C++ from the Spatial Modeler building blocks:  operators, connections, and data.


This example is located in smNDVIExample.  This example model performs a Normalized Difference Vegetation Index (NDVI) calculation on an input raster file and saves the results in a new raster file.  It shows how to create and run a model using the C++ API by creating operators, making connections, and setting data on ports.


Dissecting Example 1

Look in NDVI.cpp at the top of the file.  The first thing that must be done in any process that is going to use the Spatial Modeler API is to initialize the environment.  This only needs to be done once per process.  Since this particular example is distributed in SMSDK’s bin directory, the SpatialModelerInitializer with no parameter can be used.  If you are initializing the environment from outside the bin directory, you need to supply the SpatialModelerInitializer with the SMSDK’s home directory (default is C:\Program Files\Hexagon\Spatial Modeler SDK 2015).


// Initialize the Spatial Modeler environment.
SpatialModelerInitializer s_Initializer;

Once the environment is initialized, a Model must be created.  This is the object that will store and execute the operators the NDVI model contains.  This is in NDVI::RunProcess().


ModelPtr model = boost::dynamic_pointer_cast<Model>( OperatorFactory::Create( L"IMAGINE",
    L"Model" ) );

The OperatorFactory is used to create a new operator by passing it a namespace and name.  All operators must have a unique {namespace, name} pair.  All the operators shipped with the SDK are located in the “IMAGINE” namespace.  The operator name is always the same as the name in the Spatial Model Editor except with spaces removed (“RasterInput” instead of “Raster Input”).  The newly-created operators must then be added to the model.


OperatorPtr rasterInput = OperatorFactory::Create( L"IMAGINE", L"RasterInput" );
model->AddOperator( rasterInput );
OperatorPtr band1 = OperatorFactory::Create( L"IMAGINE", L"BandSelection" );
model->AddOperator( band1 );
OperatorPtr band2 = OperatorFactory::Create( L"IMAGINE", L"BandSelection" );
model->AddOperator( band2 );
OperatorPtr subtract = OperatorFactory::Create( L"IMAGINE", L"Subtract" );
model->AddOperator( subtract );
OperatorPtr add = OperatorFactory::Create( L"IMAGINE", L"Add" );
model->AddOperator( add );
OperatorPtr divide = OperatorFactory::Create( L"IMAGINE", L"Divide" );
model->AddOperator( divide );
OperatorPtr rasterOutput = OperatorFactory::Create( L"IMAGINE", L"RasterOutput" );
model->AddTail( rasterOutput );

The last operator in the chain must be added as a tail.  This tells the model which operator(s) to request data from, which will pull the processing chain. 


Once all the operators are added to the model, we need to set up the data connections between them.


band1->ConnectParent( rasterInput->GetPort(L"RasterOut"), band1->GetPort(L"RasterIn") );
band2->ConnectParent( rasterInput->GetPort(L"RasterOut"), band2->GetPort(L"RasterIn") );
subtract->ConnectParent( band1->GetPort(L"RasterOut"), subtract->GetPort(L"Input1") );
subtract->ConnectParent( band2->GetPort(L"RasterOut"), subtract->GetPort(L"Input2") );
add->ConnectParent( band1->GetPort(L"RasterOut"), add->GetPort(L"Input1") );
add->ConnectParent( band2->GetPort(L"RasterOut"), add->GetPort(L"Input2") );
divide->ConnectParent( subtract->GetPort(L"Output"), divide->GetPort(L"Input1") );
divide->ConnectParent( add->GetPort(L"Output"), divide->GetPort(L"Input2") );
rasterOutput->ConnectParent( divide->GetPort(L"Output"), rasterOutput->GetPort(L"RasterIn") );

The model now contains the necessary operators and data connections.  This is how it would look in the ERDAS IMAGINE Spatial Modeler Editor (not included in the SMSDK).


SMSDK Example 1 Model.png

The next step is to set the input and output filenames and the band indices for the NIR and red band.


    L"$IMAGINE_HOME/smsdk/examples/smNDVIExample/data/lanier.img" );
rasterInput->GetPort(L"DataType")->SetDataFromString( L"Float" );
band1->GetPort(L"BandRange")->SetDataFromString( L"4" );
band2->GetPort(L"BandRange")->SetDataFromString( L"3" );
    L"$IMAGINE_HOME/smsdk/examples/smNDVIExample/data/ndvi_output.img" );

You can either set data on a port using a specific data type, such as FileData above, or use a string through the SetDataFromString function.  This will work identical to entering a string from the properties pane in the Spatial Modeler Editor.


Now that everything is set up correctly, the model can be run.




Previous article:  Building and Running Spatial Models in C++

Next article:   Example 2: Loading/Saving/Editing a Spatial Model