Table of Contents
Documentation for developer who want to use and extend the Geomajas GIS framework.
Geomajas is a free and open source GIS application framework for building rich internet applications. It has sophisticated capabilities for displaying and managing geospatial information. The modular design makes it easily extendable. The stateless client-server architecture guarantees endless scalability. The focus of Geomajas is to provide a platform for server-side integration of geospatial data, allowing multiple users to control and manage the data from within their own browsers. In essence, Geomajas provides a set of powerful building blocks, from which the most complex GIS applications can easily be built. Key features include:
Modular architecture
Clearly defined API
Integrated client-server architecture
Built-in security
Advanced geometry and attribute editing with validation
Custom attribute definitions including object relations
Advanced querying capabilities (searching, filters, style, ...)
Copyright © 2009 Geosparc nv.
Licensed under the GNU Affero General Public License. You may obtain a copy of the License at http://www.gnu.org/licenses/
This program is distributed in the hope that it will be useful, but without any warranty ; without even the implied warranty of merchantability or fitness for a particular purpose . See the GNU Affero General Public License for more details.
The project also depends on various other open source projects which have their respective licenses.
From the Geomajas source (possibly specific module), the
dependencies can be displayed using the "mvn
dependency:tree" command.
For the dependencies of the Geomajas back-end, we only allow dependencies which are freely distributable for commercial puposes (this for example excludes GPL and AGPL licensed dependencies).
This framework and documentation was written by the Geomajas Developers. If you have questions, found a bug or have enhancements, please contact us through the user fora at http://www.geomajas.org/.
List of contributors for this manual:
Pieter De Graef
Jan De Moerloose
Joachim Van der Auwera
Frank Wynants
Table of Contents
Geomajas uses the Apache Maven project management tool for its build and documentation process. Thanks to Maven , the easiest way to start using Geomajas is by creating a new project using the Maven archetype. This will create a simple working project that you can use as starting point.
As the simple project is created using the Maven archetype, you will need to install Maven on your system, which can be downloaded from http://maven.apache.org/. We recommend using the latest stable version (2.2.1 at the time of writing). Installing Maven is quite simple: just unzip the distribution file in the directory of your choice and make some environment changes so you can access the executable. More information for your specific OS can be found at the bottom of http://maven.apache.org/download.html.
When Maven is installed you can create a base project using the following command line:
mvn archetype:generate -DarchetypeCatalog=http://maven.geomajas.org/
Example 2.1. Create project using GWT Maven archetype
You first have to select the archetype you want to build (geomajas-gwt-archetype). Then it will ask you the "groupId", "artifactId", version and base package. Once you confirmed the settings, the project will be created in a subdirectory with a name equalling the "artifactId" you choose.
From the project root, you can immediately compile, test (using jetty as servlet container), or run the application in development mode using the following commands.
mvn install mvn jetty:run mvn gwt:run
The "install" target will create a war file for
the project in the target directory.
Example 2.2. Maven targets
The "jetty:run" variant will immediately start a jetty server and start the application. This way, you can test your application at full speed (as when deployed). The application can be accessed at http://localhost:8080/.
The "gwt:run" option allows you to start the application in GWT
development mode. A console will appear which allows starting your
application (from the browser). Amongst other things, this allows you to
see the messages GWT generates and see the output of the
"GWT.log" commands.
From the command line you can create eclipse project files.
You can now open the project in eclipse.
For working with the GWT face, you may want to make use Google's GWT Eclipse plug-in. Detailed instructions can be found at http://code.google.com/eclipse/docs/download.html. The launch configuration should already be created by the "gwt:eclipse" target in the command above. It will install the dependent libraries in the lib folder of the GWT war layout.
After importing and building the GWT projects, make sure you convert them to GWT projects in the project properties dialog:
Check the "Use Google Web Toolkit" checkbox. The GWT SDK can be
configured by clicking on the " Configure SDKs..." link.
After configuration, you should now be able to run the project as a GWT
Web application.
The setup in IntelliJ IDEA is quite straightforward and does not require running a separate Maven command. Just open the project from IDEA by selecting the pom in the root directory.
IDEA will recognize this as a GWT project and assign the correct facet but as always you will have to make your own run configuration (which is fortunately trivial). You will need version 9.0 or later for the GWT 2.0 support.
Before being able to use this configuration, you need to invoke the gwt:i18n Maven target to assure the files which are used for internationalisation are available (otherwise, you will get compilation errors). You can do this from the "Maven projects" tab.
Some additional settings have to be done in the "project structure" dialog. Apart from specifying the GWT installation directory, there is a specific project setting which has to be done manually, which is setting the target Web facet to "Web". The project structure for the simple GWT project should look as follows:
After this, you should be able to run the project. Any changes in the source code will be automatically detected, and debugging is possible.
You can both create the project from the archetype or open directly the Maven project in NetBeans. See http://wiki.netbeans.org/MavenBestPractices for more details.
Table of Contents
Geomajas is an application framework which allows building powerful GIS application. We will try to look at the architecture starting from a high level overview, drilling down to discover more details.
At the highest level, Geomajas makes a distinction between the back-end and faces.
The back-end is where you configure your maps, layers and attributes/features. It is always server side. The back-end has an API for interaction with the outside world and for extension using plug-ins. While one of the main purposes of the back-end is to provide bitmaps and vector graphics for the maps and provide data about features to be rendered and edited, it contains no display code.
The actual display and editing is done in the faces. The back-end is agnostic of web (or other) display frameworks. Faces are often split in two modules, a sever-side module (which directly talks to the back-end using java calls) and serializes data to the client, and a client-side module which only talks to the server side module. The communication between the two modules is private to the face. The face itself provides a additional client API. This will typically provide details about available widgets, parameters for these widgets and other possible interactions (like message queues or topics you can subscribe to).
The purpose of Geomajas is to provide rich editing of GIS data in the browser, but the faces also make other applications possible. You could unlock the maps which are configured in Geomajas using a face which makes data available as web services (this would result in a face with only a server-side module). You could also build a java swing application using the Geomajas back-end by writing a swing face. This would result in a thick client application (possibly accessible using Java Web Start).
Geomajas contains two faces out-of-the-box.
The a dojo face, which uses the dojo toolkit JavaScript widget library in the browser, is mainly provided for backward compatibility. Up until Geomajas 1.4 this was the only face which existed. It integrates well with dojo but has the disadvantage that you need to develop in both java (for the server side) and JavaScript (for the client side) and that debugging can be a challenge.
Since 1.5 we also provide a GWT face. This allows all development to be done in Java and GWT will handle conversion to Javascript for code which needs to run in the browser. Obviously this integrates best with GWT based applications, but it can be combined with other web frameworks as well.
The Geomajas back-end is itself built from several modules which are tied together using the Spring framework (http://springframework.org/). The Geomajas-api module is a set of interfaces which shields implementation details between the different modules. The base plumbing and some generic features are provided by the Geomajas-impl module.
There are four possible ways to extend the back-end.
command : commands are used as main interaction point between the face (client side) and the Geomajas back-end. The retrieval of maps and features, calculations, updates on the features and all all other functionalities which are available client-side are done using commands.
layer : this groups a set of access options for all details of the layers of a map. A layer can be either raster or vector based. A vector layer can be editable. The features describing the objects which are part of the vector layer are accessed through the "feature model" which converts generic feature objects into something Geomajas can use (this way, there is no need for the generic feature objects to implement a "feature" interface, allowing the use of pojos). A feature contains a geometry and can contain attributes, style and labels. Attributes can be complex, including one-to-many and many-to-one relations to other objects.
pipeline : all Geomajas back-end services which deal with layers are implemented using pipelines. A pipeline is a list of steps (actions) executed in order. Each pipeline can be overwritten for a layer, or you can change the default which is used when not overwritten for a layer.
Configuring pipelines can be used to change the rendering method, add additional rendering steps (for example marking the editable area on a tile), to introduce caching,...
security : these modules contain the pluggable security features. You can configure the security services which are used to verify the validity of an authentication token and return the authorization objects based on it. These authorization objects can read the security policies from your (secure) policy store.
The interaction of the client faces with the Geomajas back-end is handled using commands.
When a command needs to be invoked (probably as result of a
user interaction), the client will build a
CommandRequest object. This contains the name of the
command to be used, the parameters for the command, and optionally
the user authentication token and language of the user
interface.
This object is transferred to the face server. For web applications, this will typically be done using an AJAX request.
The face server will use this CommandRequest to
invoke the CommandDispatcher service, which can be
obtained using the Spring context.
The CommandDispatcher will start by invoking the
SecurityManager to check whether the execution of the
requested command is allowed. If it is allowed, the actual Command
is obtained using the Spring context. The
CommandResponse object is created and the command is
executed.
The Command will now do its job, writing the
results in the CommandResponse object. When problems
occur during execution of the command, it can either write this into
the response object or throw an exception.
When the command has executed, if it threw an exception, the dispatcher will add this in the response object. It will then convert any exceptions in the response object into some messages which may be sensible to the user (put the message in the correct language in the result object, assuring the "cause" chain is also included). Some extra information is also added in the response object (like command execution time).
The CommandResponse is returned to the face
server.
The face server serializes the CommandResponse
back to the face client.
When the command had something to do with rendering, then the
response object is likely to contain a Tile.
All the layer access services provided by the Geomajas back-end
are implemented by invoking a pipeline. Using
PipelineService, blocks of functionality become reusable
and highly configurable with limited coupling between the
pipeline step
s.
Some of the services which are implemented as
PipelineService include
RasterLayerService: methods for accessing a
raster layer, especially getting tiles for a raster
layer.
VectorLayerService: methods for accessing a
vector layer, for example for getting the features or obtaining
vector tiles
Pipelines can nest. One of the steps in the default "vectorLayer.saveOrUpdate" pipeline will loop over all features to be updated and invoke the "vectorLayer.saveOrUpdateOne" pipeline for each.
Pipelines are server side only, client access is typically made available by invoking a command.
Pipelines are typically invoked for a specific layer. In that case, the default pipeline can be replaced by a layer specific pipeline. This way, layer specific configurations (like optimizations or specific rendering) can be applied. When a layer does not overwrite a pipeline, the default is used. Pipelines are always selected on pipeline name. You can create the layer specific pipeline by setting the layer id for which it applied. When several pipelines have the same steps, you can define the pipeline once, and refer to it later by using a pipeline delegate instead of a list of steps.
A pipeline consists of a number of steps. A pipeline is
configured using a PipelineInfo object which details the
pipeline id and steps. When a pipeline is started (using the
PipelineService) it executes the pipeline steps until the
pipeline is finished (a status which can be set by one of the steps),
or until no more steps are available in the pipeline. Each step gets
two parameters.
a context which contains a map of (typed) objects which can be used to pass data between the steps (including initial parameters for the pipeline).
the result object which can be filled or transformed during the pipeline's execution.
The layer extensions allow determining how a layer is built, which data is part of the layer, update and creation of extra data on a layer.
A Layer has some metadata (id, coordinate system,
label, bbox, stored in the LayerInfo object) and allows you
get obtain the layer data.
The data which is accessed using Geomajas can be security sensitive.
Geomajas is built to be entirely independent of the authentication mechanism and the way to store policies.
Based on the user who is logged into the system, the following restrictions can apply:
access rights to a command
access rights for a layer
a filter which needs to be applied for a layer
a region which limits the data which may be accessed for a layer
access rights on the features
access rights on the individual attributes of the features
To assure the authentication mechanism is pluggable, an authentication token is used. The authentication token is used to determine the security context . The security context contains the policies which apply and which can be queried.
A list of
security services
can be defined
(using Spring bean security.SecurityInfo). This list can
be overwritten in configuration. By default the list is empty, which
prohibits all access to everyone. The back-end does however include a
security service which can be used to allow all access to
everyone.
The security service is responsible for converting the
authentication token into a list of
authorization
objects
. The security manager will loop all configured
security services (using Spring bean
security.SecurityInfo) to find all the authorization
objects which apply for the token. By default the security manager
will stop looping once one of the security services gave a result. You
can change this behaviour to always combine the authorization objects
from all security services.
The system explicitly allows authentication tokens to be generated by different authentication servers. In that case for each authentication server, at least one security service should be configured in Geomajas. However, when using such a configuration, you have to verify that the authentication tokens which are generated by the different servers cannot be the same.
In many systems (including RBAC systems) an authorization object matches a roles for the authenticated user.
Note that, as the actual authentication mechanisms are handled by the security services, Geomajas does not know any passwords or credentials. Similarly the secure, tamper-proof storage of policies is not handled by Geomajas either.
Details about the current authentication token and access to the
policies (using the authorization objects) is available using the
SecurityContext. The security context is thread specific.
When threads are reused between different users, the security context
needs to be cleared at the end of a request (group). This is normally
handled by the faces.
The following general authorization checks exist:
isToolAuthorized(String toolId): true when the
tool can be used. The "toolId" matches the "id" parameter which is
used in the configuration as specified using the
ToolInfo class.
isCommandAuthorized(String commandName): true
when the command is allowed to be called. The "commandName"
parameter is the same as the command name which is passed to the
CommandDispatcher service.
And for each layer:
isLayerVisible(String layerId): true when (part
of) the layer is visible.
isLayerUpdateAuthorized(String layerId): true
when (some of) the visible features may be editable.
isLayerCreateAuthorized(String layerId): true
when there is an area in which features can be created.
isLayerDeleteAuthorized(String layerId): true
when (some of) the visible features may be deleted.
getVisibleArea(String layerId): only the area
inside the returned geometry is visible (uses layer coordinate
space). All features which fall outside the layer's MaxExtent area
are also considered not visible.
getUpdateAuthorizedArea(String layerId): only
the area inside the returned geometry may contain updatable
features (uses layer coordinate space). All features which fall
outside the layer's MaxExtent area are also considered not
updatable.
getCreateAuthorizedArea(String layerId): only
the area inside the returned geometry can new features be created
(uses layer coordinate space). All features which fall outside the
layer's MaxExtent area are also considered not creatable.
getDeleteAuthorizedArea(String layerId): only
the area inside the returned geometry may contain deletable
features (uses layer coordinate space). All features which fall
outside the layer's MaxExtent area are also considered not
deletable.
getFeatureFilter(String layerId): get an
additional filter which needs to be applied when querying the
layer's features.
isFeatureVisible(String layerId, InternalFeature
feature): check the visibility of a feature.
isFeatureUpdateAuthorized(String layerId,
InternalFeature feature): check whether a feature is
editable.
isFeatureUpdateAuthorized(String layerId,
InternalFeature oldFeature, InternalFeature newFeature):
check whether the update contained in the feature is allowed to be
saved.
isFeatureCreateAuthorized(String layerId,
InternalFeature feature): check whether a feature is
allowed to be created.
isFeatureDeleteAuthorized(String layerId,
InternalFeature feature): check whether deleting the
specific feature is allowed.
isAttributeReadable(String layerId, InternalFeature
feature, String attributeName): check the readability of an
attribute. The result can be feature specific.
isAttributeWritable(String layerId, InternalFeature
feature, String attributeName): check whether an attribute
is editable. The result can be feature specific.
These authorizations are split in several groups. The security service is not required to provide an implementation of each authorization test (see API), the security context always ensures that all methods are available.
Checking authentication and fetching the authorization details
can be time consuming (not to mention the hassle of logging in again).
To solve this, the results of the security services can be cached.
When a security service can authenticate a token, the reply can
contain details about the allowed caching. Three parameters are
allowed to be passed, the validUntil and
invalidAfter timestamps and an extendValid
duration.
The security manager first checks the cache to find (valid) authentication results. A cache entry is only valid until the "validUntil" timestamp. When an entry is valid, validUntil may be extended until "now" plus "extendValid" duration. However, "validUntil" is never extended past "invalidAfter". When no data can be found in the cache, the security services are queried.
There may be multiple authentications stored for a authentication token. When one of them becomes invalid, they are all considered invalid.
Entering credentials is never handled by Geomajas. When the authentication token cannot be verified, a security exception is thrown. It is up to the client application (the face probably) to assure that a new authentication token is created.
The authorization have two possible results. When reading or rendering, all unauthorized data should simply be filtered without warning or exception. When trying to invoke a command or to create, update or delete, any attempt which is not authorized should result in a security exception.
The security uses the approach that all access is forbidden unless is is allowed. Hence, you will always need to configure at least one security service to assure the system is usable.
The security is applied throughout Geomajas. A list of places (which is not necessarily complete) and some additional ideas for application follow.
Back-end services:
CommandDispatcher verifies the existence of a
SecurityContext and creates one if needed.
CommandDispatcher verifies whether the command
is allowed to be used.
VectorLayerService:
Check layer access.
Handle the "filter" for the layer (if any).
Filter on visible area as this can increase query speed.
Post-process features filtering unreadable attributes, set update flags, remove features which are not allowed.
Commands:
configuration.Get and configuration.GetMap: layers which are invisible should be removed, tools which are not authorized should be removed, "editable" and "deletable" statusses on layers, features, attributes need to be set.
configuration.UserMaximumExtent: max extent should only consider visible features.
feature.PersistTransaction: making changes to attributes which are not editable should cause a security exception.
feature.SearchByLocation: only return visible features and readable attributes.
feature.SearchFeature: only return visible features and readable attributes.
geometry.Get: only return the geometry for visible features.
geometry.MergePolygon: no security implications.
geometry.SplitPolygon: no security implications.
render.GetRasterTiles: should only return data for visible layers, ideally post-processing the image to ensure only visible area is included (making the rest transparent).
render.GetVectorTile: should only return data for visible layers, only display visible features, only return visible features, only render visible features. When attributes need to be included, only readable attributes should be included and the "editable" flag needs to be set.
Rendering:
The individual rendering steps (especially the layer/feature
model) can use the SecurityContext to filter the data
they produce.
Images can have areas masked which are not allowed to be seen.
The rendering pipeline can include steps which check the security. This can make life easier on the layer model which are not guaranteed (or forced) to handle all security aspects. These are active by default but can be removed for speed (when you are sure this is double work).
Cache:
The caching needs to consider the access rights when storing and retrieving data.
Face:
The face is responsible for assuring a authentication token is included in all access to the back-end.
The "get configuration" commands filter the data to assure invisible layer and attributes and tools which are not allowed are not displayed. No action needed.
Specific tests on editability of individual features and attributes would be useful to assure the user does try to enter or modify data which cannot be saved.
The face should ask for credentials again when the token was
not available or is no longer valid. Specifically when a
GeomajasSecurityException is received which code
ExceptionCode.CREDENTIALS_MISSING_OR_INVALID.
While this is not really touched by description above, the following system configuration issues are likely to be important when you want to secure your Geomajas application.
Make sure the communication between the client and server uses encryption, possibly by using https. This prevents snooping of your data and/or hijacking the security token.
Even if your application is using http for some reason, at the very least your authentication method should use https to prevent your passwords from being transmitted on the wire in cleartext. I would expect all authentication servers do this.
Depending on your needs, it may make sense to store the data encrypted on the server. If you want that, your need a layer model which can access your secured data (possibly passing on the security token).
Different modules have different impacts and different purposes. Therefore different categories of modules are required. Geomajas has defined the following set of module categories (matching the directories in the source):
application: working examples of applications using the Geomajas GIS framework.
backend : these are essential Geomajas modules. Each Geomajas application needs these modules. However, you also need some extensions and layer models or you won't be able to do much.
build-tools : some modules which are useful for starting or building Geomajas or a Geomajas project.
documentation : documentation modules, specifically the different Geomajas guides.
face : faces that present a certain Geomajas client interface to the user.
plugin : modules that extend Geomajas. This can either add new functionality, add support for a certain type of data source, provide a security service or a combination.
test : modules which are used for (integration) testing of Geomajas.
Full list of Geomajas modules:
| Name | Purpose |
|---|---|
| geomajas-dojo-example | Example application using the dojo face. Is more advanced than the dojo-simple demo. Can be useful as template project when starting a new dojo based Geomajas application which uses project specific JavaScript code. |
| geomajas-dojo-simple | Simple example project using the dojo face. Can be a useful template project when starting a new dojo based Geomajas application. |
| geomajas-gwt-example | Example application using the GWT face which serves both as showcase and test application. |
| geomajas-gwt-simple | Simple example project using the GWT face. Very similar to the GWT archetype which can be used to start a new GWT based Geomajas project. |
Table 4.1. List of Geomajas application modules
| Name | Purpose |
|---|---|
| geomajas-api | Stable interfaces. Reference guide for other modules. |
| geomajas-api-experimental | Experimental interfaces. This contains some experimental stuff which may be promoted to the supported API when useful, or may be changed or dumped. As this is not part of the API, it may change between revisions. |
| geomajas-command | Lists all basic commands. |
| geomajas-common-servlet | Code which is shared by the different faces which are servlet based. |
| geomajas-impl | Main library with default implementations. |
| geomajas-testdata | Module which contains data which is used for testing Geomajas. |
Table 4.2. List of Geomajas back-end modules
| Name | Purpose |
|---|---|
| geomajas-checkstyle | Module which contains the checkstyle definitions which should be adhered to for all code in the Geomajas source tree. |
| geomajas-gwt-archetype | Archetype which builds a simple application which can be used to start a new GWT based Geomajas application. |
| geomajas-maven-dojo | Maven plugin which helps to combine all the JavaScript code for dojo, Geomajas and the project itself. This is usually referred to as the "shrink" or "shrinksafe" step. |
| geomajas-maven-plugin | Maven plugin which is used for generating the documentation. It extracts excerpts from the code to allow inclusion in the docbook guides. |
Table 4.3. List of Geomajas build-tools modules
| Name | Purpose |
|---|---|
| contributorguide | Guide for contributors to the project. Includes information about compilation of the project, coding style, how to contribute to the documentation, JIRA guidelines etc. |
| devuserguide | Guide for developers who want to use Geomajas in their applications. |
| enduserguide | Guide for end-users who use the Geomajas widgets. This guide should probably be included in the application documentation. |
| style | Style module for conversion of the docbook files to usable output. |
| xslt | Transformation module for conversion of the docbook files to usable output. |
Table 4.4. List of Geomajas documentation modules
| Name | Purpose |
|---|---|
| geomajas-dojo-client | Client side module for the dojo AJAX interface. |
| geomajas-dojo-client-shrinksafe | Server side module for the dojo AJAX interface, after shrinking to combine everything in one JavaScript file. |
| geomajas-dojo-server | Server side module for the dojo AJAX interface. |
| geomajas-gwt-client | Module which contains the GWT face. This contains both client and server side code for the face, the split is handled by GWT (using GWT conventions). |
Table 4.5. List of Geomajas face modules
| Name | Purpose |
|---|---|
| geomajas-layer-geotools | Support for any data format GeoTools supports. |
| geomajas-layer-google | Support for GoogleMaps raster format. |
| geomajas-layer-hibernate | Support for database formats through Hibernate. |
| geomajas-layer-openstreetmap | Support for OpenStreetMap raster format. |
| geomajas-layer-wms | Support for the WMS raster format. |
| geomajas-plugin-printing | Adds advanced printing capabilities. |
| geomajas-plugin-springsecurity | Simple security service which allows including the entire security configuration in the Spring configuration files, making the configuration static. |
Table 4.6. List of Geomajas plug-in modules
| Name | Purpose |
|---|---|
| geomajas-test-integration | Integrations tests, currently mostly testing the security handling. |
Table 4.7. List of Geomajas test modules
Table of Contents
Then there are two main sets of plug-ins which provide extra features.
The layer plug-ins provide access to the actual data which needs to be displayed as part of a maps. There are basically two types of layers, providing either raster data (bitmaps) or vector data. The layers which are provided as part of the normal distribution include
geomajas-layer-hibernate (vector): allow access to any kind of features which are stored in a spatial (relational) database. The data is accessed using the hibernate and hibernate-spatial open source libraries.
geomajas-layer-geotools (vector): access data from any vector data source which has a GeoTools data store defined for it (http://geotools.org/javadocs/org/geotools/data/DataStore.html).
geomajas-layer-google (raster): include Google rasters. This allows access to the normal and satellite views provided by Google. You still have to make sure you comply with Google terms of use (http://code.google.com/apis/maps/).
geomajas-layer-openstreetmap (raster): support for raster data coming from the OpenStreetMap project (http://www.openstreetmap.org/).
geomajas-layer-wms (raster): access data from a WMS server (http://www.opengeospatial.org/standards/wms).
geomajas-layer-shapeinmem (vector): access data from an ESRI shape file which handled in memory. The actual data access if done using GeoTools (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf).
Other plug-ins allow extensions in functionality, either by providing additional commands or extending the rendering pipelines, or provide additional security services.
geomajas-command : set of commands which are provided as part of the standard distribution. This is so fundamental to using Geomajas that it is provided as a back-end module.
geomajas-plugin-printing : printing extensions for the framework
geomajas-plugin-springsecurity : a basic security service which can be configured as part of the Spring configuration and does not use an external source for users or policies, making the security configuration entirely static.
Geomajas leverages the Spring framework for configuration.
In your web.xml file, you need to assure the
configuration is made available to the application, and you can indicate
which files are used to contain the configuration. Though it is possible
to put all configuration information in one file, we recommend splitting
your configuration in several files. At least one file per application,
possibly split further per client layer configuration, and one file for
server-side configuration of each of the layers.
Apart from the servlets (which are specific for each face), your web.xml needs an excerpt similar to the following:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
org/geomajas/dojo/simple/applicationContext.xml
org/geomajas/dojo/simple/layerRoads.xml
org/geomajas/dojo/simple/layerStructures.xml
org/geomajas/dojo/simple/layerOsm.xml
</param-value>
</context-param>
<listener>
<listener-class>org.geomajas.servlet.GeomajasContextListener</listener-class>
</listener>
Example 6.1. Defining spring configuration locations in web.xml
The listener class initialises the application context as needed for
Geomajas. It appends the context configuration locations which are
specified in the contextConfigLocation context parameter to
the list of internal configuration locations and uses these to build the
application context. All locations which are specified are read from the
class path.
Each configuration file needs the following header:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
Example 6.2. Spring configuration preamble
This defines the most common schemas which are needed. The configuration is built by populating the configuration classes. The configuration classes are split up between client-side and back-end. Only the back-end classes are necessary to configure the back-end, which behaves as a catalog of layers. The client side classes are used to define applications and maps, which are purely client-side concepts in the Geomajas architecture.
The back-end classes exist in the
org.geomajas.configuration package and have a class name
ending in "Info". These classes are actually used to represent the DTO
part of the back-end layers, thereby allowing to transfer information or
metadata of these layers to the client.
Configuration is done using the Spring Framework. We will give some notions here, but for a full introduction to Spring, please read the reference documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/.
Each configuration file can contain one or more bean definitions,
which correspond to actual Java bean instances. You can set all the
properties of the objects using this configuration file. Primitive types
can be set directly using a string representation of the value. When the
value is another bean, then it can either be defined in-line, or you can
use a reference. You can choose whether the referenced bean is defined
in the same file or a different one. As long as the bean name is unique,
and the location is added in the contextConfigLocation
context parameter in the web.xml file, the reference is resolved.
It is possible to define a bean with the same name (or id) more than once. In that case, the last occurrence will be used.
The initial bean which needs to be defined is a bean indicating the client application info .
<bean name="gwt-simple" class="org.geomajas.configuration.client.ClientApplicationInfo">
<property name="maps">
<list>
<ref bean="sampleFeaturesMap" />
<ref bean="sampleOverviewMap" />
</list>
</property>
</bean>
Example 6.3. ClientApplicationInfo definition
As you can see, this defines the list of maps for the application. It may (optionally) also define some additional user info and a screen DPI parameter. The DPI refers to the resolution in pixels per inch of your monitor, for a PC its usually 96 (the default) or 72.
There needs to be at least one ClientApplicationInfo
bean. The bean name is used when requesting the application info.
The back-end configuration consists of security, transaction and layer configurations, plus any additional configuration that is needed for plug-ins and your application.
To make sure the system can be used, you have to configure the security to allow access. The easiest configuration is to allow access to everybody.
<bean name="security.securityInfo" class="org.geomajas.security.SecurityInfo">
<property name="loopAllServices" value="false"/>
<property name="securityServices">
<list>
<bean class="org.geomajas.security.allowall.AllowAllSecurityService"/>
</list>
</property>
</bean>
Example 6.4. Allow full access to everybody
Any other configuration would depend on the available security services. For example, when using the springsecurity plugin, the following could be defined.
<bean name="SecurityService" class="org.geomajas.plugin.springsecurity.security.SpringSecurityService"/>
<bean name="security.securityInfo" class="org.geomajas.security.SecurityInfo">
<property name="loopAllServices" value="true"/>
<property name="securityServices">
<list>
<ref bean="SecurityService"/>
<bean class="org.geomajas.plugin.springsecurity.security.LoginAllowedSecurityService"/>
</list>
</property>
</bean>
<bean class="org.geomajas.plugin.springsecurity.configuration.SecurityServiceInfo">
<property name="users">
<list>
<!-- User elvis has restricted attribute editing permissions on roads layer -->
<bean class="org.geomajas.plugin.springsecurity.configuration.UserInfo">
<property name="userId" value="elvis"/>
<property name="password" value="BUOMyQ95onvc7gMrMjFtDQ"/> <!-- "elvis" -->
<property name="userName" value="Elvis Presley"/>
<property name="authorizations">
<list>
<bean class="org.geomajas.plugin.springsecurity.configuration.AttributeAuthorizationInfo">
<property name="commandsInclude">
<list>
<value>.*</value>
</list>
</property>
<property name="visibleLayersInclude">
<list>
<value>.*</value>
</list>
</property>
<property name="updateAuthorizedLayersInclude">
<list>
<value>beans</value>
</list>
</property>
Example 6.5. Partial springsecurity configuration
Most notable in this example is the inclusion of two security services. The first is provided to allow login and logout ( only ) for everybody. The second defines users and authorizations (only the beginning of the configuration is displayed here).
Spring has support declarative transaction management, which
relieves us from the burden of writing our own transaction demarcation
and exception handling code. Of course, Spring transaction management
has to be hooked up with the transaction definition and life cycle of
the underlying data platform (hibernate, JTA, JDBC) . Each data access
technology should provide its own implementation of the Spring class
PlatformTransactionManager. For many platforms this is
already part of the Spring libraries, and for GeoTools we have
provided a transaction manager.
Transaction management is typically only needed for editable
database layers (although we support and encourage it for read-only
layers as well), of which we provide two types:
HibernateLayer and GeoToolsLayer. There is
currently no support for having multiple platform transaction
managers, although configurations with multiple transaction managers
should at least theoretically be possible and the subject will be
investigated further. In practice this means that you currently must
not mix editable Hibernate and GeoTools layers.
For Hibernate layers, the following configuration should be used:
<!-- DataSource Property -->
<bean id="simpleDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://127.0.0.1:5432/simple" />
<property name="username" value="simple" />
<property name="password" value="simple" />
</bean>
<!-- Hibernate SessionFactory -->
<bean id="simpleSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="simpleDataSource" />
<property name="configLocation">
<value>classpath:/mypackage/hibernate/cfg/hibernate.cfg.xml
</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
</bean>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="simpleSessionFactory" />
</bean>
Example 6.6. Hibernate transaction configuration
Starting from the top, the following are defined:
The data source: this specifies the connection pool type and the connection properties of the database (PostGis in this case)
The session factory: this is Hibernate's primary singleton and used by the Hibernate layer to access the session/connection
A tag to enable annotation-based transactional behavior, internally used by Geomajas to decide which commands need transaction support
The platform transaction manager for Hibernate
For GeoTools layers, the configuration is as follows:
<bean name="simpleDatastore" class="org.geomajas.layer.geotools.DataStoreFactory" factory-method="create">
<constructor-arg>
<map>
<entry key="namespace" value="postgis" />
<entry key="user" value="simple" />
<entry key="passwd" value="simple" />
<entry key="database" value="simple" />
<entry key="host" value="localhost" />
<entry key="port" value="5432" />
<entry key="dbtype" value="postgis" />
</map>
</constructor-arg>
</bean>
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.geomajas.layer.geotools.GeoToolsTransactionManager">
</bean>
Example 6.7. GeoTools transaction configuration
Starting from the top, the following are defined:
The data store: this is GeoTools' primary data object and will be referenced in the GeoTools layer. The parameters define the connection properties of the underlying physical medium (PostGis in this case)
A tag to enable annotation-based transactional behavior, internally used by Geomajas to decide which commands need transaction support
The platform transaction manager for GeoTools
Raster layers are image-based layers which, depending on the
type, may be configured to retrieve their images from WMS, Google Maps
or OpenStreetMap (tile) servers. All raster layer implementations
implement the org.geomajas.layer.RasterLayer interface,
which means they provide an accessor for a
RasterLayerInfo metadata object. The info object
configuration is normally defined in the Spring configuration as part
of the entire layer configuration. Depending on the type of layer,
extra properties are needed to provide a full configuration.
For all raster layers, you will need to define a raster layer info object to define the back-end configuration for the layer. The exact meaning for some of the fields depend on the actual layer, but most important features include:
| Name | Description |
|---|---|
| dataSourceName | The name of the data source as used by the layer. |
| crs | The coordinate reference system, expressed as "EPSG:<srid>". Caveat: make sure this is the same as the maps' crs as full raster image reprojection is not supported! If the crs is not the same, an attempt will be done to rescale and align the center coordinates, though. |
| maxExtent | The bounds of the layer, specified in layer coordinates. After transformation to map coordinates, this determines the locations and absolute size of the tiles. |
| resolutions | A list of resolution values. An image or tile resolution is defined as the ratio between map units and pixels for a tile. If a resolution is 5 and the map unit is meter, this means that 1 pixel represents a square area of 5 by 5 m. Images are usually optimized for a specific (set of) resolution(s), so it is important to specify these here if they are known. On top of that, some servers provide specific tile caching for these predefined resolutions (for example WMS-T). Care has to be taken, the same resolutions should be defined in the map as well if one wants to avoid linear distortion of the images. |
| tileWidth | Width in pixels of the requested images. |
| tileHeight | Height in pixels of the requested images. |
Table 6.1. Raster Layer info
The location of the images or tiles is defined by calculating the real width and height (based on the resolution) and "paving" the maximum extent with tiles starting at the origin (x,y) of the extent. If no resolutions are predefined, the tiles are calculated by dividing the maximum extent by successive powers of 2. Make sure the width/height ratio of the maximum extent corresponds to the width/height ratio of the tile.
Make sure your classpath contains the required plug-in. If you are using Maven, add the following dependency to your pom:
<dependency>
<groupId>org.geomajas.plugin</groupId>
<artifactId>geomajas-layer-wms</artifactId>
<version>${geomajas-layer-wms-version}</version>
</dependency>
Example 6.8. WMS layer dependency
A complete WMS layer configuration looks as follows:
<bean name="wmsBluemarbleInfo" class="org.geomajas.configuration.RasterLayerInfo">
<property name="crs" value="EPSG:4326"/>
<property name="maxExtent">
<bean class="org.geomajas.geometry.Bbox">
<property name="x" value="-180"/>
<property name="y" value="-90"/>
<property name="width" value="360"/>
<property name="height" value="180"/>
</bean>
</property>
<property name="resolutions">
<list>
<value>0.5</value>
<value>0.25</value>
<value>0.125</value>
<value>0.0625</value>
<value>0.03125</value>
<value>0.015625</value>
<value>0.0078125</value>
<value>0.00390625</value>
<value>0.001953125</value>
<value>0.0009765625</value>
<value>0.00048828125</value>
<value>0.000244140625</value>
<value>0.000122070312</value>
</list>
</property>
<property name="dataSourceName" value="bluemarble" />
<property name="tileWidth" value="512"/>
<property name="tileHeight" value="512"/>
</bean>
<bean name="bluemarble" class="org.geomajas.layer.wms.WmsLayer" >
<property name="layerInfo" ref="wmsBluemarbleInfo" />
<!-- When configuring your own applications, please do not use this WMS server -->
<property name="baseWmsUrl" value="http://apps.geomajas.org/geoserver/wms"/>
<property name="version" value="1.1.1"/>
<property name="format" value="image/jpeg"/>
<property name="styles" value=""/>
</bean>
Example 6.9. Wms layer configuration
As you can see, the bean class refers to the actual layer
type: org.geomajas.layer.wms.WmsLayer.
The first property is the layer info object, which describes the metadata common to all raster layers. The following properties are needed for this object:
The layer object contains some extra properties which are specifically tied to the WMS server:
| Name | Description |
|---|---|
| baseWmsUrl | The base url of the WMS server. This is the base part (excluding the request parameters) of the url that would be called to execute a WMS request. |
| version | Version of the WMS protocol which should be used. Check your server configuration for possible values. |
| format | The mime type in which the images should be returned, for example "image/gif". Check your server configuration for possible values. |
| styles | Some WMS servers support multiple styles for their layers. Check your server configuration for possible values. |
| parameters | You can define additional parameters which can be
passed to the WMS server. These are name/value pairs which
are passed in
org.geomajas.configuration.Parameter
objects. |
Table 6.2. WMS layer properties
Make sure your classpath contains the required plug-in. If you are using Maven, add the following dependency to your pom:
<dependency>
<groupId>org.geomajas.plugin</groupId>
<artifactId>geomajas-layer-openstreetmap</artifactId>
<version>${geomajas-layer-openstreetmap-version}</version>
</dependency>
Example 6.10. OpenStreetMap layer dependency
A complete OpenStreetMap layer configuration looks as follows:
<bean name="osmInfo" class="org.geomajas.configuration.RasterLayerInfo">
<property name="crs" value="EPSG:900913"/>
<property name="maxExtent">
<bean class="org.geomajas.geometry.Bbox">
<property name="x" value="-20026376.393709917"/>
<property name="y" value="-20026376.393709917"/>
<property name="width" value="40052752.787419834"/>
<property name="height" value="40052752.787419834"/>
</bean>
</property>
<property name="resolutions">
<list>
<value>156543.03</value>
<value>78271.52</value>
<value>39135.76</value>
<value>19567.88</value>
<value>9783.94</value>
<value>4891.97</value>
<value>2445.98</value>
<value>1222.99</value>
<value>611.49</value>
<value>305.75</value>
<value>152.874057</value>
<value>76.4370283</value>
<value>38.2185141</value>
<value>19.1092571</value>
<value>9.55462853</value>
<value>4.77731427</value>
<value>2.38865713</value>
<value>1.19432857</value>
</list>
</property>
<property name="tileWidth" value="256"/>
<property name="tileHeight" value="256"/>
</bean>
<bean name="osm" class="org.geomajas.layer.osm.OsmLayer" >
<property name="layerInfo" ref="osmInfo" />
</bean>
Example 6.11. OpenStreetMap layer configuration example
The layer info inner bean has the same general content as usual, with one exception : the dataSourceName property is not necessary in this case. As with the WMS layer, the resolutions have been carefully chosen to match the tile server's resolutions. The tile width and height are implied by the server in this case but since they are required metadata for all raster layers, they should be set to the server's values: 256 x 256.
Make sure your class path contains the required plug-in. If you are using Maven, add the following dependency to your pom:
<dependency>
<groupId>org.geomajas.plugin</groupId>
<artifactId>geomajas-layer-google</artifactId>
<version>${geomajas-layer-google-version}</version>
</dependency>
Example 6.12. Google layer dependency
A complete Google layer configuration looks as follows:
<bean name="googleInfo" class="org.geomajas.configuration.RasterLayerInfo">
<property name="crs" value="EPSG:900913"/>
<property name="maxExtent">
<bean class="org.geomajas.geometry.Bbox">
<!--
see http://cfis.savagexi.com/2006/05/03/google-maps-deconstructed
-20037508.342789, -20037508.342789 to 20037508.342789, 20037508.342789
-->
<property name="x" value="-20026376.393709917"/>
<property name="y" value="-20026376.393709917"/>
<property name="width" value="40052752.787419834"/>
<property name="height" value="40052752.787419834"/>
</bean>
</property>
<property name="tileWidth" value="256"/>
<property name="tileHeight" value="256"/>
</bean>
<bean name="google" class="org.geomajas.layer.google.GoogleLayer" >
<property name="layerInfo" ref="googleInfo" />
<property name="satellite" value="false" />
</bean>
Example 6.13. Google layer configuration
| GoogleLayer configuration | |
|---|---|
| satellite | Set to true to use satellite view from Google. When false (the default), the normal view (showing streets) will be used. |
Table 6.3. GoogleLayer configuration
The layer info inner bean has the same general content as usual. There are some simplifications though. There is no need to provide resolutions or tile size (256x256 is always used). The crs should always be set to Mercator ("EPSG:900913").
On the layer, you can set the "satellite" property to choose between using normal display (showing streets) or satellite view (by setting this to "true").
You need to assure that you comply with Google's terms of
use (see http://code.google.com/apis/maps/).
Some of the things you need to do include adding the Google API
code in your application (using a Google API key when not running
on localhost), and (from the GWT face, using the
GoogleAddon class to assure the copyright notes are
displayed on the map.
Vector layers contain homogeneous vectorial features. All vector
layer implementations implement the
org.geomajas.layer.VectorLayer interface, which means
they provide an accessor for a VectorLayerInfo metadata
object. The info object configuration is normally defined in the
Spring configuration as part of the entire layer configuration.
Depending on the type of layer, extra properties are needed to provide
a full configuration.
The definition of the actual layer is similar to the definition of a raster layer.
For the layer configuration, you have to create the layer info object.
<bean name="countriesInfo" class="org.geomajas.configuration.VectorLayerInfo">
<property name="layerType" value="MULTIPOLYGON"/>
<property name="crs" value="EPSG:4326"/>
<property name="maxExtent">
<bean class="org.geomajas.geometry.Bbox">
<property name="x" value="-85.05112877980659"/>
<property name="y" value="-85.05112877980659"/>
<property name="width" value="170.102257"/>
<property name="height" value="170.102257"/>
</bean>
</property>
<property name="featureInfo" ref="countriesFeatureInfo" />
<property name="namedStyleInfos">
<list>
<ref bean="countriesStyleInfo" />
</list>
</property>
</bean>
Example 6.14. ShapeInMem layer info example
This defines the details common to both raster and vector layers, like layer id, crs, layer type, max extent (bounding box) etc.
The following table describes the properties of the
VectorLayerInfo object:
| Property | Description |
|---|---|
| layerType | This property determines the type of the default geometry of the features. The following types are supported: POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING and MULTIPOLYGON |
| crs | The coordinate reference system, expressed as "EPSG:<srid>". This is probably determined by the layer, but has to be specified anyhow as we have no autodetection in place yet.. |
| maxExtent | The bounds of the layer, specified in layer coordinates. After transformation to map coordinates, this determines the locations and absolute size of the tiles. |
Table 6.4. VectorLayer info
The feature metadata can be found in the
FeatureInfo object. This objects contains the complete
feature type description (id, attributes and geometry) as well as
the validation rules for the attributes. An example definition of
this object is given below:
There is also the definition of the features (featureInfo), and the style which needs to be used for the label. Note that the labelAttributeName refers to the name of an attribute as defined in the feature info.
<bean class="org.geomajas.configuration.FeatureInfo" name="countriesFeatureInfo">
<property name="dataSourceName" value="country"/>
<property name="identifier">
<bean class="org.geomajas.configuration.PrimitiveAttributeInfo">
<property name="label" value="Id"/>
<property name="name" value="ID"/>
<property name="type" value="LONG"/>
</bean>
</property>
<property name="geometryType">
<bean class="org.geomajas.configuration.GeometryAttributeInfo">
<property name="name" value="the_geom"/>
<property name="editable" value="false"/>
</bean>
</property>
<property name="attributes">
<list>
<bean class="org.geomajas.configuration.PrimitiveAttributeInfo">
<property name="label" value="Name"/>
<property name="name" value="CNTRY_NAME"/>
<property name="editable" value="true"/>
<property name="identifying" value="true"/>
<property name="type" value="STRING"/>
</bean>
<bean class="org.geomajas.configuration.PrimitiveAttributeInfo">
<property name="label" value="UNESCO"/>
<property name="name" value="UNESCO"/>
<property name="editable" value="true"/>
<property name="identifying" value="true"/>
<property name="type" value="INTEGER"/>
<property name="validator">
<bean class="org.geomajas.configuration.validation.ValidatorInfo">
<property name="toolTip" value="Member of UNESCO? (value must be 0 or 1)"/>
<property name="errorMessage" value="Invalid value: The value must be either 0 or 1."/>
<property name="constraints">
<list>
<bean class="org.geomajas.configuration.validation.NotNullConstraintInfo"/>
<bean class="org.geomajas.configuration.validation.MinConstraintInfo">
<property name="value" value="0"/>
</bean>
<bean class="org.geomajas.configuration.validation.MaxConstraintInfo">
<property name="value" value="1"/>
</bean>
</list>
</property>
</bean>
</property>
</bean>
</list>
</property>
</bean>
Example 6.15. ShapeInMem layer feature info example
The following table describes the properties of the
FeatureInfo object:
Last but not least, you can define one or more style definitions which should be used for rendering of the layer. You can define formulas to determine which style should be used. The first style which passes the formula will be applied for the feature.
We also have to define the features.
<bean class="org.geomajas.configuration.NamedStyleInfo" name="countriesStyleInfo">
<property name="featureStyles">
<list>
<bean class="org.geomajas.configuration.FeatureStyleInfo">
<property name="name" value="UNESCO Member"/>
<property name="formula" value="(UNESCO == 1)"/>
<property name="fillColor" value="#99AA33"/>
<property name="fillOpacity" value=".6"/>
<property name="strokeColor" value="#99AA33"/>
<property name="strokeOpacity" value=".3"/>
<property name="strokeWidth" value="1"/>
</bean>
<bean class="org.geomajas.configuration.FeatureStyleInfo">
<property name="name" value="Other"/>
<property name="fillColor" value="#99DD22"/>
<property name="fillOpacity" value=".6"/>
<property name="strokeColor" value="#99DD22"/>
<property name="strokeOpacity" value=".3"/>
<property name="strokeWidth" value="1"/>
<property name="symbol">
<bean class="org.geomajas.configuration.SymbolInfo">
<property name="rect">
<bean class="org.geomajas.configuration.RectInfo">
<property name="w" value="5"/>
<property name="h" value="5"/>
</bean>
</property>
</bean>
</property>
</bean>
</list>
</property>
<property name="labelStyle">
<bean class="org.geomajas.configuration.LabelStyleInfo">
<property name="labelAttributeName" value="CNTRY_NAME"/>
<property name="fontStyle">
<bean class="org.geomajas.configuration.FontStyleInfo">
<property name="color" value="#000000" />
<property name="opacity" value="1" />
</bean>
</property>
<property name="backgroundStyle">
<bean class="org.geomajas.configuration.FeatureStyleInfo">
<property name="fillColor" value="#FFFFFF"/>
<property name="fillOpacity" value="0"/>
<property name="strokeColor" value="#000000"/>
<property name="strokeOpacity" value="0"/>
<property name="strokeWidth" value="1"/>
</bean>
</property>
</bean>
</property>
</bean>
Example 6.16. ShapeInMem layer style info example
This defines the identifier, geometry object and attributes for the feature.
Attributes can be either primitive attributes or association
attributes. Primitive attributes represent primitive Java types as
well as some common types like Date and String. The following
primitive attribute types are defined: BOOLEAN, SHORT, INTEGER,
LONG, FLOAT, DOUBLE, CURRENCY, STRING, DATE, URL and IMGURL.
Association attributes represent non-primitive Java types. There are
two types of association attributes defined: MANY_TO_ONE and
ONE_TO_MANY. These reflect the many-to-one and one-to-many
relationships as defined in an entity-relationship model and can
only be used in conjunction with the
HibernateLayer.
Most feature attributes should be validated before they can be saved to a file or database. Validation is a concern that stretches across many layers of a typical application: there is usually a need for client-side validation (making the application more user friendly) , server-side validation (to protect the server from invalid data) as well as database validation (to preserve data integrity). Preferably validation rules should be defined as much as possible in a single place to avoid conflicts and duplication.
Our attribute configuration supports several types of
validation by defining a "validator" property inside
the attribute:
<property name="validator">
<bean class="org.geomajas.configuration.validation.ValidatorInfo">
<property name="constraints">
<list>
<bean class="org.geomajas.configuration.validation.DecimalMinConstraintInfo">
<property name="value" value="0" />
</bean>
</list>
</property>
<property name="toolTip" value="Area you may wish to fill in." />
</bean>
</property>
Example 6.17. Attribute validator configuration
This property contains some general validator information and a set of constraints that should be applied to the attribute. The available constraint types have been based on the new JavaBeans standard: JSR-303.
The Hibernate layer is based on the popular Hibernate O/R mapping framework. It uses a special spatial extension of Hibernate, unsurprisingly called Hibernate Spatial. The Hibernate Spatial project has its project website at http://www.hibernatespatial.org. The spatial extensions or dialects (in Hibernate language) allow the definition of spatial types and the execution of spatial queries in a database independent way.
You need to include the following dependencies to make this work. This needs to include the hibernate spatial provider, in this example, PostGis.
<dependency>
<groupId>org.geomajas.plugin</groupId>
<artifactId>geomajas-layer-hibernate</artifactId>
<version>${geomajas-layer-hibernate-version}</version>
</dependency>
<dependency>
<groupId>org.hibernatespatial</groupId>
<artifactId>hibernate-spatial-postgis</artifactId>
<version>1.0-M2</version>
</dependency>
Example 6.18. Hibernate layer dependency
A Hibernate layer cannot be defined by configuration only. As in every O/R model, there has to be a mapping between Java classes and tables in the database. In the most simple case there is a one-to-one mapping between a single class and a single spatial table.
The following listing shows the (partial) definition of a
Hibernate annotated Java class Road.java that is mapped
to a table roads in the database.
@Entity
@Table(name = "roads")
public class Road {
@Id
@GeneratedValue(strategy = javax.persistence.GenerationType.IDENTITY)
@Column(name = "gid")
private Long gid;
private Long id;
private String type;
private Float length;
private Long use;
private Short wid;
private Date date;
private String url;
@Type(type = "org.hibernatespatial.GeometryUserType")
@Column(name = "the_geom")
private Geometry geometry;
Example 6.19. Hibernate entity
The field annotations describe the relation between the fields
of the class and the columns in the table. A special annotation
@Type with type
org.hibernatespatial.GeometryUserType is used to map
the geometry field to the the_geom spatial
column.
Once the Java class mapping is finished, the actual layer
configuration can be made. An example configuration that matches the
Road.java class is shown below:
<bean name="roads" class="org.geomajas.layer.hibernate.HibernateLayer">
<property name="layerInfo" ref="roadsInfo" />
<property name="featureModel">
<bean class="org.geomajas.layer.hibernate.HibernateFeatureModel">
<property name="sessionFactory" ref="simpleSessionFactory" />
</bean>
</property>
<property name="sessionFactory" ref="simpleSessionFactory" />
<property name="dateFormat" ref="simpleDateFormat" />
</bean>
Example 6.20. Hibernate roads layer definition
The first property layerInfo is the reference to
the VectorLayerInfo object. While it can be defined
inline, it has been defined as an outer bean for clarity
here.
The featureModel property refers to the internal
feature accessor face of the layer. This property will probably be
removed as it has no additional configuration parameters for the
moment.
The sessionFactory property refers to the
Hibernate session factory. This is the same factory that has to be
defined by the transaction configuration.
The dateFormat property determines how the layer
will convert date values to strings and vice versa.
As already mentioned, the bulk part of the layer's metadata is
defined through the VectorLayerInfo object. An example
definition of this object is given below:
<bean name="roadsInfo" class="org.geomajas.configuration.VectorLayerInfo">
<property name="layerType" value="MULTILINESTRING" />
<property name="crs" value="EPSG:900913" />
<property name="maxExtent">
<bean class="org.geomajas.geometry.Bbox">
<property name="x" value="505500" />
<property name="y" value="6588000" />
<property name="width" value="2000" />
<property name="height" value="2000" />
</bean>
</property>
<property name="featureInfo" ref="roadsFeatureInfo" />
<property name="namedStyleInfos">
<list>
<ref bean="roadsStyleInfo" />
</list>
</property>
</bean>
Example 6.21. Hibernate roads layer info
The feature metadata can be found in the
FeatureInfo object. This objects contains the complete
feature type description (id, attributes and geometry) as well as
the validation rules for the attributes. An example definition of
this object is given below:
<bean class="org.geomajas.configuration.FeatureInfo" name="roadsFeatureInfo">
<property name="dataSourceName" value="mypackage.server.Road" />
<property name="identifier">
<bean class="org.geomajas.configuration.PrimitiveAttributeInfo">
<property name="label" value="gid" />
<property name="name" value="gid" />
<property name="type" value="LONG" />
</bean>
</property>
<property name="geometryType">
<bean class="org.geomajas.configuration.GeometryAttributeInfo">
<property name="name" value="geometry" />
<property name="editable" value="true" />
</bean>
</property>
<property name="attributes">
<list>
<bean class="org.geomajas.configuration.PrimitiveAttributeInfo">
<property name="label" value="Id" />
<property name="name" value="id" />
<property name="type" value="LONG" />
</bean>
<bean class="org.geomajas.configuration.PrimitiveAttributeInfo">
<property name="label" value="Length" />
<property name="name" value="length" />
<property name="editable" value="true" />
<property name="identifying" value="true" />
<property name="type" value="FLOAT" />
</bean>
</list>
</property>
</bean>
Example 6.22. Hibernate roads layer feature info
Apart from a reference to the layer info, you can set the following parameters:
| Name | Description |
|---|---|
| url | Url for the shape file. Apart from standard protocols supported by java, you can also use the "classpath:" protocol (the resource location should not start with a slash) to refer to shape files on the classpath. |
| dbtype | Database type, useful when the data store is a database. |
| parameters | You can define additional parameters which can be
passed to the GeoTools data store. These are name/value
pairs which are passed in
org.geomajas.configuration.Parameter
objects. |
Table 6.6. GeoToolsLayer configuration
In most cases, you will need to pass data using the parameters property to get a functional GeoTools layer. This would look similar to this:
<bean name="blabla" class="org.geomajas.layer.geotools.GeoToolsLayer">
<property name="parameters">
<list>
<bean class="org.geomajas.configuration.Parameter">
<property name="name" value="WFSDataStoreFactory:GET_CAPABILITIES_URL" />
<property name="value" value="aaaaaaa" />
</bean>
<bean class="org.geomajas.configuration.Parameter">
<property name="name" value="WFSDataStoreFactory:TIMEOUT" />
<property name="value" value="bbbbbbb" />
</bean>
</list>
</property>
<property name="layerInfo" ref="blablaInfo" />
</bean>
Example 6.23. GeoTools WFS configuration skeleton
The names which are valid depend on the GeoTools data store.
To access a WFS server, the following names can be used as parameters.
WFSDataStoreFactory:GET_CAPABILITIES_URL : URL for the getCapabilities document on the server instance.
WFSDataStoreFactory:PROTOCOL : determine which HTTP command use when requesting WFS functionality. Set this value to "true" for POST, "false" for GET or NULL for AUTO.
WFSDataStoreFactory:USERNAME : set the user name which should be used to authenticate the connection. This parameter should not be used without the password parameter.
WFSDataStoreFactory:PASSWORD : set the password which should be used to authenticate the connection. This parameter should not be used without the user name parameter.
WFSDataStoreFactory:TIMEOUT : specify the connection timeout in milliseconds. This parameter has a default value of 3000ms.
WFSDataStoreFactory:BUFFER_SIZE : set the buffer size for the features. This parameter has a default value of 10 features.
WFSDataStoreFactory:TRY_GZIP : indicate whether the data store should use gzip compression to transfer data if the server supports it. Default is true.
WFSDataStoreFactory:LENIENT : indicate whether the data store should do its best to create features from the provided data even if it does not accurately match the schema. Errors will be logged but the parsing will continue if this is true. Default is false.
You also have to assure the GeoTools WFS data store is added as dependency in your project. When using Maven, you can add the following dependency.
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-wfs</artifactId>
<version>${geotools-version}</version>
</dependency>
Example 6.24. GeoTools WFS dependency
This layer is backed by a shape file that is loaded in memory at startup. All layer updates are performed in memory, so this layer is not really useful except for examples.
| ShapeInMemLayer configuration | |
|---|---|
| url | Url for the shape file. Apart from standard protocols supported by java, you can also use the "classpath:" protocol (the resource location should not start with a slash) to refer to shape files on the classpath. |
Table 6.7. ShapeInMemLayer configuration
Bean layer provides an in-memory layer which is not persisted in any way. The features can be defined in the configuration file using some specialised beans. It is particularly useful for testing.
TODO.....
| Name | Description |
|---|---|
| features | List of features, which should be
org.geomajas.layer.bean.FeatureBean
instances. |
Table 6.8. BeanLayer configuration
A map is a client side object. The Geomajas back-end works almost exclusively on layers.[1]On the client side however, these layers are combined into maps. In general, the back-end never needs to know which map the layer is displayed in when doing its work. However the back-end does need to know the coordinate reference system which is used.
<bean name="sampleFeaturesMap" class="org.geomajas.configuration.client.ClientMapInfo">
<property name="crs" value="EPSG:900913" />
<property name="displayUnitType" value="METRIC" />
<property name="maximumScale" value="9.5" />
<property name="initialBounds">
<bean class="org.geomajas.geometry.Bbox">
<property name="x" value="506356" />
<property name="y" value="6588779" />
<property name="width" value="330" />
<property name="height" value="260" />
</bean>
</property>
<property name="layers">
<list>
<ref bean="osmLayer" />
<ref bean="roadsLayer" />
<ref bean="structuresLayer" />
</list>
</property>
Example 6.25. Client map configuration
The layers refers to the client layer info objects, not the server layer info or layer instances.
For the client side, a lot of additional information can be included in the map configuration. This includes information like background colour, styles which should be used for selected points, lines and polygons and whether scale bare or pan buttons should be enabled.
<property name="backgroundColor" value="#F0F0F0" />
<property name="lineSelectStyle">
<bean class="org.geomajas.configuration.FeatureStyleInfo">
<property name="fillOpacity" value="0" />
<property name="strokeColor" value="#FF6600" />
<property name="strokeOpacity" value="1" />
</bean>
</property>
<property name="pointSelectStyle">
<bean class="org.geomajas.configuration.FeatureStyleInfo">
<property name="fillColor" value="#FFFF00" />
</bean>
</property>
<property name="polygonSelectStyle">
<bean class="org.geomajas.configuration.FeatureStyleInfo">
<property name="fillColor" value="#FFFF00" />
<property name="fillOpacity" value=".5" />
</bean>
</property>
<property name="scaleBarEnabled" value="true" />
<property name="panButtonsEnabled" value="true" />
Example 6.26. Client map configuration
A map typically also contains a tool bar. If you want one, you have to specify the tools it should include.
<property name="toolbar">
<bean name="sampleFeaturesMapToolbar" class="org.geomajas.configuration.client.ClientToolbarInfo">
<property name="tools">
<list>
<ref bean="ZoomIn" />
<ref bean="ZoomOut" />
<ref bean="ZoomToRectangleMode" />
<ref bean="PanMode" />
<ref bean="ToolbarSeparator" />
<ref bean="ZoomPrevious" />
<ref bean="ZoomNext" />
<ref bean="ToolbarSeparator" />
<ref bean="EditMode" />
<ref bean="MeasureDistanceMode" />
<ref bean="SelectionMode" />
</list>
</property>
</bean>
</property>
Example 6.27. Client map configuration
Obviously the tools themselves need to be defined as well. You can pass some parameters to the tools. An example tool definition look like this.
<bean name="ZoomIn" class="org.geomajas.configuration.client.ClientToolInfo">
<property name="parameters">
<list>
<bean class="org.geomajas.configuration.Parameter">
<property name="name" value="delta" />
<property name="value" value="2" />
</bean>
</list>
</property>
</bean>
Example 6.28. Tool configuration
Note that the tool id and the names of the parameters are interpreted by the client, so it is the client face which defines the possible values.
Last but not least, you can also configure the layer tree component which may be connected to the map.
<property name="layerTree">
<bean name="sampleFeaturesTree" class="org.geomajas.configuration.client.ClientLayerTreeInfo">
<property name="tools">
<list>
<ref bean="LayerVisibleTool" />
<ref bean="LayerLabeledTool" />
<ref bean="ShowTableAction" />
<ref bean="LayerRefreshAction" />
</list>
</property>
<property name="treeNode">
<bean class="org.geomajas.configuration.client.ClientLayerTreeNodeInfo">
<property name="label" value="Layers" />
<property name="layers">
<list>
<ref bean="osmLayer" />
<ref bean="roadsLayer" />
<ref bean="structuresLayer" />
</list>
</property>
<property name="expanded" value="true" />
</bean>
</property>
</bean>
</property>
</bean>
Example 6.29. Client map configuration
This defines the tools which are available in the layer tree widget, and the tree of layers (as a node, which can contain a list of nodes etc).
Note that the layers are indicated by referring to the client configuration object.
Layer configuration is split in two (linked) parts. You have to create the actual layer which is used in the back-end, and this layer needs to know the configuration information which is also used on the client side. Secondly, there is a distinction between raster and vector layers as they each needs a lot of specific information.
[1] The only current exception is the printing command which converts maps to PDF document. Clearly this also uses the map configuration.
As Geomajas is a framework for building enterprise application, it is important to be very accurate about what exactly is considered part of the API, specifically which classes and interfaces and which methods in these classes and interfaces are considered as part of the API.
For this reason, we have introduced the "@Api"
annotation. A class or interface is only considered part of the public
API when it is annotated using "@Api". When all public
methods in the class or interface are considered part of the API, you
could use "@Api(allMethods = true)". The alternative is to
annotate the individual methods.
The API includes many interfaces. These interfaces should only be
implemented by client code when they are annotated by
"@". All other interfaces are
provided to indicate the methods available on instances which are
obtained through the API or Spring wiring and may have extra methods
added in future versions.UserImplemented
All classes and methods which are indicated with
"@Api" should also have a "@since" javadoc
comment indicating the version in which the class or method was added to
the API.
Please beware that only the annotations determine whether something is part of the API or not. The manual may discuss things which are not considered API, probably because they are experimental.
The full details about the API can be found in the published javadoc, available on the Geomajas site at http://www.geomajas.org/gis-documentation. There you can find the links for the different versions.
The API for the Geomajas back-end is contained in the geomajas-api module. This contains only interfaces, exceptions and data transfer objects. The data transfer objects are classes which only contain getters and setters. The back-end API is divided in the following packages:
command : interfaces, services and data transfer objects related with the command extension points.
configuration : data transfer objects which are used for defining the configuration in Geomajas.
geometry : Geomajas geometry related data transfer objects.
global : some general interfaces, annotations and exceptions which are relevant for a combination of several extension points or the entire API.
layer : interfaces, services, exceptions, data transfer objects and some internal objects related with the layers and objects in a layer. These include the definition of a layer, tiles, features and feature models.
security : interfaces, services and data transfer objects related with the security extension points and security handling.
service : utility services provided by Geomajas.
The back-end also contains a module geomajas-api-experimental. This contains some experimental stuff which may be promoted to the supported API when useful, or may be changed or dumped. As this is not part of the API, it may change between revisions.
For commands and plug-ins, the same rule applies as for the
back-end API. That means that the "@Api" annotation
indicates the stability of the interfaces, classes and methods.
These classes can typically be found in packages containing "command.dto" for command request and response objects or packages containing "configuration" for objects which are expected to be defined from the Spring configuration files.
The command name is also considered part of the API when the
implementing class in annotated using the "@Api"
annotation.
The GWT plug-in also uses the "@Api" annotation to
indicate classes and methods which are supported to remain stable
between minor versions of the face.
You can expect to find this annotation on all widgets, though it is likely that not all public methods will be considered part of the API.
Versions have the structure "major.minor.revision". Geomajas uses a even-odd versioning scheme for the "minor" part.
The major number indicates major changes in the framework and thus gives no guarantee about API compatibility with previous major versions.
Versions with an even minor part are considered stable and suitable for production use. Odd minor versions are used for development to work on and test new features to be released in the next stable version. The API for Geomajas needs to be upward compatible for all stable versions with same major number. Specifically this means that
No API classes or interfaces may be removed.
No API classes or interfaces may be renamed.
No API classes or interfaces may have their package name modified.
No API methods may be removed.
No API methods may have their signature changed.
No methods may be added to classes annotated using
"@".UserImplemented
Additionally, all methods and classes which are added should
include an indication of the version in which the class and/or method
was added. This is done using the "@since" javadoc comment
for the methods, class or interface.
Because of the guarantees about API, the use of the
"@Deprecated" annotation only indicates that a method or
class is not recommended to be used. The method or class will not be
removed in future versions with the same major number.
The commands are all registered in the Spring context. The "registry key" as indicated below is used to retrieve the commands. These are services, so a singleton should be sufficient for this.
The default naming for the keys is derived from the fully qualified class name. This is automatically assigned when the command is in a (sub package of) the "command" package. To determine the bean name, all parent packages of the "command" package are removed. Then the name is simplified. It will end up having "command." as prefix, optionally followed by a package, followed by the name. As there already is a "command" prefix, the "Command" suffix is removed from the name if present. When the resulting name starts or end with the sub package, then that is removed as well. For example the "org.geomajas.command.configuration.GetConfigurationCommand" class will get "command.configuration.Get" as registry key.
| GetConfigurationCommand | |
|---|---|
| Registry key | command.configuration.Get |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.EmptyCommandRequest |
| Parameters | none |
| Description | Get the client side configuration information. This returns information about all maps which have been configured. |
| Response object class | org.geomajas.command.dto.GetConfigurationResponse |
| Response values |
|
Table 8.1. GetConfigurationCommand
| GetMapConfigurationCommand | |
|---|---|
| Registry key | command.configuration.GetMap |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.GetMapConfigurationRequest |
| Parameters |
|
| Description | Get the client side configuration information for the specified map. |
| Response object class | org.geomajas.command.dto.GetMapConfigurationResponse |
| Response values |
|
Table 8.2. GetMapConfigurationCommand
| GetRasterTilesCommand | |
|---|---|
| Registry key | command.render.GetRasterTiles |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.GetRasterTilesRequest |
| Parameters |
|
| Description | Retrieve a set of raster tiles as image links for a given layer within a certain bounding box expressed in a certain coordinate reference system. |
| Response object class | org.geomajas.command.dto.GetRasterTilesResponse |
| Response values |
|
Table 8.3. GetRasterTilesCommand
| GetVectorTileCommand | |
|---|---|
| Registry key | command.render.GetVectorTile |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.GetVectorTileRequest |
| Parameters |
|
| Description | Fetches a single tile for a vector layer. The tile can contain both vectors and labels. This command is used to paint vector layers in the map. |
| Response object class | org.geomajas.command.dto.GetVectorTileResponse |
| Response values |
|
Table 8.4. GetVectorTileCommand
| LogCommand | |
|---|---|
| Registry key | command.general.Log |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.LogRequest |
| Parameters |
|
| Description | This allows you to send a statement to the server side which will be logged there. |
| Response object class | org.geomajas.command.CommandResponse |
| Response values | none |
Table 8.5. LogCommand
| MergePolygonCommand | |
|---|---|
| Registry key | command.geometry.MergePolygon |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.MergePolygonRequest |
| Parameters |
|
| Description | This command allows the user to merge multiple polygons into a single polygon or multipolygon. |
| Response object class | org.geomajas.command.dto.MergePolygonResponse |
| Response values |
|
Table 8.6. MergePolygonCommand
| PersistFeatureTransactionCommand | |
|---|---|
| Registry key | command.feature.PersistTransaction |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.PersistTransactionRequest |
| Parameters |
|
| Description | Persist a single transaction on the backend (create, update, delete of features). |
| Response object class | org.geomajas.command.dto.PersistTransactionResponse |
| Response values |
|
Table 8.7. PersistTransactionCommand
| SearchAttributesCommand | |
|---|---|
| Registry key | command.SearchAttributes |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.SearchAttributesRequest |
| Parameters |
|
| Description | Search for attribute possible values for a certain attribute. This command is only used for many-to-one and one-to-many relationships, to search for possible values. |
| Response object class | org.geomajas.command.dto.SearchAttributesResponse |
| Response values |
|
Table 8.8. SearchAttributesCommand
| SearchByLocationCommand | |
|---|---|
| Registry key | command.SearchByLocation |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.SearchByLocationRequest |
| Parameters |
|
| Description | This command allows you to search for features, based on geographic location. |
| Response object class | org.geomajas.command.dto.SearchByLocationResponse |
| Response values |
|
Table 8.9. SearchByLocationCommand
| SearchFeaturesCommand | |
|---|---|
| Registry key | command.feature.Search |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.SearchFeatureRequest |
| Parameters |
|
| Description | This command allows you to search for features, based criteria which allow matching on feature attributes. You can specify multiple search criteria and a filter. |
| Response object class | org.geomajas.command.dto.SearchFeatureResponse |
| Response values |
|
Table 8.10. SearchFeatureCommand
| SplitPolygonCommand | |
|---|---|
| Registry key | command.geometry.SplitPolygon |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.SplitPolygonRequest |
| Parameters |
|
| Description | Split up a geometry into many pieces by means of a splitting geometry. |
| Response object class | org.geomajas.command.dto.SplitPolygonResponse |
| Response values |
|
Table 8.11. SplitPolygonCommand
| UserMaximumExtentCommand | |
|---|---|
| Registry key | command.configuration.UserMaximumExtent |
| Module which provides this command | geomajas-command |
| Request object class | org.geomajas.command.dto.UserMaximumExtentRequest |
| Parameters |
|
| Description | Get the bounding box of the visible features across the requested layers (visible area for the raster layers). |
| Response object class | org.geomajas.command.dto.UserMaximumExtentResponse |
| Response values |
|
Table 8.12. UserMaximumExtentCommand
A Geomajas command usually consist of three classes, the actual
command (which implements the Command interface), and two
data transfer objects, one to pass the request parameters (extending
CommandRequest, LayerIdCommandRequest or
LayerIdsCommandRequest), and one which carries the response
(extending CommandResponse).
It is important to assure your request object extends from LayerIdCommandRequest or LayerIdsRequest when one of the parameters is the layer id (or a list thereof). This can be used by the command dispatcher to assure the layer specific (transaction) interceptors are called.
To create a new command we recommend you use a similar package structure as we used in the geomajas-extension-command module. That is to create a "command" package with under that a "dto" package which contains all the request and response objects, and to put the actual commands in sub packages based on some kind of grouping. This helps to automatically determine a sensible command name.
The basic command implementation looks like this:
package com.my.program.command.mysuper;
import com.my.program.command.dto.MySuperDoItRequest;
import com.my.program.command.dto.MySuperDoItResponse;
import org.geomajas.command.Command;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
/**
* Simple example command.
*
* @author Joachim Van der Auwera
*/
@Component()
public class MySuperDoItCommand implements Command<MySuperDoItRequest, MySuperDoItResponse> {
private final Logger log = LoggerFactory.getLogger(MySuperDoItCommand.class);
public MySuperDoItResponse getEmptyCommandResponse() {
return new MySuperDoItResponse();
}
public void execute(MySuperDoItRequest request, MySuperDoItResponse response) throws Exception {
log.debug("called");
// ..... perform the actual command
}
}
Example 8.1. Example command template
Note the presence of the "@Component" annotation which assures the command is registered. You could add the name under which the command needs to be registered in the annotation, but when that is omitted, the default command name is derived from the fully qualified class name. In the example given here this results in command name "command.mysuper.DoIt".
The default way to determine the command name assumes there is a package named "command" in the fully qualified name of the implementing class. It will remove everything before that. It will then remove a "Command" suffix if any. Lastly, it will remove duplication between the intermediate package (between "command" and the class name) and the class name itself. Some examples:
| Fully qualified class name | Command name |
|---|---|
| my.app.command.DoIt | command.DoIt |
| my.app.command.super.DoIt | command.super.DoIt |
| my.app.command.super.DoItCommand | command.super.DoIt |
| my.app.command.super.SuperDoItCommand | command.super.DoIt |
| my.app.command.super.DoItSuperCommand | command.super.DoIt |
| my.app.command.super.CommandDoIt | command.super.CommandDoIt |
| my.app.command.super.CommandSuperDoIt | command.super.CommandSuperDoIt |
| my.app.command.super.CommandDoItSuper | command.super.CommandDoIt |
Table 8.13. Samples of command name resolution
You have to include a line in your Spring configuration to scan class files for annotation to make the components available. For the case above, this could be done by including the following XML fragment in one of your Spring configuration files.
<context:component-scan base-package="com.my.program" name-generator="org.geomajas.spring.GeomajasBeanNameGenerator" />
Example 8.2. Scan to assure command is available
The command will be executed using a singleton. The use of object variables is not recommended. Any object variables will be shared amongst all command invocation, which can be coming from multiple threads at the same time.
Note that it is not mandatory to create your own request and
response object classes. If you don't require any parameters you can use
EmptyCommandRequest as request class. If you only require a
layer id, then use LayerIdCommandRequest. If you only
return a success code, you could use the
SuccessCommandResponse class.
You have to take care that all objects which are referenced by
your request and response objects are actually serializable for the
faces in which the commands need to be used. For the dojo face this may
require the use of the "@Json" annotation to exclude
fields. For GWT you have to assure the no-arguments constructor exists
and that the class can be compiled by GWT (no Hibernate enhanced
classes, no use of "super.clone()",...).
When the commands are included in a separate module, you should assure the sources are available as these are needed for GWT compilation. This can easily be done using the Maven source plugin.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
Example 8.3. Maven source plugin
Actually including the sources can then be done using a dependency like the following (this includes the springsecurity module, both the actual code and the sources). You could set "provided" scope on the source dependency to exclude it from the war file. However, this may prevent use of GWT development mode.
<dependency>
<groupId>org.geomajas.plugin</groupId>
<artifactId>geomajas-plugin-springsecurity</artifactId>
<version>${geomajas-plugin-springsecurity-version}</version>
</dependency>
<dependency>
<groupId>org.geomajas.plugin</groupId>
<artifactId>geomajas-plugin-springsecurity</artifactId>
<version>${geomajas-plugin-springsecurity-version}</version>
<classifier>sources</classifier>
</dependency>
Example 8.4. sprngsecurity source plugin - including source
When using a separate module for your commands and trying to run your project from eclipse, it is quite likely to have problems with your commands not being found. When checking the output generated at startup, the commands are sometimes not registered even though the classes exist and the correct package is being scanned. This can be caused by a class path problem and can be fixed using a context listener which assures the relevant jars are loaded. In the following excerpt, which should be part of your web.xml, make sure to add one of the class in your jar file.
<context-param>
<param-name>preloadClasses</param-name>
<param-value>
org.geomajas.command.general.LogCommand
org.geomajas.spring.GeomajasBeanNameGenerator
org.hibernate.validator.engine.ValidatorImpl
org.geomajas.plugin.springsecurity.command.dto.LoginRequest
org.geotools.data.shapefile.ShapefileDataStoreFactory
</param-value>
</context-param>
<listener>
<listener-class>org.geomajas.servlet.PrepareScanningContextListener</listener-class>
</listener>
Example 8.5. Preload class to prevent scanning problems
Layers allow access to data which needs to be displayed in a map.
For the existing layers, the details about configuring you map to include that layer are included in the configuration section above.
Geomajas has security built-in. If you don't provide a security configuration, nothing will be authorized. For unsecured access, you can add
<bean name="security.securityInfo" class="org.geomajas.security.SecurityInfo">
<property name="loopAllServices" value="false"/>
<property name="securityServices">
<list>
<bean class="org.geomajas.security.allowall.AllowAllSecurityService"/>
</list>
</property>
</bean>
Example 10.1. Allow full access to everybody
TODO.....
Table of Contents
Client-server communication in Geomajas is executed through a
series of commands instantiated on the client and executed by the
server. In the GWT face, all such commands are executed by
org.geomajas.gwt.client.command.GwtCommandDispatcher. This
class has one single method for instantiating such commands and handling
the result: the execute method.
The command, required by the execute method
(GwtCommand), is in fact a wrapper around a
CommandRequest object (see architecture), wherein the name of
the requested command is found. Each command expects a certain
implementation of the CommandRequest and
CommandResponse objects. Note that these request and
response objects must also be known server-side, and thus will not be
packaged within the GWT client packages. As a second parameter for the
execute method, a CommandCallback object is required that
will execute it's only method on a successful return of a
command.
The execution will immediately return a Deferred
object, that can be used to add more callback methods, both for
successful as for an erroneous result of the command's execution.
MySuperDoItRequest commandRequest = new MySuperDoItRequest();
// .... add parameters to the request.
// Create the command, with the correct Spring bean name:
GwtCommand command = new GwtCommand("command.MySuperDoIt");
command.setCommandRequest(commandRequest);
// Execute the command, and do something with the response:
GwtCommandDispatcher.getInstance().execute(command, new CommandCallback() {
public void execute(CommandResponse response) {
// Command returned successfully. Do something with the result.
}
});
Example 11.1. Example use of executing a command.
As with any GIS framework or application, the most crucial of all
entities is the Map. A map is represented by the
org.geomajas.gwt.client.widget., but
is set up using the standard model-view-controller paradigm. The widget
is the actual view of a map, with MapWidget
Painter objects building
the display. The user interaction is handled using controllers,
represented by the GraphicsController interface. The third
aspect, the model, is represented by the
org.geomajas.gwt.client.map..MapModel
The definition of the model behind a map
(org.geomajas.gwt.client.map.). This
object stores all map metadata and layers, and has an extensive
arsenal of methods to operate on the layers and the features.MapModel
Another aspect of the MapModel is the list of
events that it fires. Handlers for the following events can be added
to the MapModel:
MapModelEvent
: this event is fired when
the MapModel has been properly initialized. When a
MapWidget is added to the HTML page, it automatically
triggers an initialization method. This will ask the server to
supply it with the correct metadata, so that the
MapModel can actually build it's layers, etc. Once
this initialization process is done, a MapModelEvent
will be fired. Add a
org.geomajas.gwt.client.map.event.MapModelHandler to
use this.
FeatureSelectedEvent
: this event is
fired every time a feature within one of this model's vector
layers is selected. Add a
org.geomajas.gwt.client.map.event.FeatureSelectionHandler
to use this.
FeatureDeselectedEvent
: this event is
fired every time a feature within one of this model's vector
layers is deselected. Add a
org.geomajas.gwt.client.map.event.FeatureSelectionHandler
to use this.
LayerSelectedEvent
: this event is fired
every time a layer within this model is selected. Add a
org.geomajas.gwt.client.map.event.LayerSelectionHandler
to use this.
LayerDeselectedEvent
: this event is
fired every time a layer within this model is deselected. Add a
org.geomajas.gwt.client.map.event.LayerSelectionHandler
to use this.
Part of a map is this
org.geomajas.gwt.client.map.MapView object, which
determines and influences what area is currently visible. Internally
the MapView has a Camera object that you can
think of a a satellite that floats above the map. This
Camera floats at a certain height, on a certain position,
and this will determine what part of the map is shown.
The map contains several coordinate spaces.
world space : coordinates use the crs of the map.
screen space : pixel coordinates.
pan space : objects have already been scaled to the map's current scale, but the translation transformation still needs to occur. This is space is used for rendering the layers and should normally not be used for other purposes.
The MapView provides conversions of
coordinates between these spaces. Inside the MapView is a transformer
(org.geomajas.gwt.client.spatial.WorldViewTransformer)
that is able to transform coordinates, geometries and bounding boxes
from screen space to world space and back.
A work flow in the GWT face determines how editing should be
handled. It can consist of several steps, called activities. When a
work flow finishes, changes will typically be persisted. The package
is: org.geomajas.gwt.client.map.workflow
Activity:
execute : this method is called by the encompassing processor to execute the activity.
getErrorHandler : get the error handler that is specifically tuned for the activity.
WorkflowErrorHandler:
handleError
: Executed when an activity
throws an exception during execution. The
WorkflowProcessor must make sure this method is
executed.
WorkflowContext:
stopProcess
: informs the
WorkflowProcessor to stop the processing of
activities. It is the WorkflowProcessors
responsibility to ask for this, and execute no more activities
when "true" is returned.
setSeedData : provide some seed information to the context. This is usually provided at the time of work flow kickoff.
WorkflowProcessor:
supports : ensure that each activity configured in this process is supported. This method should be called by implemented subclasses for each activity that is part of the process.
doActivities : this method kicks off the processing of work flow activities.
setActivities : set a list of activities to be executed in the process. This would also be a good time to check if activities are supported.
setDefaultErrorHandler : set a default error handler, which is invoked when the activity throws an uncaught exception.
A feature in a vector layer has the possibility to be selected.
Selection usually changes the colour of the feature on the map or in a
table to make it stand out. The actual selecting handled by the
MapModel. In the map model, the selected features are
stored. After all, it is possible that by moving away from a certain
area, the selected features are no longer in sight, and perhaps no
longer in the client layer-cache. Still their contents may be needed
for specific tasks.
The MapModel fires events when changes in selection
occur. It will fire either FeatureSelectedEvent or
FeatureDeselectedEvent. The MapModel
implements the HasFeatureSelectionHandler interface so
other components can register with the MapModel as
FeatureSelectionHandler. For example the
MapWidget will register itself as a handler to know when
a feature is selected or deselected and redraw it accordingly.
When writing code that needs to react upon the selection of
features, implement the FeatureSelectionHandler
interface, and register yourself with the
MapModel.
Individual vector layers can also fire selection events.
Actually the MapModel propagates the events from the
individual vector layers, so that the user need only install his
handlers in one place.
The spatial package (org.geomajas.gwt.client.spatial)
contains a collection of math and geometry related classes and utilities
to provide all the client-side calculations one should need. If really
complex calculations need to be performed, it's best to let the server
(probably using JTS) handle it anyway. The root of the package contains
the general mathematical definitions of a Vector, Matrix, LineSegment,
and so on. It also provides a general math library and the
org.geomajas.gwt.client.spatial.WorldViewTransformer.
The WorldViewTransformer in particular can be a very
valuable tool. It allows you to transform coordinates, bounding boxes
and geometries between the 3 pre-defined spaces (world, view,
pan).
All Geometry definitions in the GWT face are based
on geometries from JTS, the Java Topology Suite, and the OGC simple
feature specification. The classes can be found in the
org.geomajas.gwt.client.spatial.geometry package.
Supported geometries are:
Point: a geometry representation of a single
coordinate.
MultiPoint: a geometry containing multiple
Point geometries.
LineString: a list of connected coordinates.
Sometimes also called a polyline.
LinearRing: an extension of the
LineString geometry that expects the last coordinate
to be equal to the first coordinate. In other words, a
LinearRing is a closed
LineString.
MultiLineString: a geometry containing multiple
LineString geometries.
Polygon: a Polygon is a two-part
geometry, consisting of an exterior LinearRing and a
list of interior LinearRings. The exterior
LinearRing, also called the shell, is the outer hull
of the geometry, while the interior rings can be seen as holes in
the exterior ring's surface area.
MultiPolygon: a geometry containing multiple
Polygon geometries.
The Geometry implementations themselves do not have
any setters methods. Instead the editing of geometries is done through
a series of operations, all implementing the
interface. This interface consists of only one method,
accepting a geometry, and returning the result as a new
geometry.org.geomajas.gwt.client.spatial.geometry.operation.GeometryOperation
Geometries have no public constructors, so the creation of new
geometries is done using a factory,
org.geomajas.gwt.client.spatial.geometry.GeometryFactory.
A GeometryFactory can be created using a spatial
reference id and a certain precision, but it can also be retrieved
from any geometry instance (where srid and precision are automatically
correct).
GeometryOperation interface:
@Api(allMethods = true)
public interface GeometryOperation {
/**
* The main edit function. It is passed a geometry object. If other values are needed, pass them through the
* constructor, or via setters.
*
* @param geometry
* The {@link Geometry} object to be adjusted.
* @return Returns the resulting geometry, leaving the original unharmed.
*/
Geometry execute(Geometry geometry);
}
Example 11.2. GeometryOperation interface
Snapping in Geomajas, is handled by a single manager class
called the
org.geomajas.gwt.client.spatial.snapping.Snapper. It is
the main handler for snapping to coordinates. It supports different
modes of operation and different algorithms for the actual snapping.
The different algorithms to use are defined in the vector layer
configuration files, while the modes are defined by the different
implementations of the SnappingMode class. Let us first
start with the different modes:
ALL_GEOMETRIES_EQUAL : this snapping mode considers all geometries equal when it comes to determining where to snap to. Depending on the snapping algorithm used, it will simply consider all nearby geometries.
PRIORITY_TO_INTERSECTING_GEOMETRIES : this snapping mode tries to snap to intersecting geometries before trying the general approach. When searching a snapping coordinate for a given point, this mode will first search for intersecting geometries and try to get a snap to that. If no snapping point can be found, it will consider all nearby geometries (like ALL_GEOMETRIES_EQUAL).
The snapping rules themselves are defined in the server-side configuration. Each vector layer can have many snapping rules. For each rule, 3 fields must be filled:
layer : the target layer to snap to.
distance : the distance over which to snap. This distance must be expressed in the map's coordinate system.
type : the snapping algorithm to use. At the moment 2 types of snapping algorithms are supported: to the nearest point (type=1), and to the nearest edge (type=2). For nearest point snapping can only occur to any coordinate which is a end-point for a geometry, for nearest edge that can be any coordinate on the edge of the geometry. Needless to say, the nearest edge requires more calculating power than the nearest point.
Snapping on the map
When a GraphicsController for the map needs to use
snapping (i.e. editing controllers), they should extend the
org.geomajas.gwt.client.controller.AbstractSnappingController
class. This class extends the GraphicsController class
(the base class for all Geomajas map controllers), and overwrites the
getScreenPosition and getWorldPosition
methods to assure the points are snapped. The
AbstractSnappingController also supports the on-the-fly
activation and deactivation of snapping.
In the GWT face, the main render method can be found
in MapWidget. The render method requires three parameters,
a paintable object, a target group to paint in and a status. The
paintable object is the actual object that needs to be painted. The
target group
(org.geomajas.gwt.client.widget.MapWidget.RenderGroup)
specifies where in the DOM to draw. The usual choices here are the
SCREEN or the WORLD groups. The rendering status
(org.geomajas.gwt.client.widget.MapWidget.RenderStatus)
determines what drawing action to take.
RenderStatus
The render status can be one of the following:
ALL : completely render or re-render the paintable object. If the paintable object contains other paintable object, go through them recursively (a map will paint layers, who in turn will paint tiles, ...)
UPDATE : update the paintable object in question, but do not update recursively.
DELETE : delete the paintable object from the map.
While rendering, the map uses a visitor to visit the paintable objects recursively and search for painters for each object or sub-object. The "ALL" status will paint recursively while the "UPDATE" status will not go deeper then the given paintable object. Of course, if a given paintable object has no recursive paintable objects, then the difference between "ALL" and "UPDATE" is irrelevant.
RenderGroup
The rendergroup that needs to be specified when calling the map's render method, represents the logical place on the map to draw the paintable object. There are four choices, each having a huge impact.
WORLD : drawing should be done in world coordinates. World coordinates means that the map coordinate system should be used. The advantage of rendering objects in the world group, is that when the user moves the map around, the objects will move with it.
SCREEN : drawing should be done in screen coordinates. Screen coordinates are expressed in pixels, starting from the top left corner of the map. When rendering objects in the screen group they will always appear at a fixed position, even when the user moves the map about.
RASTER : drawing should be done in pan coordinates. All raster layers are drawn in this group. In essence this means that the coordinates are expected to have been scaled for the current scale before drawing, and that only the translation still needs to occur. For advanced use only.
VECTOR : drawing should be done in pan coordinates. All vector layers, their selection and their labels are drawn in this group. In essence this means that the coordinates are expected to have been scaled for the current scale before drawing, and that only the translation still needs to occur. For advanced use only.
As will be explained in more detail in the "rendering manual",
there are 2 ways of drawing on the map: directly using some rendering
context, or indirectly using Paintable objects,
Painters and the MapWidget's
render method (as explained above). When using the direct
approach, one has to call the methods of one of the different
rendering contexts. A MapWidget contains a
MapContext implementation, which in turn contains 3
different contexts:
MenuContext : used for keeping details about right mouse clicks. Not used for rendering.
ImageContext : used for rendering images in HTML. All raster layers use this context.
GraphicsContext : the main vector graphics renderer. Can also render images, but uses SVG or VML to do so. When rendering shapes, circles, rectangle, etc. you will always be using this context.
The GraphicsContext is the main vector drawing
context. It has two implementations: one for SVG and one for
VML.
Every object that appears on a Geomajas map, has to implement
the Paintable interface. This interface marks types of
objects that can be painted. For each type/class of paintable object,
an accompanying Painter must be defined as well. The
painter will ultimately decide exactly how a paintable object should
be rendered. The painter will render objects using it's
paint method, or delete objects from the map using it's
deleteShape method.
GraphicsContext
: this is the basic
drawing interface. Different implementations will draw in
different technologies (i.e. SvgGraphicsContext and
VmlGraphicsContext). The whole idea of this
GraphicsContext and the painters, was inspired by the
Java AWT library. This context will draw basic shapes, according
to their id. Since we are using web technologies, all
implementations (be they SVG or VML) will use DOM elements to
create their drawings. Each DOM element in the
GraphicsContext DOM tree should have an id and a
parent. This combination of id and parent can later be used to
update or delete the object.
In essence, the drawing methods will result in changes in the visuals of the map, and the painters that will call these methods.
Paintable
: the basic definition of an
object that can be painted onto the map. For each
Paintable class, an accompanying Painter
class must be defined. The Paintable interface has
only two methods. The getId method returns the
Paintable objects id, which is it's key in the DOM
tree within the parent group. While the accept method will be
traversed by the PainterVisitor, and is used to have
the object passed to the correct Painter, which will
draw the object.
WorldPaintable
: extension of the
Paintable interface for objects that support being
rendered in world space. This means that it should be possible to
transform the object's geometry/location/coordinate/bbox.
Painter
: A Painter knows
how to paint a specific kind of Paintable object.
Exactly what class of Paintable objects it can draw,
must be made clear by it's getPaintableClassName
method. Furthermore, the Painter has two methods to
paint or remove Paintable objects on or from the
given GraphicsContext. Basically, the
Painter translates the fields and parameters of the
Paintable object into calls to the
GraphicsContext.
PainterVisitor
: Geomajas uses a visitor
algorithm for it's client side rendering process. The
MapWidget uses a PainterVisitor to
recursively traverse the tree of Paintable objects,
calling the accept method on each node.
Of course this recursive system of searching for the correct
Painter, can only work when the
PainterVisitor has all the necessary painters
registered. When registering a Painter with the
MapWidget, it will actually pass it along to this
PainterVisitor instance.
An example of the recursive painting, can be found in the
MapModel, which calls the accept methods
of it's layers, which call the accept methods of the
visible tiles, which contain features.
There are two ways to render objects onto the map. One uses
Paintable objects and Painters, the other is
by using the GraphicsContext directly. There are still
some general notions that one must know before attempting to draw on
the map. Since HTML, SVG and VML are all markup languages which use a
DOM tree as basic model, rendering basically is the adding and
removing of nodes within this tree. As parent nodes have styling
information or other attributes that reflect their child nodes, it is
very important to add nodes to the correct parent when drawing.
The GraphicsContext
reflects this, by requiring a parent object as first parameters in all
drawing methods. Associated with every node is an object that
represents it. Given this object, the GraphicsContext can
find the correct node. When using the GraphicsContext
directly, it is important to be aware of the necessity of using the
correct parent group when drawing.
This approach uses MapWidget's render method,
which requires three parameters: a Paintable object,
a target RenderGroup, and a
RenderStatus. If you are unfamiliar with these, visit
the beginning of this chapter for
a detailed description.
Let us start with an example, where we draw a rectangle in screen space (=pixel coordinates). The code would look something like this:
Rectangle rectangle = new Rectangle("myRectangle");
rectangle.setBounds(new Bbox(10, 10, 200, 200));
rectangle.setStyle(new ShapeStyle("#FF0000", .8f, "#0000FF", .6f, 2));
map.render(rectangle, RenderGroup.SCREEN, RenderStatus.ALL);
This code snippet would draw a rectangle (which implements
Paintable), called "myRectangle" in screen space (10
pixels from the top, 10 pixels from the left, and with a width and
height of both 200), using the defined style (red interior with a
blue border). To delete the rectangle again, you would have to do
something like this:
map.render(rectangle, RenderGroup.SCREEN, RenderStatus.DELETE);
Remember that there was also an extension of the
interface, designed for
rendering objects in world space. Rendering in world space means
that objects are drawn in the coordinate system of the map. This
also means that when the user moves about on the map, the object
will move with it (keeping the same location in map coordinates).
Only objects that implement the Paintable
WorldPaintable
interface can be drawn in world space.
Let us, for the next example, assume that the map has been defined using EPSG:4326 (lonlat) as coordinate system, and we would apply the following code snippet:
Rectangle rectangle = new Rectangle("myRectangle");
rectangle.setBounds(new Bbox(-60, -60, 120, 120));
rectangle.setStyle(new ShapeStyle("#FF0000", .8f, "#0000FF", .6f, 2));
// Register the rectangle to the map, so that it gets redrawn
// automatically when the user navigates on the map.
map.registerWorldPaintable(rectangle);
Starting from -60, -60 and using a width and height of 120,
we would have a rectangle that encompasses a huge part of the
world. Note that drawing objects in world space requires you to
register them with the map. This is necessary to have the map
automatically update the object's position when the user
navigates. When registering the WorldPaintable
rectangle, it is automatically drawn on the map.
When more flexibility is required from the rendering system, the map's render method might sometimes not be enough. If for example we want to render more than one object within a specific parent group. The following code snippet shows how to render a specific parent in screen space and then render a circle within this parent group:
// Create a parent group within screen space:
Composite parent = new Composite("myParent");
map.getVectorContext().drawGroup(map.getGroup(RenderGroup.SCREEN), parent);
// Draw a circle at (20, 20) with radius 10 pixels within parent group:
Coordinate pos = new Coordinate(20, 20);
ShapeStyle style = new ShapeStyle("#FF0000", .8f, "#0000FF", .6f, 2);
map.getVectorContext().drawCircle(parent, "myCircle", pos, 10, style);
Since no special parameters are added to the "myParent" node, the circle is drawn as if it were in the screen group itself. But thanks to the extra parent group, we now have the ability for apply specific styling or transformations on that parent group, and thus altering all children within it. Let us for example move the circle 100 pixels to the right:
// Translate the parent group 100 pixels to the right: Matrix m = new Matrix(1, 0, 0, 1, 100, 0); map.getVectorContext().drawGroup(map.getGroup(RenderGroup.SCREEN), parent, m);
Do not try to render objects in world space by directly
accessing the GraphicsContext. Behind the screens of
the MapWidget, the world space objects are actually
transformed and rendered in
vector
space.
This is done to avoid scaling in the DOM tree (as this is not
possible cross browsers).
This section covers the many interfaces regarding buttons, menu
items and such that make up the user interface. The specific Geomajas
widgets (i.e. LayerTree) require a specific way of doing
things. We will cover the interfaces for the Toolbar,
LayerTree, map controllers and context menus.
Toolbar
: the tool bar has two types of
default actions one can add to it (there is always the
addChild method, which can add any widget, but we are
now talking about Geomajas specific possibilities): the
ToolbarAction and the ToolbarModalAction.
The ToolbarAction is used for actions that need
immediate response upon clicking, while the
ToolbarModalAction is used for enabling or disabling a
certain state.
LayerTree
: the LayerTree has
the possibility to add buttons to it's tool bar that usually act
upon the selected layer within the LayerTree. Again two
types of actions can be added: the LayerTreeAction and
the LayerTreeModalAction. The same difference as with
the tool bar applies: the LayerTreeAction is a base
abstract class for actions that execute immediately upon clicking,
while the LayerTreeModalAction is used for enabling or
disabling a certain state (for example: toggle the layer's
visibility).
Menu
: each item in a context menu should
extend the MenuAction base class. This is your basic
starting point for easily creating new menu items or context menu
items.
Controllers on the map
: for controllers
listening to mouse events on a map, there is the
GraphicsController interface, or an abstraction under
the name of AbstractGraphicsController.
For buttons in the Toolbar or
LayerTree it is possible to add them to the
org.geomajas.gwt.client.action.toolbar.ToolbarRegistry or
org.geomajas.gwt.client.action.layertree.LayerTreeRegistry
upon application startup (before MapWidget
initialisation!). This allows you to add new buttons which can be
included in the map configuration.
The ToolbarAction is your basic abstract class for
building tool bar buttons that are executed immediately when clicked.
The class implements the ClickHandler interface and
requires your to specify an icon and a tool tip on creation.
ToolbarAction classes need to be registered in the
class. This allows you to get an instance of the widget to
put in the tool bar. The tools which are part of the GWT face are
statically defined in the class. Other tools can be added (or
overwritten) at runtime before the map is initialised.ToolbarRegistry
When a ToolbarAction is configurable (XML
configuration), it should implement the
ConfigurableAction interface. This contains a
"configure()" method which will be called for each of the
parameters which are defined in the tool configuration.
/**
* Abstract class that serves as a template for building tool bar actions. A tool bar action is an action that is
* executed immediately when the tool bar button is clicked. If you want a selectable tool bar button, have a look at
* the {@link ToolbarModalAction} class.
*
* @author Pieter De Graef
* @since 1.6.0
*/
@Api(allMethods = true)
public abstract class ToolbarAction extends ToolbarBaseAction implements ClickHandler {
public ToolbarAction(String icon, String tooltip) {
super(icon, tooltip);
}
}
Example 11.3. ToolbarAction
The ToolbarModalAction is the basic template for
creating selectable tool bar buttons. Usually they enable and disable
a certain state on the map when selected or deselected. Many of the
implementations that come with Geomajas set a new controller on the
map when they are selected.
Note that only one of these ToolbarModalActions can
be selected at any given time. In that sense they act as radio
buttons.
ToolbarModalAction classes need to be registered in
the
class. This allows you to get an instance of the widget to
put in the tool bar. The tools which are always part of the GWT face
are statically defined in the class. Other tools can be added (or
overwritten) at runtime before the map is initialised.ToolbarRegistry
When a ToolbarAction is configurable, it should
implement the ConfigurableAction interface. This contains
a configure() method which will be called for each of the
parameters which are defined in the tool configuration.
/**
* Abstract class which serves as a template for selectable buttons in a tool bar. These selectable buttons can be
* selected and deselected. With each of these actions a different method is executed. Usually this type of tool bar
* button is used to set a new controller onto the {@link org.geomajas.gwt.client.widget.MapWidget}. If you are looking
* for an action that should be executed immediately when clicking on it, have a look at the
* {@link org.geomajas.gwt.client.action.ToolbarAction} class.
*
* @author Pieter De Graef
* @since 1.6.0
*/
@Api(allMethods = true)
public abstract class ToolbarModalAction extends ToolbarBaseAction {
public ToolbarModalAction(String icon, String tooltip) {
super(icon, tooltip);
}
// Class specific actions:
/**
* When the tool bar button is selected, this method will be called.
*/
public abstract void onSelect(ClickEvent event);
/**
* When the tool bar button is deselected, this method will be called.
*/
public abstract void onDeselect(ClickEvent event);
}
Example 11.4. ToolbarModalAction
The LayerTreeAction is your basic abstract class
for building layer tree buttons that are executed immediately when
clicked. The onClick() method needs to be implemented and
it also requires you to specify an icon, a tool tip and a disabled
icon. Note that the onClick() has the selected layer
within the LayerTree as a parameter.
LayerTreeAction classes need to be registered in
the LayerTreeRegistry class. The tools which are always
part of the GWT face are statically defined in the class. Other tools
can be added (or overwritten) at runtime before the map is
initialised.
public abstract class LayerTreeAction extends ToolbarBaseAction {
private String disabledIcon;
/**
* Constructor setting all values.
*
* @param icon The default icon for the button.
* @param tooltip The default tooltip for the button.
* @param disabledIcon The icon used when the button is disabled.
*/
public LayerTreeAction(String icon, String tooltip, String disabledIcon) {
super(icon, tooltip);
this.disabledIcon = disabledIcon;
}
/**
* This method will be called when the user clicks on the button.
*
* @param layer The currently selected layer.
*/
public abstract void onClick(Layer<?> layer);
/**
* Is the this action enabled for the layer?
*
* @param layer layer to test
* @return enabled status of action for layer
*/
public abstract boolean isEnabled(Layer<?> layer);
/**
* Set icon to display when button is disabled.
*
* @return icon shown when the button is disabled
*/
public String getDisabledIcon() {
return disabledIcon;
}
/**
* Set icon for disabled state.
*
* @param disabledIcon icon for disabled state
*/
public void setDisabledIcon(String disabledIcon) {
this.disabledIcon = disabledIcon;
}
}
Example 11.5. LayerTreeAction
The LayerTreeModalAction is the basic template for
creating selectable layer tree buttons. Usually they enable and
disable a certain state for the selected layer within the layer tree
(for example that layer's visibility).
LayerTreeModalAction classes need to be registered
in the LayerTreeRegistry class. The tools which are
always part of the GWT face are statically defined in the class. Other
tools can be added (or overwritten) at runtime before the map is
initialised.
To create menu items or context menu items, Geomajas provides a
base which extends from SmartGWT's MenuItem class. It
requires you to set a title and icon. It also implements the
ClickHandler interface for defining the
onClick() execution function.
/**
* General definition of a <code>MenuAction</code>. All Geomajas actions in toolbars or context menus should build upon
* this class.
*
* @author Pieter De Graef
* @since 1.6.0
*/
@Api(allMethods = true)
public abstract class MenuAction extends MenuItem implements ClickHandler {
/**
* Constructor that expects you to immediately fill in the title and the icon.
*
* @param title
* The textual title of the menu item.
* @param icon
* A picture to be used as icon for the menu item.
*/
protected MenuAction(String title, String icon) {
super(title, icon);
addClickHandler(this);
}
}
Example 11.6. MenuAction
For interactive mouse listeners on the map there is a general
interface, GraphicsController. To write a custom
controller, you should always extend
AbstractGraphicsController.
The GraphicsController interface does NOT use
SmartGWT events as they provide no way of getting the target DOM
element from the mouse events. So the list of handlers that the
GraphicsController extends, are all basic GWT event
handlers. A separate widget (GraphicsWidget) has been
created to catch the events, while the normal MapWidget
(which encapsulates the GraphicsWidget) can still
handle SmartGWT events.
On top of all the event handling methods that come from the
different handlers, the interface also has onActivate()
and an onDeactivate() methods. The
onActivate() is called before the controller is actually
applied on the GraphicsWidget. This is usually used to
apply a new context menu on the map and such. The
onDeactivate() method is called when the controller is
removed from the GraphicsWidget. This is usually used for
cleaning up.
/**
* General interface for a controller set on a {@link org.geomajas.gwt.client.widget.MapWidget}. It should implement
* all of the available mouse handling events.
*
* @author Pieter De Graef
* @since 1.6.0
*/
@Api(allMethods = true)
public interface GraphicsController extends MouseDownHandler, MouseUpHandler, MouseMoveHandler, MouseOutHandler,
MouseOverHandler, MouseWheelHandler, DoubleClickHandler {
/**
* Function executed when the controller instance is applied on the map.
*/
void onActivate();
/**
* Function executed when the controller instance is removed from the map.
*/
void onDeactivate();
}
Example 11.7. GraphicsController
You should never directly implement
GraphicsController (not that it does not have the
"@UserImplemented" annotation), you should always extend
AbstractGraphicsController. This abstract class
implements all methods as empty methods so you don't have to clutter
your code with empty methods (often only a few of the mouse event
methods are actually used). It also has some extra methods for return
useful information for the mouse events, such as the position
(expressed in screen coordinates) or the target DOM element.
Small extract from the AbstractGraphicsController
class:
protected Coordinate getScreenPosition(MouseEvent<?> event) {
return GwtEventUtil.getPosition(event);
}
protected Coordinate getWorldPosition(MouseEvent<?> event) {
return getTransformer().viewToWorld(GwtEventUtil.getPosition(event));
}
protected Element getTarget(MouseEvent<?> event) {
return GwtEventUtil.getTarget(event);
}
protected String getTargetId(MouseEvent<?> event) {
return GwtEventUtil.getTargetId(event);
}
Example 11.8. Extract from AbstractGraphicsController
Now that you have your controller you can set it;
mapWidget.setController(new MeasureDistanceController(mapWidget));
There are more abstractions than just the
AbstractGraphicsController:
AbstractRectangleController
: abstract controller that handles drawing a
rectangle by dragging the mouse on the map.
AbstractSnappingController
: abstract controller that allows snapping to be
enabled and disabled. When enabled, the returned points when
asking getPosition(), are snapped (depending on the
configured snapping rules).
For internationalization, Geomajas uses the default GWT i18n
implementation. For Geomajas specifically, the i18n is used in several
places, each having it's own list of messages. Basically all i18n
message definitions are located in the package
org.geomajas.gwt.client.i18n, as are the properties files
containing the translations.
Several separate definitions have been created:
MenuMessages
: the
MenuMessages defines parameterized string values that
are used in the titles of MenuAction classes. Examples
are the editing context menus.
ToolbarConstants
: this defines strings
that are used as tool tips when hovering over the buttons in the
tool bar. This list of values is used by the
ToolbarAction and ToolbarSelectAction
classes. Note that for tool tips the " " character is used
instead of the default space.
AttributeMessages : .....
GlobalMessages : .....
LayerTreeMessages : .....
SearchMessages : .....
To avoid multiple instantiations of the constants and messages
classes and have a central access point for all internationalization
concerns, the I18nProvider class has been created. This
class has static methods for accessing the constants and messages
classes. Usage is as follows:
String dist = I18nProvider.getMenu().getMeasureDistanceString(totalDistance, radius); setContents( "<div><b>" + I18nProvider.getMenu().distance() + "</b>:</div><div style='margin-top:5px;'>" + dist + "</div>");
A GWT unit test should inherit from the GWTTestcase
base class and should be named GwtTestXxx.java. GWT unit
tests are run inside a development mode environment and can refer to
most of the GWT API. To run a GWT test case, run the Maven command
gwt:test or execute the integration test
phase.
This second chapter of the GWT face describes all the widgets that Geomajas has added on top of the SmartGWT widget list. Each widget will be handled in detail so that developers might get a better understanding of what they are here for, and how to use them. Know that many of these widgets are closely connected, either through configuration or coding.
The GraphicsWidget is the basic widget that allows
drawing onto a GraphicsContext, and catches mouse events at
the same time. It implements the MapContext interface and
provides it's own MenuContext implementation. As for the
VectorContext, it delegates to a browser specific
implementation (VmlGraphicsContext or
SvgGraphicsContext). It is also responsible for handling
GraphicsControllers (only one global controller at a
time!). The reason to place the controller handling here, is because we
needed a default GWT widget to handle the events, not a SmartGWT widget.
The SmartGWT events do not contain the actual DOM elements for
MouseEvents, while the default GWT events do - for some functionality it
is absolutely vital that it is well-known which DOM node was the target
of an event.
Using the MenuContext, this widget always has the coordinates of the latest right mouse click. Usually the right mouse button is used for drawing context menus. But sometimes it is necessary to have the DOM element onto which the context menu was clicked, to influence this menu. That is why this widget always stores this latest event (or at least it's DOM element id, and screen location).
This widget is the bridge between the internal Svg or Vml rendering in GWT and the SmartGWT widget library. It is used internally in the MapWidget, but is not meant to be used directly by developers.
The main map for any Geomajas application using the GWT face. This
widget controls the MapModel, the MapView
objects, has an internal GraphicsWidget for the actual
rendering, and much more. Being the most central of all widget, the
MapWidget has quite a few responsibilities and options.
Map - initialization
A first responsibility of the map is the correct initialization of
it's model and all layers from the configuration. When the MapWidget is
added to the HTML (onDraw), it will automatically fetch the
configuration from the server, and than initialize itself (more
precisely, build the MapModel). When this is done, the
MapModel will fire a MapModelEvent. Many other
widgets wait for this moment to initialize themselves, as they often
require the MapModel's contents.
View - rendering
A second responsibility lies in the ability to render shapes. The
render() method uses a PainterVisitor to
recursively go through Paintable objects and look for the
correct Painter. All Painter definitions must
be registered in the MapWidget, by means of the
registerPainter() and unregisterPainter()
methods. Also the full list of WorldPaintables is stored
within the MapWidget. For more information regarding the
rendering, using the render method, visit the
.
As an addition of the Paintable objects in
screen-space, the definition of a MapAddon has been created
as well. MapAddons are self regulating pieces of software
that are visible at a certain location on the map (in screen space!),
and optionally have attached behaviour. Examples are the Navigation
buttons and the scale bar.
Controller
To add interactivity to a map, you can add two types of
controllers: the GraphicsController and the GWT
MouseWheelHandler. For both it is possible to apply a
single instance using the setController() and
setMouseWheelController() methods.
Options
On top of the previous list of responsibilities, the map also has a few options that allow certain functionality to be present or not. The following options are standard:
navigationAddonEnabled
: this option can
be configured from within the configuration, and determines whether
or not the navigation MapAddon is visible. This
is placed in the upper left
corner of the map and allows the user to pan, zoom in and out, and
zoom to maximum extent.MapAddon
scaleBarEnabled : this option can be configured from within the configuration, and determines whether or not the scale bar is visible. This shows you the scale of the map by means of a bar of certain length, expressed in the preferred unit type (metric versus English).
zoomOnScrollEnabled
: this option
determines whether or not the ZoomOnScrollController is
active by default. This allows zooming in and out on the map using
the mouse wheel.
The overview map is an extension of the MapWidget,
which keeps the overview of a target
. It keeps track of the target map's
view, and reacts whenever that target map changes it's view. The
MapWidget
OverViewMap implements the
MapViewChangedHandler to track the changes of it's target
map. As it is an extension of a normal MapWidget, it has
all the functionality of a normal map. So you can configure layers for
an overview map, just as you would for a normal map.
The Geomajas tool bar is an extension of the SmartGWT
ToolStrip widget, and allows for many different widgets to
be added to it. A tool bar must be initialized with an instance of the
MapWidget it is related to. When the MapWidget
has successfully initialized itself, it's MapModel will
fire the MapModelEvent saying so. The tool bar reacts on
this event by searching in the map configuration for the correct list of
tool bar buttons. The map configuration can contain tool ids to indicate
the tools which need to be added, together with optional parameters.
Using the ToolbarRegistry, which contains the mappings
between these ids and the relevant ToolbarAction or
ToolbarModalAction classes, the tool bar will initialize
itself (for more info; see User
Interaction).
Existing tools which can be defined include:
EditMode : a ToolbarModalAction for editing on the map. Allows the user to create new objects in the selected layer, and allows updating and deleting of selected objects.
MeasureDistanceMode : a ToolbarModalAction which allows the user to measure distances on the map.
SelectionMode : allow selecting features either by clicking on them, or by dragging a rectangle, thus selecting the features which are inside the rectangle. You need an active (vector) layer for the selection to work. The right click menu allows clearing the selected features and toggling selection at the current position. Press shift or control while selecting to add the selection to the previously selected features. Possible parameters:
clickTimeout : when the button it released in less than the number of milliseconds specified here, then the selection is treated as a click. When it takes longer, it is treated as dragging. Default is "500" (ms).
coverageRatio : ratio of the feature which needs to be inside the selected area for the feature to be selected. When this is "1.0" then the entire feature needs to be inside the selection rectangle. Default is "0.7".
priorityToSelectedLayer : when this is "true" selection will first check the selected layer, and use default behaviour only if nothing is found in that layer. Default behaviour is to try all visible layers, from front to back.
pixelTolerance : number of pixels of tolerance allowed when trying to select features.
ZoomIn : zoom in to the map at the location clicked (will be centered), using the zoom factor which is configured.
delta : zoom in factor, should be >1 to effectively zoom in.
ZoomOut : zoom out of the map at the location clicked (will be centered), using the zoom factor which is configured.
delta : zoom in factor, should be in the ]0,1[ range to effectively zoom out
PanMode : this action allows you to pan the screen by dragging. When keeping either the shift or control key down, it is also possible to indicate an area to zoom into (like ZoomToRectangleMode ).
ZoomToRectangleMode : you can indicate a rectangle (by dragging) and it will zoom to make the selected area as big as possible while still entirely inside the map widget.
ZoomToSelection : first select some items on the map. After clicking on the the zoomToSelection button the map will be zoomed so that all selected items will fit nicely on the screen.
panToSelection : first select some items on the map. After clicking on the panToSelection button the map will be panned in such a way that the center of the selected items is in the center of the screen.
ZoomPrevious : go back to the previous zoom level (and location).
ZoomNext : go forward again, cancelling a click on ZoomPrevious .
This widget represents a view on the map model which is focused on
layers. You see the map layers in a tree, just as they are configured.
Accompanied with this view, there are buttons that define certain
actions on these layers. Originally there are no buttons in this widget,
so they have to be added manually or through configuration. These
buttons can either be single actions or selectable buttons (similar to
the Toolbar widget - see User
Interaction).
Just like the tool bar, the LayerTree waits for the
MapModel to be initialized, and also reacts to the
MapModelEvent. The layer tree configuration is contained in
the map configuration. When the MapModelEvent is fired, the
LayerTree will read configuration to know the layer tree
structure, which buttons to include,...
Below you see a screen shot of a simple LayerTree
where no layer has been selected (and thus all the buttons are
disabled):
Once a layer is selected, the LayerTree will ask all
buttons whether they should be enabled for that layer. For example, the
org.geomajas.gwt.client.action.layertree.LabelAction, which
toggles a layer's labels, is only applicable on vector layers, so if the
selected layer is a raster layer, that button will remain disabled. The
same LayerTree with a selected layer looks like
this:
The LayerTree has few public methods, but it does
quite a lot behind the screen. The tree is a SmartGWT
TreeGrid, where the LayerTree adds handlers to
the nodes and leaves (using LeafClickHandler and
FolderClickHandler), which trigger layer selection in the
MapModel. The LayerTree also listens to layer
selection events, to adjust it's own appearance. For example, when a
layer is selected, the proper node has to be selected and all the
buttons updated.
The LayerTreeAction and
LayerTreeModalAction are also specifically designed to cope
with the different stages that they should be able to display. The
LayerTreeModalAction can be disabled, enabled and selected
or enabled and deselected. For each it is imperative that clear markings
are given. This means that different icons are usually used for the
different stages. These different icons should be given to the actions
at construction time.
Currently the following actions are defined:
org.geomajas.gwt.client.action.layertree.VisibleAction
:
a LayerTreeModalAction that switches the visible flag
on the selected layer.
org.geomajas.gwt.client.action.layertree.LabelAction
:
a LayerTreeModalAction that switches the display of
labels for the selected layer.
org.geomajas.gwt.client.action.layertree.LayerRefreshAction
:
a LayerTreeAction that refreshes the selected layer on
click.
The Legend is a graphical widget that shows all
styles for the currently visible vector layers. In that sense it is
another view on the map model that only shows the style definitions that
are currently relevant. Just like the map widget, the legend is built on
GraphicsWidget. It reads the available layers from the map
model and draws a legend to match the style of these layers.
The FeatureListGrid is a table listing the attributes
of features within a single vector layer. Each feature is represented by
a row in the grid, with at the top a header that shows the attribute
label, as configured in the configuration. As only vector layers can
contain features, the grid will be empty for raster layers.
The FeatureListGrid has a few options that determine
it's behaviour and appearance:
selectionEnabled : enables or disables selection of features when selecting a row in the table. When this is enabled, the table will keep feature selection consistent with the map model. If the user selects a row, the feature will also be selected on the map.
allAttributesDisplayed : show all attributes (true) or only the 'identifying' attributes (false)? Attributes can be configured as "identifying" in the configuration. This difference allows for a select list of attributes to be visible in the grid, keeping overview. The user can always ask more details by double clicking the line.
editingEnabled
: determines whether or not
editing the attributes is allowed. When double clicking a row in the
table, a FeatureAttributeWindow will appear, containing
the feature of the selected row. This setting determines if that
window allows editing or not.
idInTable : show the feature's id in the table. This is false by default, and should not really be necessary.
This table is an extension of the SmartGWT ListGrid
widget. It automatically has grouping, filtering and sorting abilities
(and much more...). The FeatureListGrid makes it possible
to easily display features. You have to set the layer from which to
display features. Then you can add features one by one. If no layer is
set, then then "addFeature" method will not add any rows to
the table. Setting the layer will automatically create the grid header,
using the layer's attribute definitions.
The FeatureAttributeWindow is a floating window to
display and enable editing of a feature's attributes and persist these
changes. This widget is a FeatureAttributeEditor with some
extra buttons like "save". When setting a feature, it first makes a
clone so you are not editing the feature directly and changes are only
applied when the save is clicked. This widget also checks whether or not
all fields are valid, and will not allow saving when at least one of the
fields is not valid.
The FeatureAttributeWindow has the following
options:
editingAllowed: is editing allowed? This must be set before the widget is actually drawn.
editingEnabled: is editing currently enabled or not? This widget can toggle this value on the fly. When editing is enabled, it will display an editable attribute form with save, cancel and reset buttons. When editing is not enabled, these buttons will disappear, and a simple attribute form is shown that displays the attribute values, but does not allow editing. This effect only works when editingAllowed is true.
Below is a screen shot of a FeatureAttributeWindow
with editing allowed but not enabled. Without the editing allowed, there
would be now "edit" button. The button itself triggers setting editing
enabled.
This widget monitors client-server communication traffic, and
displays that activity to the end-user. It's purpose is inform the user
that a server request is in progress. For example, when the user zooms
in, it can sometimes take a few seconds before everything is redrawn.
This widget displays that traffic by listening to the
GwtCommandDispatcher events. On the screen shot below, you
can see the difference between it being passive and active:
Combo box for changing the scale on the map which can be added to
a tool bar. It displays a list of possible scales to choose from, but
also allows the user to type a specific scale. The scale select is
constructed with a MapView as parameter. If this
MapView contains pre-configured resolutions (zoom-steps -
these can be set in the configuration), than the scale select will allow
selection from these scales. If no resolutions are present, the scale
select will automatically choose relevant scales to choose from.
Using the setScales() method, one can always override
the list of scales in the widget.
Widget that supports searching for features on the attributes.
Requires a value for manualLayerSelection at construction
time. If true, a select box will be shown so the user can select what
layer to search in. The possible list of layers consists of all the
vector layers that are present in the map model. If false, this widget
will search in the currently selected layer.
When the "search" button is indicated, the search will be
performed server-side. When the result returns, a
SearchEvent is fired. This event holds a reference to the
VectorLayer in which the search took place, and a list of
all the features that were found. In order to do something with the
results (such as displaying in a FeatureListGrid), add a
SearchHandler. For the specific case of displaying the
feature in a FeatureListGrid, there is a
DefaultSearchHandler that already does this.
Note that there is a limit to the number of features that are
returned. By default this limit is set to 100. Modify
maximumResultSize to change this. Note that a high limit
can have a serious impact on performance and memory consumption!
Table of Contents
For the dojo face to function properly, it is important that your web.xml contains the references to the necessary information and servlets. Let's dissect an example web.xml file.
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Geomajas dojo face example application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
applicationContext.xml
layerBelt.xml
layerBluemarble.xml
layerCities.xml
layerCountries.xml
layerLakes.xml
layerOsm.xml
layerProvinces.xml
layerRivers.xml
layerRoads.xml
layerStructures.xml
layerBeans.xml
mapBeans.xml
</param-value>
</context-param>
<listener>
<listener-class>org.geomajas.servlet.GeomajasContextListener</listener-class>
</listener>
Example 13.1. Context listener and configuration locations
The file starts with the reference and configuration of the Geomajas context listener. This assures the application configuration is available for Geomajas and indicates the location of the additional configuration files.
<servlet>
<servlet-name>PdfServlet</servlet-name>
<servlet-class>org.geomajas.extension.printing.PdfServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>JsonServlet</servlet-name>
<servlet-class>org.geomajas.dojo.server.servlet.JsonServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>ResourceServlet</servlet-name>
<servlet-class>org.geomajas.servlet.ResourceServlet</servlet-class>
<!--
<init-param>
<description>
When this is specified, files are searched here first.
Files which are found at this locations are not cached.
</description>
<param-name>files-location</param-name>
<param-value>/home/joachim/apps/java/geomajas/geomajas/face/geomajas-face-dojo/geomajas-dojo-client/src/main/resources</param-value>
*** OR - only one path can be set ***
<param-value>/home/joachim/apps/java/geomajas/application/geomajas-dojo-example/geomajas-dojo-example-client/src/main/resources</param-value>
</init-param>
-->
<load-on-startup>3</load-on-startup>
</servlet>
Example 13.2. Servlet definitions
Next up is the definition of the actual servlets which are needed. The order in which they need to be initialised is also specified.
PdfServlet: this is used to produce printable maps
in PDF format.
JsonServlet: the servlet which allows you to
execute commands by sending the request as JSON and which returns the
result as JSON as well.
ResourceServlet: this allows you to get the
Geomajas and dojo script files assuring that they are properly cached
and gzip compressed.
<servlet-mapping>
<servlet-name>PdfServlet</servlet-name>
<url-pattern>*.pdf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>JsonServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ResourceServlet</servlet-name>
<url-pattern>/js/*</url-pattern>
</servlet-mapping>
</web-app>
Example 13.3. Servlet mappings
To finish, the servlet mappings need to be defined. You don't really have a lot of choice here, as the client side JavaScript expects these values.
In many cases you also want to do some client side configurations. This can be done in one of two ways.
You can include a "geomajas-constants.js" file in your code (the name of the file can differ).
var djConfig={
isDebug: false,
parseOnLoad: true,
usePlainJson: true,
locale: "en"
};
var geomajasConfig={
dijitTheme: "soria",
showLog: true,
useLazyLoading: true, // use lazy loading
lazyFeatureIncludesDefault: 12, // by default, only include style + label
lazyFeatureIncludesSelect: 15, // attributes + geometry + style + label (see GeomajasConstant)
lazyFeatureIncludesAll: 15 // attributes + geometry + style + label (see GeomajasConstant)
};
Example 13.4. Define constants in external file
You can override some configurations in your html file, on the script tags in head..
<script type="text/javascript" src="js/dojo/dojo.js"
djConfig="parseOnLoad:true, usePlainJson :true"></script>
<script type="text/javascript" src="js/example/example.js"
geomajasConfig="showLog:false, connectionPoint:'generalConnectionPoint'"></script>
Example 13.5. Configure Dojo and Geomajas on script inclusion
To use the Google maps raster layer in the dojo face, you have to include the Google maps API files in your application to allow Geomajas to use those. This can be done using code like the following
<script type="text/javascript"
src="http://maps.google.com/maps?file=api&v=2&sensor=false&key=ABCDEF">
</script>
Example 13.6. Google API inclusion
or
<script type="text/javascript" src="http://www.google.com/jsapi?key=ABCDEFG">
</script>
<script type="text/javascript">
google.load("maps", "2.95");
</script>
Example 13.7. Google API inclusion using Google loader
Note that you do need to have a Google maps key ("ABCDEF" is used in the example above) if you want to deploy your site (it should work without when running your tests on "localhost").
In your layer, you can determine the kind of layer by setting the "satellite" property. When this is true, satellite images will be displayed. When this is false, the normal Google maps images are used (street map).
Each widget needs a list of parameters, use case, information about the commands it uses, how to customize, list of topics it consumes and provides.
TODO.....
For simple applications which don't have any custom JavaScript code, the dojo-simple project layout with only one module is recommended. Anything more advanced is better of using the project layout of the dojo-example project. This allows using dojo's shrinked builds which combine all JavaScript in one large cachable file, thus optimizing load time. As this shrinking processes has a massive impact on project build time (and makes the code difficult to debug), you want to be able to test without this skrinking step. This is tackled using the multi-module project. It has the following sub-projects :
| geomajas-dojo-example-client | JavaScript module code |
| geomajas-dojo-example-modules | generates dojo layer script with collected modules |
| geomajas-dojo-example-modules-shrinksafe | generates compressed version (shrinksafe) of dojo layer |
| geomajas-dojo-example-modules-war | web application with html and configuration |
Table 15.1. List of dojo-example projects
As opposed to the conventional practice of putting JavaScript directly in the war project, our module concept requires you to put this code in a separate jar project. This jar will be added to the web application as a normal java library and the JavaScript files in it will be served from the class path by the ResourceServlet. The appropriate mapping should be added to the web.xml:
<servlet-mapping>
<servlet-name>ResourceServlet</servlet-name>
<url-pattern>/js/*</url-pattern>
</servlet-mapping>
Example 15.1. ResourceServlet mapping
By structuring JavaScript code as jar artifacts, our Maven dojo plug-in is capable of making this code accessible to the browser by importing a single dojo layer script in the web page. If the name of the layer is example , the import statements will be:
<script type="text/javascript" src="js/dojo/dojo.js"
djConfig="parseOnLoad:true, usePlainJson :true"></script>
<script type="text/javascript" src="js/
example
/
example
.js"
geomajasConfig="showLog :false"></script>
Example 15.2. Custom build script inclusion
As you can see, some configuration parameters can be passed by adding a geomajasConfig attribute to the script tag, exactly like dojo does with djConfig. The above import statements will be exactly the same for the compressed or uncompressed version of the layer. While the compressed version contains all necessary code in a single file (using the dojo custom build facility), the uncompressed version is just a set of dojo.require(...) statements, one for each module.
Our Maven dojo plug-in has two different configurations, depending on whether the custom build should be applied or not. The two different configurations have been applied in the geomajas-dojo-example-modules-shrinksafe and geomajas-dojo-example-modules projects, respectively. By defining a Maven dependency in geomajas-dojo-example-modules-war on one or the other, the compressed or uncompressed version of the JavaScript libraries is used. The switch between the two options is managed by a Maven profile in the parent project (geomajas-dojo-example). This should be passed in the Maven build command:
compressed/shrinked build: mvn install
uncompressed build: mvn -Pnoshrink install
The Maven dojo plug-in is capable of executing a custom dojo build from within Maven. This essentially collects and/or compresses (using shrinksafe) JavaScript modules that have been bundled as jar artifacts.
A typical configuration looks as follows:
<plugin>
<groupId>org.geomajas</groupId>
<artifactId>geomajas-maven-dojo</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
<configuration>
<layerName>example</layerName>
<localeList>en,fr,nl,pt,sp</localeList>
<modules>
<module>
<groupId>org.geomajas</groupId>
<artifactId>geomajas-dojo-client</artifactId>
<version>${geomajas-face-dojo-version}</version>
<requires>geomajas.geomajas</requires>
<type>jar</type>
</module>
<module>
<groupId>org.geomajas</groupId>
<artifactId>geomajas-dojo-example-client</artifactId>
<version>${project.version}</version>
<requires>tutorial.tutorial</requires>
<type>jar</type>
</module>
</modules>
</configuration>
</execution>
</executions>
</plugin>
Example 15.3. Maven Dojo plugin configuration
The plug-in has the following goals:
collect: generates a dojo layer script with the
necessary dojo.require() statements to import all the
modules
build: generates a compressed version
(shrinksafe) of the dojo layer script
The following parameters can be configured:
| Name | Description | Default value |
| layerName | The name of the target layer file (without the ".js"
extension). The file is put in its own module directory and has
to be imported as follows: <script src="js/ layerName / layerName .js"/> |
modules |
| localeList | List of locales to be included in the build file (see dojo custom build parameters) | en |
| layerOptimize | Compression type to be used (see dojo custom build parameters). Options include "shrinksafe", "shrinksafe.keepLines", "packer". | shrinksafe |
| modules | A list of module tags, one for each JavaScript module.
Beware:
Each module tag should provide a requires
parameter. This parameter is the argument of the
|
empty list |
Table 15.2. Dojo plug-in configuration parameters
The output of the plug-in consists of generated JavaScript files.
The precise output dependens on the goal. If the goal is
"collect", it is a single file. If the goal is
"build", it contains the complete contents of the release
directory of the dojo build. In all cases, the JavaScript output is
generated in the folder
<projectdir>/target/generated-resources/dojo, which
is added as a resource folder to the Maven project.
Table of Contents
The gwt-client module no longer automatically adds the "nl" locale to the application. This should now be done by the application. You can do this by adding the line
<extend-property name="locale" values="nl"/>
to your gwt.xml file.
In the GWT face, you should now use MapContext
instead of directly accessing GraphicsContext.
RasterLayer.paint() now throws
GeomajasException instead of
RenderException. The RenderException
class has been moved to api-experimental.
LocaleSelect now needs a parameter in the
constructor. This parameter is the name of the default
language.
The OpenStreetMap layer changes changed groupId from "geomajas-layer-opentreetmaps" to "geomajas-layer-opentreetmap".
GeomajasSecurityException has moved from
"org.geomajas.global" to
"org.geomajas.security".
AllowAllSecurityService has moved from
"org.geomajas.internal.security" to
"org.geomajas.security.allowall".
VectorLayerService and
RasterLayerService have moved from
"org.geomajas.service" to
"org.geomajas.layer".
In LabelStyleInfo the style for the font is now
of type FontStyleInfo.
LayerIdsCommandRequest has been introduced and
this is now extended by SearchByLocationRequest (no
change) and UserMaximumExtentRequest (changing
includeLayers to layerIds).
SuccessCommandResponse class contained typos. The methods
isSucces() and getSucces() have been
renamed to isSuccess() and getSuccess()
respectively.
Changes in pipeline and promotion to stable API.
The method getRasterLayer() has been added in
ConfigurationService.
The findMathTransform() method in
GeoService now throws GeomajasException
instead of FactoryException.
InternalTile changes (should not affect anybody as these are used internally in the back-end).
Many DtoConverterService methods now throw
GeomajasException.
The method getId() has been added to
Layer.All server layers should have a unique id. The
id is automatically assigned based on the Spring bean name.
Configuration changes: maxTileLevel has been
removed as this was not used.
Configuration changes: the server-side layers are no longer connected to the client-side layer configurations via the layerInfo objects. Instead, client-side layers refer directly to the server layer's id via a serverLayerId property. The references to the layerinfo objects are injected by a configuration postprocessor, so the layerInfo should no longer be set manually.
| Name | Property | Description |
|---|---|---|
| LayerInfo | id | Removed, use id property of Layer instead |
| SnappingRuleInfo | layerInfo | Replaced with serverLayerId |
| serverLayerId | String ,should refer to id of Layer bean |
Table A.1. Back end configuration changes
| Name | Property | Description |
|---|---|---|
| ClientLayerInfo | serverLayerId | String, should refer to id of Layer bean |
| layerInfo | Should no longer be set manually, will be set by Spring |
Table A.2. Client configuration changes
The LayerModel class has been integrated in
VectorLayer. This modifies the configuration. Where
before you would have written
<bean name="countriesModel" class="org.geomajas.layermodel.shapeinmem.ShapeInMemLayer">
<property name="url" value="classpath:shapes/africa/country.shp"/>
</bean>
<bean name="countries" class="org.geomajas.internal.layer.layertree.DefaultVectorLayer" >
<property name="layerInfo" ref="countriesInfo" />
<property name="layerModel" ref="countriesModel" />
</bean>
into
<bean name="countries" class="org.geomajas.layer.shapeinmem.ShapeInMemLayer">
<property name="layerInfo" ref="countriesInfo" />
<property name="url" value="classpath:shapes/africa/country.shp"/>
</bean>
Note that this includes changing "layermodel" to "layer" in all module and package names.
FeaturePainter interface and related stuff has
been removed. These are obsolete with the introduction of the
VectorLayerService.
GeotoolsLayer has been renamed
GeoToolsLayer.
With the change in directory structure, the commands have
moved from the org.geomajas.extension.command package
to org.geomajas.command. The LogCommand
has also been moved into the general
sub-package.
Security constraints are now applied in Geomajas. By default, nothing is authorized, so you always have to configure at least one security service. To go back to the old (allow-all) behaviour, include the following excerpt in your configuration file.
<bean name="security.securityInfo" class="org.geomajas.security.SecurityInfo">
<property name="loopAllServices" value="false"/>
<property name="securityServices">
<list>
<bean class="org.geomajas.security.allowall.AllowAllSecurityService"/>
</list>
</property>
</bean>
Layers are now more sensitive to the attributes which are
defined for the layer. Attributes which have not been defined in the
feature info are not accessible this is the result of the
refactoring where the InternalFeature store attributes
as Attribute objects).
The geomajas-API has been split up in a formal (geomajas-API) and experimental API (geomajas-api-experimental). All interfaces/classes from the cache and rendering packages have been moved to experimental. This means that the rendering pipeline is at the moment not a part of the official API, but instead more of a preview of what's to come. Furthermore, some major changes have been made in many other packages:
The org.geomajas.rendering.tile has been moved
to org.geomajas.layer.tile
Introduction of a DtoConverterService that is able to convert DTO objects from and to back-end internal representations.
All the different feature definitions have been cut down.
Only 2 versions remain at the moment: a DTO feature
(org.geomajas.layer.feature.Feature) and a feature
definition used internally in the backed
(org.geomajas.layer.feature.InternalFeature).
All the different tile definitions have been cut down. Only
3 remain. 2 DTO tiles:
org.geomajas.layer.tile.VectorTile - used in vector
layers and org.geomajas.layer.tile.RasterTile - used
in raster layers. The third is the
org.geomajas.tile.InternalTile. This tile is used
internally on the back-end.
GeometricAttributeInfo has been renamed to
GeometryAttributeInfo.
ApplicationService has been renamed to
ConfigurationService.
The configuration API has been split up in a back-end part and a client (or faces) part. The following general rules have been kept in mind:
Back-end configuration should be restricted to those properties that are functionally needed on the back-end. We essentially regard the back-end as a container of layers or, in WFS terms, feature types. Higher level concepts like map or application should be dealt with at the client (or faces) level.
Client configuration should not impact the back-end state. In the near future, this will make it possible to reconfigure clients without restarting the server.
The configuration API has profoundly changed. Where
possible, the back-end classes have retained their original (before
the split) names, after pruning them to remove all client related
information. The client classes have been mostly created from scratch
and have been named ClientXxxInfo.java for consistency.
They have been located in a separate package, called
org.geomajas.configuration.client.The following table
gives a top-down overview of the back-end configuration classes (new
classes and properties have been marked in
bold
):
| Name | Property | Action or description |
|---|---|---|
| ApplicationInfo | * | removed |
| LayerInfo | label | moved to ClientLayerInfo |
| visible | moved to ClientLayerInfo | |
| viewScaleMin, viewScaleMax | moved to ClientLayerInfo | |
| VectorLayerInfo | labelAttribute | moved to LabelStyleInfo |
| snappingRules | moved to ClientVectorLayerInfo | |
| styleDefinitions | replaced by namedStyleInfos | |
| creatable, updatable, deletable | moved to ClientVectorLayerInfo (automatically assigned) | |
| namedStyleInfos | list of NamedStyleInfo. Lists the predefined styles available for this layer. Multiple styles are possible so clients can choose a style | |
| RasterLayerInfo | style | moved to ClientRasterLayerInfo |
| NamedStyleInfo | featureStyles | list of FeatureStyleInfo. Ordered list of style definitions with applicable filters. Together with the label style they define a single named layer style. |
| labelStyleInfo | label attribute name and style | |
| FeatureStyleInfo | * | replaces StyleInfo same properties except for index |
| index | replaces id (automatically assigned) | |
| LabelStyleInfo | * | replaces LabelAttribute, same properties |
| ValidatorInfo and XxxConstraintInfo | * | moved to package
org.geomajas.configuration.validation
|
Table A.3. Back end configuration changes
The most important changes are:
The removal of client-side properties like visible, label, viewScaleMin, viewScaleMax, style and snapping rules. These are moved to the client configuration (see hereafter).
The replacement of the single style definition list by a set of named styles. These are styles that are preconfigured in the back end.
Inclusion of the label attribute name and style as part of the named style. This is more logical and in line with the SLD (Styled Layer Descriptor) specification.
The client or face classes are largely new and have been
relocated to the org.geomajas.configuration.client
package. The following table gives a top-down overview of the back-end
configuration classes (new classes and properties have been marked in
bold
):
| Name | Property | Action or description |
|---|---|---|
| ClientApplicationInfo | name | removed |
| ClientMapInfo | maxBounds | replaces MapInfo, optional maximum extent of the map, if present it will be used instead of the union of the layers' maximum extent |
| ClientLayerInfo | label | moved from LayerInfo |
| visible | moved from LayerInfo | |
| viewScaleMin, viewScaleMax | moved from LayerInfo | |
| layerInfo | reference to back-end LayerInfo | |
| maxExtent | transformed extent from back-end | |
| ClientVectorLayerInfo | snappingRules | moved from VectorLayerInfo |
| namedStyleInfo | The style to apply on the layer. Should be a reference to one of the back-end layer's predefined styles (see VectorLayerInfo). | |
| creatable, updatable, deletable | moved from VectorLayerInfo | |
| featureInfo | optional replacement of the back-end layer's FeatureInfo. If present, it is used instead. | |
| ClientRasterLayerInfo | style | moved from ClientRasterLayerInfo |
| ClientLayerTreeInfo | * | rename of LayerTreeInfo, same properties |
| ClientLayerTreeNodeInfo | * | rename of LayerTreeNodeInfo |
| layers | list of ClientLayerInfo objects, replaces previous list of layer ids | |
| expanded | changed from string to boolean | |
| ClientToolbarInfo | * | rename of ToolbarInfo |
| ClientToolInfo | * | rename of ToolInfo |
Table A.4. Client configuration
Apart from these changes in content, some general technical improvements have been made as well:
The Spring bean name (or id) is used to set the id property
of the class if there is one. This makes it unnecessary to define
the id separately. The way this is done is by using a Spring
BeanPostProcessor. (see
org.geomajas.internal.configuration.ConfigurationBeanPostProcessor)
Some calculations that were previously done in the
GetConfigurationCommand are now done in the
ConfigurationBeanPostProcessor.
Cloning of the client configuration classes can be done with general deep cloning techniques like serialization, bypassing the need for custom cloneable implementations.
As usual, example configurations can be found in the application projects.
"layerRef" is renamed to "layerIds" in
LayerTreeNodeInfo.
Configuration has changed from the proprietary format to using Spring configuration.
There is now a CommandDispatcher service and
official command names and defined request and response objects.
Deprecated commands have been removed.
In your application.xml, you should change "OSMLayerFactory" to "OsmLayerFactory"
In your application.xml, you should change "WMSLayerFactory" to "WmsLayerFactory"
replace package "layermodels" with "layermodel"
replace "org.geomajas.core.application.DefaultLayerFactory" with "org.geomajas.internal.application.DefaultLayerFactory"
mapWidget.addController() and mapWidget.removeController() have been removed. They are replaced by mapWidget.setController(). You could only add one controller anyway.