MAPS FOR ENTERPRISE APPLICATION.

 

Introduction

 

The goal of this project is to make maps development for your project as simple as possible. The implementation is based on the JS framework OpenLayers. Client’s look and feel is closed to Google maps. Also the clients don’t need to install any extra components for their web browsers. I even was able to test it on Apple IPhone and it worked correctly.

Every feature of the map is a component. Component consists of a java bean and a velocity template. Map rendering is based on velocity templates. Every template produces separate java script class at the web page during the page rendering time. Initialization parameters as long as current available templates are given through Spring framework.

Interaction of JS classes is preformed through event manager and there is no direct reference form one JS class to the other. This approach gives you a possibility to have only those components at the page you currently need.

AJAX is based on JSON, a lightweight java script data-interchange format. JS object is delivered to correspondent POJO and vice versa. AJAX controllers and their configurations are defined is Spring configuration. All a JS class has to do is to fire the event with its JS object is to be delivered. All the details of AJAX transport will be done by framework itself.

 

Velocity – based javascripts

 

Map java scripts are individual components (classes) and they are not coupled. Every component must inherit java script class:

 

MapControl = OpenLayers.Class.create();

            MapControl.prototype= {

                                   map: null,

                                   events: null,

 

       init: function(map, events){

            this.map = map;

            this.events = events;

        },

 

        CLASS_NAME: "MapControl"

       }

 

As you can see it has only one method: init where there are two parameters: map and events. Map is a façade to OpenLayers map and events are delivered by event manager. Event manager is an object which notifies other components about some activities for the current control (Observer pattern). By subscribing to events you can intercept messages thrown by other components. Basically it performs interoperability inside the framework and solves the problems of creating decoupled components.

Notification example:

this.events.fireEvent('changeWMSLayer', {id:layer.idname:layer.name, visible: layer.checked} );

Subscribing example:

this.events.attachEventHandler( 'changeWMSLayer', this.changeWMSLayer.bind(this));

 

 

 

When the whole java script is loaded the function init will be called for every single component and components should perform initialization.

You are required to provide some data for components form you java back end. And its done by using the elegance of Spring framework. Let’s have a look at some example where we going to use Zoom To component:

First we need to create the java bean:

 

public class ZoomToControl  extends MapAbstractControl {

 

            private Extent zoomToExtent;

 

 

            @VelocityParam(name = "zoomToExtent")

            public Extent getZoomToExtent() {

                        return zoomToExtent;

            }

 

            public void setZoomToExtent(Extent zoomToExtent) {

                        this.zoomToExtent = zoomToExtent;

            }

 

}

 

We are extending basic control MapAbstractControl which has the common properties.

Then note that we use annotation VelocityParam(name = "zoomToExtent"). In the velocity template the variable with the name zoomToExtent corresponds the getter of the property zoomToExtent.

Then we need to create velocity template where our current JS component will be generated from when the page loads:

 

${javaScriptClassName} = OpenLayers.Class.create();

 ${javaScriptClassName}.prototype =  OpenLayers.Class.inherit ( MapControl, {

                       

                        // from map

                        init: function(map, events){

                                    MapControl.prototype.init.apply(this, arguments);

                                   

                        #if (${zoomToExtent})

                                   map.zoomToExtent(new OpenLayers.Bounds($zoomToExtent.minX,$zoomToExtent.minY,$zoomToExtent.maxX,$zoomToExtent.maxY));

                        #end

 

                        #if (${zoomToPoint})

##                    to do

                        #end

                       

        },

       

             CLASS_NAME: "$javaScriptClassName

 });

You see that we using some velocity parameters which will substituted by constants during rendering time. Also some primitive velocity logic is used.

The reason I use velocity is that you have a feeling that you are working with JS itself, while it is still a template.

Also all the java variables are defined in velocity by annotations as mentioned above.

And the last step we need to create a Spring configuration:

 

