Prerequisites
This tutorial assumes you are familiar with:
-
The fundamental LuciadLightspeed concepts, such as
ILcdModel
,ILspLayer
andILspView
. -
How to support a custom vector format in a non-Lucy-based application
Goal
This tutorial shows you how you add support for static, non-editable business data to a Lucy application using a Lightspeed view.
The custom data format used in this tutorial is the same waypoints format as introduced in the Support a custom vector format tutorial of LuciadLightspeed.
Adding support for a custom data format to Lucy requires a number of Lucy-specific steps that are not required in a non-Lucy application. In return however, you get a lot of functionality for free. For example, if you follow these steps, you can:
-
Drag-and-drop the data on the map
-
Use the File | Open menu item to load your data
-
Open the table view for your data
-
Customize the styling through the layer properties panel
Adding support to decode the data
The recommended way to add support for decoding data is to:
-
Create an
ALcyFormat
class: theALcyFormat
is a class that groups everything that Lucy needs to decode and handle models, and optionally to visualize them on a GXY view. -
Create an
ALcyAddOn
that registers everything theALcyFormat
creates as Lucy services: for example the File | Open action will query the services for all available model decoders and layer factories. By registering our instances as services, we ensure that they are picked up automatically.
Creating the ALcyFormat
As our data format is file-based, we start from one of the available ALcyFormat
extensions for the creation of our ALcyFormat
:
package com.luciad.lucy.addons.tutorial.staticlspdata.model;
class WayPointModelFormat extends ALcyFileFormat {
WayPointModelFormat(ILcyLucyEnv aLucyEnv, String aLongPrefix, String aShortPrefix, ALcyProperties aProperties) {
super(aLucyEnv, aLongPrefix, aShortPrefix, aProperties);
}
}
The ALcyFileFormat
allows us to configure certain items in the configuration file instead of in code.
We will create the configuration file later on in this tutorial.
Create the model decoder
For our model decoder, we re-use the model decoder created in the How to support a custom vector format tutorial.
This model decoder is returned from the createModelDecoders
method in our ALcyFormat
:
@Override
protected ILcdModelDecoder[] createModelDecoders() {
return new ILcdModelDecoder[]{new WayPointsModelDecoder()};
}
Creating the model content type provider
Lucy tries to determine the best initial position for a layer when adding new data to a map. This requires knowledge about the kind of data the layer and model represent.
One of the interfaces used in this process is the ILcyModelContentTypeProvider
interface, which indicates what kind of data a model contains.
See How to influence the initial layer position in the view for more information.
The ALcyFormat
has a method to create this model content type provider:
@Override
protected ILcyModelContentTypeProvider createModelContentTypeProvider() {
//All our models only contain point data, so we can return a fixed type
//No need to check the contents of the model
return aModel -> ILcyModelContentType.POINT;
}
As documented in the Our implementation currently does not do that. We will take care of that later on in this tutorial. |
Disable GXY layer creation support
In this tutorial, we are only interested in supporting our data on a Lightspeed view.
The ALcyFormat
class has abstract methods related to GXY layer creation, however.
We need to implement these methods, but can simply return null
:
@Override
protected ILcyGXYLayerTypeProvider createGXYLayerTypeProvider() {
return null;
}
@Override
protected ILcdGXYLayerFactory createGXYLayerFactory() {
return null;
}
Adding an ALcyAddOn to plug in the ALcyFormat
If you are coding along with this tutorial, you’ll notice that at this point our This missing method will be implemented later on. |
Creating the ALcyAddOn
Because we are creating our own functionality and adding it to Lucy, we need to create our own Lucy add-on. A Lucy add-on
is an extension of ALcyAddOn
.
One of the benefits of using an ALcyFormat
in the previous step is that our add-on can be an extension of
ALcyFormatAddOn
. The format add-on:
-
Uses a configuration file for configurable settings.
-
Makes it easy to register all the objects created in the
ALcyFormat
, such as the model decoder, as a service.
package com.luciad.lucy.addons.tutorial.staticlspdata.model;
public class WayPointModelAddOn extends ALcyFormatAddOn {
public WayPointModelAddOn() {
super(ALcyTool.getLongPrefix(WayPointModelAddOn.class),
ALcyTool.getShortPrefix(WayPointModelAddOn.class));
}
}
The prefixes we need to pass to the super
constructor are:
-
A long prefix, which is used to generate unique IDs
-
A short prefix, which is the prefix used in the configuration file of the add-on
The add-on needs to create the ALcyFormat
so that it can plug in everything, including the model decoder and model content type provider we created:
@Override
protected ALcyFormat createBaseFormat() {
return new WayPointModelFormat(getLucyEnv(),
getLongPrefix(),
getShortPrefix(),
getPreferences());
}
We also pass the parsed version of the configuration file to the format with getPreferences()
, and we pass the prefix used in the configuration file to the format with getShortPrefix
.
This allows the format to read settings from the configuration file.
The last method we need to implement is the createFormatWrapper
method.
The Lucy API contains a number of decorators or wrappers for an ALcyFormat
, which makes it easy to re-use functionality.
You can find all available decorators in the Javadoc by looking for the available extensions of ALcyFormatWrapper
.
In this tutorial, we are going to use the TLcySafeGuardFormatWrapper
:
this wrapper ensures that the instances created in the base ALcyFormat
are called only with the correct models and layers.
For example, the ILcyModelContentTypeProvider
we created currently violates the contract because it indicates that all models contain point data.
Instead, it should indicate that our waypoint models contain point data, and that it does not know what kind of data other
models contain.
By decorating our format with a TLcySafeGuardFormatWrapper
, our ILcyModelContentTypeProvider
will only be called for waypoint models.
For this to work, the TLcySafeGuardFormatWrapper
needs to know which models belong to our format, and which do not.
This information is provided by the last method we still need to implement in our ALcyFormat
:
@Override
public boolean isModelOfFormat(ILcdModel aModel) {
//All the waypoint models created by our model decoder have CWP as type name
//We assume here that this typename is unique over all supported formats
return "CWP".equals(aModel.getModelDescriptor().getTypeName());
}
We add the safeguard wrapper in the add-on:
@Override
protected ALcyFormat createFormatWrapper(ALcyFormat aBaseFormat) {
return new TLcySafeGuardFormatWrapper(aBaseFormat);
}
Creating the configuration file for the add-on
Our add-on requires a configuration file. The ALcyFileFormat
we used reads some properties from that configuration, as documented in the class Javadoc.
WayPointModelAddOn.fileTypeDescriptor.displayName=Waypoint files
WayPointModelAddOn.fileTypeDescriptor.defaultExtension=cwp
WayPointModelAddOn.fileTypeDescriptor.filters=*.cwp
WayPointModelAddOn.fileTypeDescriptor.groupIDs=All Vector Files
Adding support to visualize the data
In the previous section, we used an ALcyFormat
instance to add support for the model part of adding data to a Lightspeed view.
The ALcyFormat
class offers no methods for the creation of an ILspLayerFactory
however. We need a layer factory for the visualization on a Lightspeed view.
For that, we use an ALcyLspFormat
instance.
The ALcyLspFormat
is conceptually the same as an ALcyFormat
, but deals with Lightspeed layers only.
Similar to what we did with the ALcyFormat
, we will:
-
Create an
ALcyLspFormat
by starting from the most suitable extension, and customize where needed. In this case, we have regular vector data and don’t require any custom visualization. As such, we can useTLcyLspVectorFormat
directly. We don’t need to create our ownALcyLspFormat
extension. -
Create an add-on to plug in the
ALcyLspFormat
. Now we will use anALcyLspFormatAddOn
, which is the Lightspeed counterpart ofALcyFormatAddOn
. -
Use a
TLcyLspSafeGuardFormatWrapper
to ensure that our layer factory is only called with waypoint models.
package com.luciad.lucy.addons.tutorial.staticlspdata;
import com.luciad.lucy.addons.lightspeed.ALcyLspFormatAddOn;
import com.luciad.lucy.format.lightspeed.ALcyLspFormat;
import com.luciad.lucy.format.lightspeed.TLcyLspSafeGuardFormatWrapper;
import com.luciad.lucy.format.lightspeed.TLcyLspVectorFormat;
import com.luciad.lucy.util.ALcyTool;
import com.luciad.model.ILcdModel;
import com.luciad.util.ILcdFilter;
public class WayPointAddOn extends ALcyLspFormatAddOn {
public WayPointAddOn() {
super(ALcyTool.getLongPrefix(WayPointAddOn.class),
ALcyTool.getShortPrefix(WayPointAddOn.class));
}
@Override
protected ALcyLspFormat createBaseFormat() {
ILcdFilter<ILcdModel> modelFilter =
(ILcdFilter<ILcdModel>) aModel -> "CWP".equals(aModel.getModelDescriptor().getTypeName());
return new TLcyLspVectorFormat(getLucyEnv(),
getLongPrefix(),
getShortPrefix(),
getPreferences(),
modelFilter);
}
@Override
protected ALcyLspFormat createFormatWrapper(ALcyLspFormat aBaseFormat) {
return new TLcyLspSafeGuardFormatWrapper(aBaseFormat);
}
}
This add-on also requires a configuration file.
Consult the class Javadoc of ALcyLspStyleFormat, the parent class of TLcyLspVectorFormat
,
for more information about the available configuration options.
WayPointAddOn.style.fileTypeDescriptor.displayName=Style Files
WayPointAddOn.style.fileTypeDescriptor.defaultExtension=sty
WayPointAddOn.style.fileTypeDescriptor.filters=*.sty
WayPointAddOn.style.fileTypeDescriptor.groupIDs=All Style Files
WayPointAddOn.defaultStyleFile=docs/articles/tutorial/lucy/customdata/defaultWaypointStyle.sty
This configuration file points to a default style file.
The TLcyLspVectorFormat
allows you to:
-
Customize the styling of your data through the layer properties panel, and save that styling
-
Use a saved style as the default style for new layers
Plugging in the add-ons
Plugging in the add-ons requires the creation of a custom add-ons file where we include our add-ons:
<?xml version="1.0" encoding="UTF-8"?>
<addonConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="config/lucy/addons.xsd">
<addons>
<!-- Include all the original add-ons -->
<include>lucy/addons.xml</include>
<!-- Add our own add-ons for waypoint data -->
<addon>
<priority>data_producer</priority>
<name>Waypoint model format</name>
<class>com.luciad.lucy.addons.tutorial.staticlspdata.model.WayPointModelAddOn</class>
<configFile>docs/articles/tutorial/lucy/customdata/WayPointModelAddOn.cfg</configFile>
</addon>
<addon>
<priority>data_producer</priority>
<name>Waypoint format</name>
<class>com.luciad.lucy.addons.tutorial.staticlspdata.WayPointAddOn</class>
<configFile>docs/articles/tutorial/lucy/customdata/WayPointAddOn.cfg</configFile>
</addon>
</addons>
</addonConfiguration>
See How to customize the add-ons used by your application for more information about using custom add-ons.
Taking advantage of other functionality that just works
Lucy comes bundled with a lot of default functionality. Now that we added support for the waypoint data files, we get a lot of extra functionality for free:
-
Integration with the table view: you can view the waypoint data in a tabular view. Just right-click on the layer and choose the Table view option. This functionality is provided by the
TLcyLspToteAddOn
. -
Object properties: double-click on a waypoint to open an object properties view. It displays properties that are based on the data model of the waypoint.
-
Adjustable layer styling: you can use
TLcyLspVectorFormat
and load theTLcyLspLayerCustomizerAddOn
to adjust the layer styling through the UI. To see such a styling UI, right-click a layer in the layer control panel, and choose the Properties action. -
Workspace support: a Lucy workspace is a file containing all the information about a state of your Lucy application: which files were loaded, which panels were open, how your map was fitted, and so on. You can create such a workspace file from File | Save workspace as. To load the file afterwards, go to File | Load workspace.
Because we used
ALcyFileFormat
andTLcyLspVectorFormat
, we automatically gained workspace support for our own data. You can see workspace support in action by saving a workspace containing waypoint data, and loading the workspace file again. As a result, the flight plan data is restored, including any modifications made to the styling.
In this tutorial, it is assumed that the model data is loaded from a file.
See the ""Generated data" sample Examples of such models are models that are the result of some computation, the result of some web service request, or a connection to a live feed of tracks. |
Full code
The WayPointModelAddOn
code
package com.luciad.lucy.addons.tutorial.staticlspdata.model;
import com.luciad.lucy.addons.ALcyFormatAddOn;
import com.luciad.lucy.format.ALcyFormat;
import com.luciad.lucy.format.TLcySafeGuardFormatWrapper;
import com.luciad.lucy.util.ALcyTool;
public class WayPointModelAddOn extends ALcyFormatAddOn {
public WayPointModelAddOn() {
super(ALcyTool.getLongPrefix(WayPointModelAddOn.class),
ALcyTool.getShortPrefix(WayPointModelAddOn.class));
}
@Override
protected ALcyFormat createBaseFormat() {
return new WayPointModelFormat(getLucyEnv(),
getLongPrefix(),
getShortPrefix(),
getPreferences());
}
@Override
protected ALcyFormat createFormatWrapper(ALcyFormat aBaseFormat) {
return new TLcySafeGuardFormatWrapper(aBaseFormat);
}
}
The WayPointModelFormat
code
package com.luciad.lucy.addons.tutorial.staticlspdata.model;
import com.luciad.lucy.ILcyLucyEnv;
import com.luciad.lucy.format.ALcyFileFormat;
import com.luciad.lucy.map.ILcyGXYLayerTypeProvider;
import com.luciad.lucy.model.ILcyModelContentType;
import com.luciad.lucy.model.ILcyModelContentTypeProvider;
import com.luciad.lucy.util.properties.ALcyProperties;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelDecoder;
import com.luciad.model.tutorial.customvector.WayPointsModelDecoder;
import com.luciad.view.gxy.ILcdGXYLayerFactory;
class WayPointModelFormat extends ALcyFileFormat {
WayPointModelFormat(ILcyLucyEnv aLucyEnv, String aLongPrefix, String aShortPrefix, ALcyProperties aProperties) {
super(aLucyEnv, aLongPrefix, aShortPrefix, aProperties);
}
@Override
protected ILcdModelDecoder[] createModelDecoders() {
return new ILcdModelDecoder[]{new WayPointsModelDecoder()};
}
@Override
protected ILcyModelContentTypeProvider createModelContentTypeProvider() {
//All our models only contain point data, so we can return a fixed type
//No need to check the contents of the model
return aModel -> ILcyModelContentType.POINT;
}
@Override
protected ILcyGXYLayerTypeProvider createGXYLayerTypeProvider() {
return null;
}
@Override
protected ILcdGXYLayerFactory createGXYLayerFactory() {
return null;
}
@Override
public boolean isModelOfFormat(ILcdModel aModel) {
//All the waypoint models created by our model decoder have CWP as type name
//We assume here that this typename is unique over all supported formats
return "CWP".equals(aModel.getModelDescriptor().getTypeName());
}
}
The configuration file for the WayPointModelAddOn
WayPointModelAddOn.fileTypeDescriptor.displayName=Waypoint files
WayPointModelAddOn.fileTypeDescriptor.defaultExtension=cwp
WayPointModelAddOn.fileTypeDescriptor.filters=*.cwp
WayPointModelAddOn.fileTypeDescriptor.groupIDs=All Vector Files
The WayPointAddOn
code
package com.luciad.lucy.addons.tutorial.staticlspdata;
import com.luciad.lucy.addons.lightspeed.ALcyLspFormatAddOn;
import com.luciad.lucy.format.lightspeed.ALcyLspFormat;
import com.luciad.lucy.format.lightspeed.TLcyLspSafeGuardFormatWrapper;
import com.luciad.lucy.format.lightspeed.TLcyLspVectorFormat;
import com.luciad.lucy.util.ALcyTool;
import com.luciad.model.ILcdModel;
import com.luciad.util.ILcdFilter;
public class WayPointAddOn extends ALcyLspFormatAddOn {
public WayPointAddOn() {
super(ALcyTool.getLongPrefix(WayPointAddOn.class),
ALcyTool.getShortPrefix(WayPointAddOn.class));
}
@Override
protected ALcyLspFormat createBaseFormat() {
ILcdFilter<ILcdModel> modelFilter =
(ILcdFilter<ILcdModel>) aModel -> "CWP".equals(aModel.getModelDescriptor().getTypeName());
return new TLcyLspVectorFormat(getLucyEnv(),
getLongPrefix(),
getShortPrefix(),
getPreferences(),
modelFilter);
}
@Override
protected ALcyLspFormat createFormatWrapper(ALcyLspFormat aBaseFormat) {
return new TLcyLspSafeGuardFormatWrapper(aBaseFormat);
}
}
The configuration file for the WayPointAddOn
WayPointAddOn.style.fileTypeDescriptor.displayName=Style Files
WayPointAddOn.style.fileTypeDescriptor.defaultExtension=sty
WayPointAddOn.style.fileTypeDescriptor.filters=*.sty
WayPointAddOn.style.fileTypeDescriptor.groupIDs=All Style Files
WayPointAddOn.defaultStyleFile=docs/articles/tutorial/lucy/customdata/defaultWaypointStyle.sty
The custom addons.xml
file
<?xml version="1.0" encoding="UTF-8"?>
<addonConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="config/lucy/addons.xsd">
<addons>
<!-- Include all the original add-ons -->
<include>lucy/addons.xml</include>
<!-- Add our own add-ons for waypoint data -->
<addon>
<priority>data_producer</priority>
<name>Waypoint model format</name>
<class>com.luciad.lucy.addons.tutorial.staticlspdata.model.WayPointModelAddOn</class>
<configFile>docs/articles/tutorial/lucy/customdata/WayPointModelAddOn.cfg</configFile>
</addon>
<addon>
<priority>data_producer</priority>
<name>Waypoint format</name>
<class>com.luciad.lucy.addons.tutorial.staticlspdata.WayPointAddOn</class>
<configFile>docs/articles/tutorial/lucy/customdata/WayPointAddOn.cfg</configFile>
</addon>
</addons>
</addonConfiguration>