Model data files

You can add support for custom data formats to Lucy, so that it is possible to read and display the data in Lucy. To add basic support for a format, so that you can read and visualize the data, you must provide Lucy with at least two things:

To read data

Add classes to Lucy that can convert a file to an ILcdModel. An ILcdModel is a logical representation of the file data. The conversion is handled by an ILcdModelDecoder. For example, an ILcdModelDecoder could convert the information in a POL file to an ILcdModel containing polygons, lines, and so on.

To visualize data

Add classes to Lucy that can visualize the information contained in your ILcdModel instances. The visualization is handled by an ILcdLayer.

For more information on ILcdModelDecoder instances, ILcdModel instances, ILcdLayer instances and their relationship, see LuciadLightspeed core concepts and Model reading and writing. The best practices sample of Lucy contains a sample implementation of an ILcdModelDecoder that demonstrates the responsibilities of an ILcdModelDecoder.

Working with the Lucy add-ons for model decoding

Lucy comes with all necessary add-ons to add the formats supported by LuciadLightspeed to Lucy. The TLcyDefaultDecodersAddOn adds the formats that are supported by LuciadLightspeed, while the formats that depend on a LuciadLightspeed component, ECW for instance, each have their own add-on. If you want to add support for a custom data format to Lucy, you can register an ILcdModelDecoder that can decode this format with Lucy. ILcyLucyEnv provides these methods for the accounting of ILcdModelDecoder instances, inherited from ILcyModelDecoderContainer:

When you register an ILcdModelDecoder, you can specify an ALcyFileTypeDescriptor. This is a descriptor that provides the user with a high-level description of the file types that can be decoded with the ILcdModelDecoder. When your decoder is not file-based, but reads data from a database for instance, you can specify null as the file type descriptor. You can make your own implementation of ALcyFileTypeDescriptor, or you can use TLcyFileTypeDescriptor and just provide it with a display name and a list of extensions.

The file type descriptors are intended to be used to provide quick filtering of a directory listing for instance. Such a list could contain thousands of files. Therefore, when you implement the includes( String ) method of ALcyFileTypeDescriptor yourself, make sure that this implementation is fast enough. The check should be implemented efficiently: do not try to read a header from a file. This also applies to the ALcyFileTypeDescriptor instances specified when you are registering ALcyDataSourceHandler instances.

If you want to decode a certain source to a ILcdModel in your add-on, you can use the composite model decoder of Lucy. This decoder, which you can retrieve with the getCompositeModelDecoder method of ILcyLucyEnv, will decode the given source name with the first registered ILcdModelDecoder that reports it can decode this source name. Lucy also contains a composite data source handler for the registered ALcyDataSourceHandler instances.

Note that you can override the ILcdInputStreamFactory used to load the data by registering your own in Lucy. You can do this either directly on the Lucy environment, or through the composite factory TLcyCompositeInputStreamFactory. Most built-in formats will use the composite factory to create an InputStream. Notable exceptions are geotiff, jpeg, jai, mrsid and dimap. If you want to write your own ILcdModelDecoder, it is advised to use TLcyCompositeInputStreamFactory as well.

Handling arbitrary non-model data files

Lucy also reads files that do not result in an ILcdModel. As such, you cannot use a ILcdModelDecoder to read these files. A workspace file is one example of this case. To read those kinds of files, Lucy works with ALcyDataSourceHandler objects. An ALcyDataSourceHandler is similar to an ILcdModelDecoder, but instead of decoding the source to a model, a ALcyDataSourceHandler can handle the source any way it wants in the method handleDataSource( String aSourceName, Object aTarget ).

For the accounting of ALcyDataSourceHandler objects, you can call the getCompositeDataSourceHandler() method of TLcyDataFormatManager of ILcyLucyEnv. It provides:

Exporting and saving models

If you want to write model data to the new file format, you need to add an ILcdModelEncoder to Lucy. For this purpose, ILcyLucyEnv is an extension of ILcyModelEncoderContainer. The addition of ILcdModelEncoder instances is similar to the addition of ILcdModelDecoder instances. Lucy uses these encoders when you click the Save as…​ menu item, for instance. Lucy will ask all registered encoders if they can encode the model of the selected layer using the method canEncode( ILcdModel, String ). All the encoders that can encode the model will be presented as a choice in the File type combo box of the file chooser. The user can then select in which format the file should be saved, or in other words, which ILcdModelEncoder should be used. Finally, the model is saved with the selected ILcdModelEncoder to the specified destination name.

Note that you can override the ILcdOutputStreamFactory used to save the data by registering your own in Lucy. You can do this either directly in the Lucy environment, or through the composite factory TLcyCompositeOutputStreamFactory. Most built-in formats will use the composite factory to create an OutputStream. If you want to write your own ILcdModelEncoder, it is advised to use TLcyCompositeOutputStreamFactory as well.

Working with file type filters

