SourceXtractorPlusPlus
0.14
Please provide a description of the project.
|
In this tutorial, it is assumed that readers have a basic understanding of the architecture of the system, with in particular a knowledge of the on-demand property computation scheme. It may also help to browse additional SExtractor source code when reading this tutorial. PixelCentroid provides a good example for features introduced below.
Note that SExtractor defines all its classes in the namespace SExtractor.
The SExtractorxx source code structure follows the Elements standards. Below a given working directory (e.g., WORKDIR), all code is located in a SExtractorxx top-level directory and all code discussed in this HowTo must be located in the SEImplementation module. The source code root directory is then
$WORKDIR/SExtractorxx/SEImplementation/
Below this root directory, three directories must be created to store all files related to a new source property computation plugin.
As an illustration, the location of the PixelCentroid source property computation plugin source code is:
A source property contains information characterizing a source. It should be possible to compute the property for any sources (but how this can happen is addressed in next section).
A new class implementing the Property.h interface has to be created to define a new property. A dummy "minimum* ExampleProperty class is show below.
The Property.h actually do not include any to-be-implemented interfaces. Implementing this interface is just a way to declare that the class represents a property. A virtual default destructor must always be present.
All information required to fill a new instance of your property must be provided through the constructor (no default constructor and no setter). The property content is to be stored in member variables and Getters should be implemented. In the above example, the property is only one value, in a more general example, the properties can include more than one values, but the principle remains the same.
A property can be fully implemented in the header file as the code should always be quite simple (but this is of course not a requirement).
Once a new property is defined, a corresponding source task can be implemented. It must derive from SourceTask. This class provides a computeProperty(SourceInterface& ) interface, which must be implemented to define how the property is to be computed for a given source. Below is the second part of our minimum example.
The computeProperty parameter is a reference to a SourceInterface. It is both an input and output parameter. Any other source properties required to compute the current property can be obtained from this source (interface) using the source.getProperty<"property_name">()... method. In our example, we retrieve the pixel values. (There is no need to worry about their availability as the on-demand property computation system manage this automatically.)
The full list of existing source properties can be found in this documentation page which also shows which getters can be used to retrieve the property content.
Once the new property content is computed, the new property itself must be created and attached to the source. This is done with
source.setProperty<ExampleProperty>(mean_value);
The setProperty will actually call the constructor of the ExampleProperty class with the mean_value argument, i.e., effectively calling ExampleProperty>(mean_value). Is also directly attached the newly created property to the source. This means that this new property content can then be obtained from the source with a
source.getProperty<ExampleProperty>().getPixelMeanValue ();
The PixelCentroidTask provides a slight more complex example which illutrates how the source pixel coordinate can be retrieved and used. Is also shows that in a typical case, the computeProperties() should be implemented in a a specific location/file separated from the header file.
In a more general case, a source task may produce more than one properties.
The purpose of the task factory is to provide a factory method to create the task itself. In our first minimum example, the task factory code looks quite trivial. Its real role and utility become more meaningful but in more complex examples (see further in this tutorial or real SExtactor examples).
In our case, ExampleTaskFactory extends TaskFactory and implement createTask in the simplest manner. The PropertyId parameter is not used (see further), std::make_shared called the ExampleSourceTask constructor and return a shared pointer to the newly created task.
In this tutorial, we have now implemented a new set of property - task - task factory and it is time to instruct the system about the capabilities of our new code. These instructions are provided through an implementation of the Plugin interface. The implementation of an ExamplePlugin class is displayed below as the last part of our minimum example.
The plugin_api parameter of registerPlugin(PluginAPI& ), supplies methods which can conveniently becalled to performed the required operations. The first is about the relationship between task factory and properties. The method registerTaskFactory<>() is templated, the first parameter is the name of the task factory and the second that of the corresponding property. It tells the system that ExampleTaskFactory (and hence ExampleTask too) can be used to compute ExampleProperties. Note that when a task produces more that one properties, they should simply be listed sidewise, such as __registerTaskFactory<ExampleTaskFactory, ExampleProperty1, ExampleProperty2>().
The second _registerColumnConverter<>()__ method must be used to define how output catalog columns will be derived from the property. The arguments are (1) the name of the column ("pixel_mean_value" in our example) and (2) a function which compute the column value. In our case, the function is a lambda function defined on-the-fly in the method argument. It takes the property as input parameter and returns its value through a call to getPixelMeanValue(). Note that a different, specific _registerColumnConverter<>()__ must be defined for each output catalog column. In general, a property may lead to more than one output columns.
The third method, __.optionalOutput<ExampleProperty>("ExamplePropertyArg")__, declares our ExampleProperty as optional. In other words, the related column will appear in the output catalog only if it is requested by the user with a "--ExamplePropertyArg" command line argument. Other options will be covered later.
A getIdString() must also be implemented to provide a plugin name to the system (here "example_plugin").
Finally, the following statement should appear in the plugin .cpp file. It registers our example plugin as a static plugin (i.e., which must be compiled with SExtractor). Other types of plugin will be described later.