This article explains how to modify the functionality of the map add-on.
Customizing maps
This section describes how you can modify the layout of the map component using a GUI factory. This includes adding and removing items in the tool bar and menu bar, changing existing items and putting parts of the map, the tool bar for example, in a different location.
Note that the factory also allows you to customize more advanced, not directly GUI-related functionality, such as the layer
ordering algorithm.
See to the API reference of TLcyMapComponentFactory
for more details.
Concept
The map add-on provides the TLcyMapComponentFactory
class, an extension of ALcyMapComponentFactory
. The map component is created by TLcyMapComponentFactory
according to the principles of a GUI factory. This means that the GUI, a TLcyMapComponent
in this case, is created in small, customizable parts. The TLcyMapComponentFactory
has a number of method pairs, one to create a component, such as a toolbar or the view, and one to insert this component
in the TLcyMapComponent
. These are protected methods to allow extended classes to override the default creation or insertion behavior. Overriding
creation behavior allows you to change, replace or remove the default component, while overriding insertion behavior allows
you to change the layout of the component. These components are customizable in TLcyMapComponentFactory
using the protected methods:
-
Tool bar: there is a
ILcyToolBar createToolBar( TLcyMapComponent )
method and avoid setToolBar( ILcyToolBar, TLcyMapComponent )
method. The first method serves the purpose of creating the toolbar, the second method is responsible for inserting the toolbar into the map component. -
Menu bar:
createMenuBar
andsetMenuBar
handle the menu. Note that the items of the menu bar of the map are merged into the main menu by default. -
Popup menu:
createPopupMenu
andsetPopupMenu
take care of the popup menu. The popup menu is displayed by default if the select controller is active and the user right-clicks on the map. -
GXY view: The
ILcdGXYView
is handled bycreateGXYView
andsetGXYView
. -
Controllers: use a generic
createGXYController
andinsertGXYController
method pair with an extra ID parameter. The ID describes which controller it is, for example the zoom controller or the pan controller. All possible IDs that can be passed to thecreateGXYController
andinsertGXYController
methods, end with the same suffixCONTROLLER
. -
Actions:
createAction
andinsertAction
take care of the actions. The given ID is one of the constants ending inACTION
. -
Active settables:
createActiveSettable
andinsertActiveSettable
use a constant ending inACTIVE_SETTABLE
as their ID. -
Layers:
createGXYLayer
andinsertGXYLayer
take care of the grid and area-of-interest layers for example. The ID has to end inLAYER
. -
Other components: use a generic
createComponent
andinsertComponent
method pair with an extra ID parameter. The ID describes which component it is, for example the map scale label or the layer control. All possible IDs that can be passed to thecreateComponent
/insertComponent
methods, end in the suffixCOMPONENT
. The layer control is special in that the actual creation is delegated to aTLcyMapLayerControlFactory
(can be set usingsetMapLayerControlFactory
), and the same GUI factory principle applies to the customization of the parts of the layer control.
For example, imagine that you want to change the behavior of the edit-current-projection action. The Javadoc of TLcyMapComponentFactory
shows that there is an
EDIT_CURRENT_PROJECTION_ACTION
constant. Because it ends in
ACTION
, we can pass this constant to the createAction
method only. Therefore, we can override the method createAction
, check if the ID is
EDIT_CURRENT_PROJECTION_ACTION
and return a custom action instance. We could also
use super.createAction
and change the icon before returning it. If you want
to remove the action completely, simply return null.
Example
All this is illustrated in the GUI factory sample, in samples/lucy/gxy/custommapui
.
The add-ons file of this sample, addons_gui_factory_sample.xml
, shows that the map add-on is configured through a custom configuration file, namely lucy_gui_factory_sample_map_addon.cfg
. In this configuration file, the property mapComponentFactoryName
is set to samples.lucy.gxy.custommapui.MapComponentFactory
to make this factory create the map components instead of the default one. The MapComponentFactory
is an extension of TLcyMapComponentFactory
that changes the icon of the file open action, removes the ruler controller, and puts the tool bar below the map. The scale
combo box is no longer inside the tool bar, but on the right side.
To change the icon of the open action, you must identify where this action is created.
You can also configure the icon by setting
|
The Javadoc of TLcyMapComponent
shows that there is an OPEN_FILE_ACTION
constant. This constant ends in ACTION
, so it is being used by the createAction
method. That means that you need to re-define the createAction
method to modify the behavior. This is shown in Program: This snippet shows how to modify the open action.. The ID
is used to decide if the action currently being created is the open action, or some other action. If some other action is
created, the behavior of the method of the superclass is left untouched. If the action being created is the open action,
the icon is modified by using action.putValue( ILcdAction.SMALL_ICON, new_icon )
on the action instance.
samples/lucy/gxy/custommapui/MapComponentFactory
)
@Override
protected ILcdAction createAction(int aID, TLcyMapComponent aMapComponent) {
ILcdAction action = super.createAction(aID, aMapComponent);
//Modify the icon of the open action
if (aID == OPEN_FILE_ACTION && action != null) {
ILcdIcon new_icon = TLcdIconFactory.create(TLcdIconFactory.GLOBE_ICON);
action.putValue(ILcdAction.SMALL_ICON, new_icon);
}
return action;
}
Removing the ruler controller is similar.
To remove the ruler, you can also configure the properties |
First, the constant RULER_CONTROLLER
needs to be identified, which leads us to the createGXYController
method. Using the ID
to detect when the ruler controller needs to be created, we return null
instead of the ruler controller. This is demonstrated in Program: This snippet shows how to remove the ruler controller..
samples/lucy/gxy/custommapui/MapComponentFactory
)
@Override
protected ILcdGXYController createGXYController(
int aID,
TLcyMapComponent tLcyMapComponent) {
//Remove the ruler controller
if (aID == RULER_CONTROLLER) {
return null;
}
//leave all other controllers untouched
else {
return super.createGXYController(aID, tLcyMapComponent);
}
}
The tool bar is created in the createToolBar
method and installed in the setToolBar
method.
To move the tool bar, you do not need to modify it, so the createToolBar
method is left untouched.
The setToolBar
method is re-defined to insert the tool bar at a different location. See Program: This code fragment shows how to move the toolbar to a different location.
Note that it is also necessary to call setToolBar
on the TLcyMapComponent
itself because it gives the TLcyMapComponent
a reference to the toolbar instance it is using.
In addition, the toolbar must be inserted into the GUI of the TLcyMapComponent
, using Swing.
samples/lucy/gxy/custommapui/MapComponentFactory
)
@Override
protected void setToolBar(ILcyToolBar aToolBar, TLcyMapComponent aMapComponentSFCT) {
//tell the map component about the toolbar it should use
aMapComponentSFCT.setToolBar(aToolBar);
aMapComponentSFCT.setToolBarComponent(aToolBar.getComponent());
//add the toolbar (using swing) in a different location
Container south_content_pane = aMapComponentSFCT.getSouthPanel();
if (south_content_pane != null) {
south_content_pane.add(aToolBar.getComponent(), BorderLayout.WEST);
}
}
samples/lucy/gxy/custommapui/MapComponentFactory
)
@Override
protected void insertComponent(int aID, Component aComponent, TLcyMapComponent aMapComponentSFCT) {
if (aID == MAP_SCALE_LABEL_COMPONENT) {
//insert the map scale label at a different place, using swing
if (aComponent != null) {
fRightToolBar.insertComponent(aComponent, new TLcyGroupDescriptor("ScaleGroup"));
}
} else {
super.insertComponent(aID, aComponent, aMapComponentSFCT);
}
}
After moving the tool bar below the map, you will notice that the scale label is still inside the toolbar. To move the scale label to the far right of the tool bar, the scale label is inserted into the right tool bar instead of in the regular tool bar. This is illustrated in Program: This code fragment shows how to move the scale label to a different location.. The right tool bar is inserted underneath the map, similar to the regular toolbar. This is demonstrated in Program: This code fragment shows how to move the right toolbar to a different location..
samples/lucy/gxy/custommapui/MapComponentFactory
)
@Override
protected void setRightToolBar(ILcyToolBar aRightToolBar, TLcyMapComponent aMapComponentSFCT) {
//add the toolbar (using swing) in a different location
Container south_content_pane = aMapComponentSFCT.getSouthPanel();
if (south_content_pane != null) {
south_content_pane.add(aRightToolBar.getComponent(), BorderLayout.EAST);
}
}
Extending opening and saving of files
The File → Open action opens a file by passing it to the TLcyDataFormatManager
.
The File→ Save action saves the ILcdModel
of an ILcdGXYLayer
of choice. The ILcdModelEncoder
of the ILcdModel
is used for this purpose. See ILcdModel.getModelEncoder()
. So if custom ILcdModel
instances need to be saved, they must have an associated ILcdModelEncoder
.
The File→ Save As action saves the ILcdModel
of an ILcdGXYLayer
of choice, but it uses ILcyLucyEnv.getDataFormatManager().getCompositeModelEncoder().canExport( model, destinationFileName )
to find out if a model can be saved. So if custom ILcdModel
instances need to be saved, the appropriate ILcdModelEncoder
instances must be registered using ILcyLucyEnv.getDataFormatManager().addModelEncoder( encoder, fileType )
.
Snapping
The LuciadLightspeed API provides quite an extensive mechanism for snapping. Part of this mechanism entails that you need
to add your domain objects to a 'snappables' list that can be used by the editing controllers, so that these objects can be
evaluated as snap target candidates. Lucy provides a mechanism to automatically maintain such a list per map component that
contains the visible and relevant domain objects of the model in the visible layers. If you want to make use of this mechanism,
your layer should implement the ILcySnappable
interface. The best practices sample contains an implementation of such a layer. When a layer implements this interface and the isSnappingOn
method returns true, all its relevant objects are added to the snappables list maintained by Lucy. This list can be retrieved
using the method getSnapList
of the ILcyMapComponent
. Amongst others, the Lucy edit controller makes use of this list. If you implement your own controller and it needs a snap
list, you can use this list.
Note that this mechanism just adds the objects in your layers to this central snap list. This mechanism does not take care of deriving an actual snap target from one of these objects, nor does it provide the visual indication of the accepted snap targets. This support needs to be included in the painter. Most painters in LuciadLightspeed support this mechanism. See Implementing a painter and editor in a GXY view for more information about writing custom painters that support snapping.
Workspace considerations
Required object codecs
The map add-on stores the existing map components and the layers that are added to those maps. As such, it needs to encode
references to map components and GXY layers. The object codecs that can save references to layers are specific to the data
format and are therefore added by the data add-ons that come with Lucy. When you write an add-on that adds support for a custom
format, you must write your own implementation of ALcyWorkspaceObjectCodec
that can encode the state of a layer.
The map add-on itself adds an object codec that is able to encode the state of the default map components, but if you have written your own map component factory that returns another implementation, you need to provide a custom object codec for that.
Registered object codecs
The map add-on adds object codecs to the Lucy back-end for the following objects:
-
Map components
-
Views
-
Layer controls
The object codec for map components can encode references to instances of TLcyMapComponent
, the default implementation of ILcyMapComponent
.
The object codec for views can encode references to instances of ILcdGXYView
that represent the main GXY view of a map component. Use ILcyMapComponent.getMainGXYView()
to get the main GXY view. That map must be encodable by the map component codec mentioned above.
The object codec for layer controls can encode the default layer controls for map components that are present in the map manager.
This means that if you write and use a custom layer control factory that returns an implementation of ILcyMapLayerControl
other than the default implementation (TLcyMapLayerControl
) and wish other add-ons to be able to reference this layer control in their workspace, you should provide an object codec
that can store the state of your layer control.
If other classes are created in a custom ALcyMapComponentFactory
, appropriate workspace codecs need to be registered for these. If not, the state of a map cannot be correctly saved or restored.
Asynchronous behavior considerations
If we assume that the asynchronous behavior is enabled so that painting a slow or heavy layer does not block the user interface,
the default controllers created by the TLcyMapComponentFactory
continuously repaint the view when they are used. .
However, if the asynchronous behavior is disabled, the continuous repainting could be detrimental to the user experience.
Therefore, you can configure the controllers so that they do not re-paint continuously:
-
Pan controller: set the configuration option
TLcyMapAddOn.panController.continuousRepaint
tofalse
in the configuration file of the map add-on to disable the continuous repainting. When the view is dragged with the pan controller, the current image of the view will be re-positioned, and the view will only be re-painted when the user releases the mouse button. -
Zoom controller: there are two zoom controllers available: the regular and the continuous zoom controller. The regular zoom controller lets the user specify the rectangle to which he wants to zoom, and performs no continuous re-painting. The continuous zoom controller zooms instantly in response to drag gestures by the user. By default, only the continuous controller is visible in the user interface. You can change which zoom controllers are visible by modifying the configuration of the menu item and tool bar item. The configuration options for the regular zoom controller start with
TLcyMapAddOn.zoomControllerActiveSettable.
. Those for the continuous zoom controller start withTLcyMapAddOn.continuousZoomControllerActiveSettable.
-
Navigate controller: by default, when the user drags on the map with the middle mouse button, the view is continuously re-painted. Changing the configuration option
TLcyMapAddOn.navigateController.continuousRepaint
tofalse
will disable the continuous re-painting. As with the regular pan controller, the view is only re-painted when the user releases the mouse and not for every drag event.
Program: With this configuration file, the controllers of the map add-on will be configured for synchronous painting is a sample configuration file that configures the controllers of the map add-on for cases in which the asynchronous behavior is disabled.
includeConfig=lucy/map/map_addon.cfg
# Disable the continuous repaint of the pan controller. The view will only be
# repainted when the user releases the mouse button.
TLcyMapAddOn.panController.continuousRepaint=false
# Disable the continuous repaint of the navigate controller. The view will only be
# repainted when the user releases the middle mouse button.
TLcyMapAddOn.navigateController.continuousRepaint=true
# Insert the regular zoom controller. With this zoom controller, the user
# specifies the region to where he wants to zoom.
TLcyMapAddOn.zoomControllerActiveSettable.toolBar.insert=true
# Remove the continuous zoom controller from the user interface.
TLcyMapAddOn.continuousZoomControllerActiveSettable.toolBar.insert=false