When Lucy users open a file with the File→ Open menu item, the file type filter mechanism gives them the possibility to choose which files they want to see. The corresponding drop-down box contains a list of group filters, followed by a sorted list of single-typed filters. The single-typed filters represent the ALcyFileTypeDescriptor instances registered together with the ILcdModelDecoder instances and ALcyDataSourceHandler instances. For a discussion about how these are registered, see Model data files and Handling arbitrary non-model data files. The group filters will combine a number of single-file-type filters. Lucy always contains the "All Known Files" filter, which represents all registered single-file-type filters. Some add-ons, such as the TLcyDefaultDecodersAddOn, will register the group filter "All Vector Files" or "All Raster Files", representing all vector or all raster files. You can add other groups using getCompositeFileTypeDescriptorGroup() on the TLcyDataFormatManager of ILcyLucyEnv. The best practices sample of Lucy shows how you can do that.

Opening a file in Lucy

The TLcyDataFormatManager class contains two methods called handleDataSources, which are used when a file is opened in Lucy:

  • If handleDataSources is invoked without ILcdModelDecoder[] and ALcyDataSourceHandler[] arrays, it goes over all registered ILcdModelDecoder instances and asks each of them if they can decode a source with a particular name, using the method canDecodeSource( String ). Lucy also uses the method canHandleDataSource( String aSourceName, Object aTarget ) on all registered ALcyDataSourceHandler instances to ask them if they can handle the file. If multiple decoders/handlers report that they can decode/handle the source name, Lucy lets the user select the decoder or handler. The selected ILcdModelDecoder or ALcyDataSourceHandler then decodes or handles the specified source name.

    You can replace this selection logic by setting a new TLcyDataFormatManager.ALcyHandlerChooser to TLcyDataFormatManager. See the API reference for more details.

    When an ILcdModelDecoder has been selected, a layer is created and added to the map so that the data is visualized.

  • The second handleDataSources method allows you to specify which ILcdModelDecoder and ALcyDataSourceHandler objects should be used instead of all the registered ones.

If certain files fail to open on the map component specified in the call of handleDataSources, an TLcyDataFormatManager.ALcyMapComponentChooser will be used to choose another map component to open the files on.

Registering an ILcyDataSourceListener to listen for file handling events

If a user opens multiple files, error messages should pop up once for each handled group of files. The model reference chooser has an OK for All button to allow the user to choose a model reference once for a group of files. To accomplish this, ILcyDataSourceListener instances are registered to ILcyLucyEnv.getDataFormatManager(). Currently they can listen to two events:

  • A TLcyDataSourceEvent with ID TLcyDataSourceEvent.HANDLING_STARTED, fired before the handling.

  • A TLcyDataSourceEvent with ID TLcyDataSourceEvent.HANDLING_ENDED, fired after the list of data sources is handled.

As intended, listeners can collect error messages between the two events, for example, and can show a combined message when the HANDLING_ENDED event is received. If no HANDLING_STARTED event is fired, the listeners should display a pop-up error message for each problem.

Registering and using ILcdModelHeightProviderFactory for height data

If a format contains height data, you must register an ILcdModelHeightProviderFactory with the ILcyLucyEnv. You can do this through the method ILcyLucyEnv.addService(). Lucy then uses these registered height provider factories to retrieve height values. When you are implementing new functionality in Lucy that uses height data, you should use these registered factories. This is demonstrated in the following code:

Program: A list of registered ILcdModelHeightProviderFactory instances is used to create a height provider.
//Create a composite factory based on all ILcdModelHeightProviderFactory instances
//registered on the Lucy backend
TLcyCompositeModelHeightProviderFactory compositeFactory =
    new TLcyCompositeModelHeightProviderFactory(lucyEnv);

// Create a set of required and optional properties.
Map<String, Object> requiredProperties = new HashMap<>();
requiredProperties.put(ALcdModelHeightProviderFactory.KEY_GEO_REFERENCE, reference);
Map<String, Object> optionalProperties = new HashMap<>();

// Create a height provider using the composite height provider factory.
ILcdHeightProvider heightProvider =
    compositeFactory.createHeightProvider(model, requiredProperties, optionalProperties);

A useful class for registering and using ILcdModelHeightProviderFactory instances is TLcyCompositeModelHeightProviderFactory. Create an instance of this class using an ILcyLucyEnv, and register height provider factories by adding them with TLcyCompositeModelHeightProviderFactory.addModelHeightProviderFactory(). It then serves as a composite height provider factory that uses all registered ILcdModelHeightProviderFactory instances registered in the ILcyLucyEnv.

To retrieve height data from a view, use the class TLcyViewHeightProvider. That class uses the registered height provider factories in the ILcyLucyEnv to create a composite height provider from all models in the view that provide height data. It uses an instance of TLcyCompositeModelHeightProviderFactory internally to do this.

Lightspeed views have a built-in height provider, which you can retrieve from the terrain support functionality of such a view , through ILspView.getTerrainSupport. See Visualizing terrain elevation in a Lightspeed view for more information.