Extent POJO:

                        <bean id="extentWMS" class="org.emaps.Extent">

                        <property name="maxY" value="88.112" />

                        <property name="minY" value="10.14911608" />

                        <property name="maxX" value="-49.1132" />

                        <property name="minX" value="-205.075" />

                        </bean>

 

ZoomTo bean:

 

            <bean id="zoomToControl" class="org.emaps.control.ZoomToControl">

                        <property name="javaScriptClassName" value="ZoomToControl" />

                        <property name="tempateName"

                                   value="/WEB-INF/map-templates/zoomto.vm" />

                        <property name="zoomToExtent" ref="extentWMS" />

            </bean>

 

Here are the following properties:

javaScriptClassName   – class name as it appears on the html page

tempateName                          - velocity template

zoomToExtent             - our zoom property

 

Then

We need to register it in the main controller in order it appears on the page its method init is called.

            <bean id="baseWMSMap" class="org.emaps.GisMap">

                        <property name="javaScriptClassName" value="GisMap" />

                        <property name="extent" ref="extentWMS" />

                        <property name="tileSize" ref="tileSizeWMS" />

                        <property name="maxResolution" value="0.15228550153247" />

                        <property name="units" value="degree" />

                        <property name="mapDiv" value="mapDiv" />

                        <property name="numZoomLevels" value="12" />

                        <property name="childernControls">

                                   <list>

                                               <ref bean="waitControl" />

                                               <ref bean="wmsLayer" />

                                               <ref bean="ajaxDispatcherControl" />

                                               <ref bean="navigationControl" />

                                               <ref bean="vectorFeaturesControl" />

                                               <ref bean="layerSwitcherControl" />

                                               <ref bean="zoomToControl" />

                                   </list>

                        </property>

                        <property name="tempateName"

                                   value="/WEB-INF/map-templates/gismap.vm" />

            </bean>

 

As you can see we are registering the components we are going to use only. The main controller will generate java script at the page for every single control and it will take care about brining java beans values to the page through velocity. After page is loaded the main controller initialized the event manager and Open layers map and then call method init for all the registered controls. At this step controls should initialize themselves and register to the events they are going to listen.

 

Java script event model

 

As you can see the control doesn’t know about the existence the others. This is done in order to use only those controls you need and simplify you map development.

But they definitely should communicate and event manager solves this issue.

Let’s have a look at the change layer mechanism.

 In order to change a layer on the map we need to have at least two controls:

-         layers switcher (checkbox)

-         layers itself (map image)

 

So when the user toggles to box we notify the event manager by firing an event:

 

this.events.fireEvent('changeWMSLayer', {id:layer.id,

            name:layer.name, visible: layer.checked} );

 

None, one or many other controls may subscribe to this message. One of the subscriber is layer control:

this.events.attachEventHandler( 'changeWMSLayer', this.changeWMSLayer.bind(this));

After message has been thrown is going to be delivered here the java script method  changeWMSLayer of the MultiTileLayer control will be executed:

 

        changeWMSLayer : function(layer) {

          for (var i=0;i<this.layerArray.length;i++) {

          var l =this.layerArray[i];

            if (layer.id == l.id) {

              l.visible = layer.visible;

              this.wmsLayer.params.LAYERS=this.getWmsServerUrl();

              this.wmsLayer.redraw();

              break;

            }

          }

        },

 

 

Ajax calls

 

As you can see from above everything are controls and it doesn’t matter if they perform visual activity or showing something or just working like mediators or controllers. One of these is Ajax control.

I tried to simplify java script communication to java and made a solution where you send java script object and you have java object at the back end and vise versa.

First you need to create java bean where you ajax object are going to be delivered to:

 

public class AjaxVectorGeometry {

            private AjaxGeometryXY[] xy;

            private String shapeName;

 

            @AjaxParam(name = "xy")

            public AjaxGeometryXY[] getXy() {

                        return xy;

            }

 

            @AjaxParam(name = "xy")

            public void setXy(AjaxGeometryXY[] xy) {

                        this.xy = xy;

            }

 

            @AjaxParam (name ="geometry")

            public String getShapeName() {

                        return shapeName;

            }

 

            @AjaxParam (name ="geometry")

            public void setShapeName(String shapeName) {

                        this.shapeName = shapeName;

            }

}

The annotations mach java script variable names.

Then from any place of our page or from any other component you need to fire the event:

 

myMap.events.fireEvent('ajaxVectorFeatures',{vectorFeatures:shapes});

 

shapes is plain java script object and it consists of the flowing fields:

-         shapes.geometry - String

-         shapes.xy         - Array of XY

 

As you can see java script has these properties by annotations, e.g. @AjaxParam (name ="geometry")

 

If the request is synchronous event manager will notify  a subscriber by thrown the event:

 

this.myMap.events.attachEventHandler( 'ajaxVectorFeaturesOk', function(d) {

                        this.myMap.events.fireEvent('hideWaitIcon');

                         var table = new Element('table',{border:'2px solid black'});

                         var tbody = new Element('tbody');

                         table.appendChild(tbody);

                         var tr = new Element('tr');

                         tbody.appendChild(tr);

                         var td = new Element('td');

                         td.innerText     =          d.name;

                         tr.appendChild(td);

                         td = new Element('td');

                         td.innerText     =          d.name1;

                         tr.appendChild(td);

 

                         $('vectorResilts').appendChild(table);

 

                         }.bind(this));  

 

this.myMap.events.attachEventHandler( 'ajaxVectorFeaturesErr', function(d) {

                         this.myMap.events.fireEvent('hideWaitIcon');

                         alert(Object.toJSON(d));

                         }.bind(this));  

 

 

 

Then let’s have a look we are defining these events names as along as class names. Of course its Spring again:

 

            <bean id="ajaxVectorFeatures"

                        class="org.emaps.control.AjaxDispatcherEvent">

                        <property name="eventName" value="ajaxVectorFeatures" />

                        <property name="className"

                                   value="org.emaps.ajax.AjaxVectorFeatures" />

                        <property name="methodName" value="ajaxCall" />

                        <property name="ajaxSuccEvent" value="ajaxVectorFeaturesOk" />

                        <property name="ajaxErrEvent" value="ajaxVectorFeaturesErr" />

            </bean>

 

So here you can see that event name we fired is: ajaxVectorFeatures ,  also after that when you have some data back from the java side by notifying either ajaxVectorFeaturesOk or ajaxVectorFeaturesErr event.

Also Ajax controller class is given here as well: org.emaps.ajax.AjaxVectorFeatures. It should have getters and setters with Ajax annotations in order to send and receive java script objects.

Also please note that we don’t need to serialize Ajax and deserialize it; the framework does it automatically. We send java script object and get annotation – matched java object and vice versa.

 When all the properties are set up the method ajaxCall will be called and you may return java object and at the page it appears and java script object automatically.

 

You may have as many beans as you want but you need to register them in the Ajax controller:

 

            <bean id="ajaxDispatcherControl"

                        class="org.emaps.control.AjaxDispatcherControl">

                        <property name="javaScriptClassName"

                                   value="AjaxDispatcherControl" />

                        <property name="dispatcherUrl" value="/mapAjaxDispatcher" />

                        <property name="tempateName"

                                   value="/WEB-INF/map-templates/mapajaxdispatcher.vm" />

                        <property name="events">

                                   <list>

                                               <ref bean="testEvent" />

                                               <ref bean="ajaxVectorFeatures" />

                                   </list>

                        </property>

            </bean>          

As you can see all the framework is event driven. This will allow the developer to create pages having some complicated java scripts and their back – end without worrying about that complexity. The application is messaging to the dispatcher to process the data. Also all the JSON mapping is annotation based and it will increase maintainability.

 

To get the war file go to the

http://sourceforge.net/projects/enterprisemaps/