Hi there,

it's been a while since my last coding post but over the past rainy weekend, I had a bit of time on my hands to build an XPages App that shows marker data coming from a Domino Database as Markers on an interactive map. I was looking for an open source solution, so after some research, I came across the beautiful JavaScript library leaflet.js (see resources [1]) and a very well done Tutorial on it (see Resources [2]). Leaflet is using OpenStreetMap data (Resources [3]), so you don't have to rely on Google or any other data provider and you can (and should) support this great project directly !

Attached, you will find a sample database that outputs something like this: https://sitlux02.sit.de/XPMapSample.nsf/Map.xsp

Image:XPages: Interactive Maps using OpentStreetMap and Leaflet.js

The Use Case:


In this sample, I want to show points of interest on a map, These are stored as Notes Documents in the database, containing a tilte, a type definition (Point of Interest or Accommodation) a description, longitude and latitude and a link to the specific url for the POI.

How it has been built:


A) The Bean:


To serve the data to leaflet, I use JSON as the data format of choice. The JSON data gets created by a request scope bean within the database. I don't use the built in rest services because I want to have full control of the JSON data that gets created. The bean exposes a method called getMarkerData() that will go into a view in the database, reads the entries and spits out JSON. To test the output, there's also an XAgent in the database (getMarkerData.xsp) that will return the marker data. Of course I have to bring the definitions into faces-config.xml.

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<managed-bean id="Markerdata">
<managed-bean-name>MarkerData</managed-bean-name>
<managed-bean-class>com.harbourlight.beans.MarkerData</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<!--AUTOGEN-START-BUILDER: Automatically generated by IBM Domino Designer. Do not modify.-->
<!--AUTOGEN-END-BUILDER: End of automatically generated section-->
</faces-config>

As I use the ExtLib, I also have to check it in the xsp.properties.The source code of the bean can be found in the Code/Java section using Domino Designer.

B) The UI:


The UI is rendered in the Xpage called "Map.xsp". It loads the leaflet.js library via...

<xp:headTag tagName="script">
    <xp:this.attributes>
            <xp:parameter name="type" value="text/javascript" />
            <xp:parameter name="src" value="leaflet.js" />
    </xp:this.attributes>
</xp:headTag>

...to be loaded before Dojo kicks in and creates a mess. Thanks for that great tipp to Sven Hasselbach (see Resources [4]). The leaflet.js is stored as a file resource in the database.
Then, I also have to load the leaflet.css and my own client side javascript library to glue everything together:


<xp:styleSheet href="/leaflet.css"></xp:styleSheet>
<xp:script src="/leafletembedd.js" clientSide="true"></xp:script>


The rendering of the map is done using one DIV -Tag in the Xpage:


<div id="map" style="width: 99%; height: 600px"></div>


To talk to our Bean, I also injected a JSON-Rest-Service-Object:


<xe:jsonRpcService id="jsonRpcService1"
            serviceName="fetchMarkerData">
            <xe:this.methods>
                    <xe:remoteMethod name="getMarkerData">
                            <xe:this.script><![CDATA[var liste = MarkerData.getMarkerData();
return liste;]]></xe:this.script>
                    </xe:remoteMethod>
            </xe:this.methods>
    </xe:jsonRpcService>


This basically exposes the getMarkerData()-Method of the bean to client side javascript.

I want to show different markers for different types of POIs  on the map. If it's a real POI it should show a red marker, if it's a motel or other accommodation type, it will show a blue marker. To do that, I can define Icon classes for leaflet, and I do so in the leafletembed.js CSJS-Library.

var
blueIcon = L.icon({
iconUrl: 'marker-icon.png',
shadowUrl: 'marker-shadow.png',

iconSize:     [19, 48], // size of the icon
shadowSize:   [25, 32], // size of the shadow
iconAnchor:   [11, 47], // point of the icon which will correspond to marker's location
shadowAnchor: [4, 32],  // the same for the shadow
popupAnchor:  [-3, -76] // point from which the popup should open relative to the iconAnchor
});

var
redIcon = L.icon({
iconUrl: 'marker-icon-red.png',
shadowUrl: 'marker-shadow.png',

iconSize:     [19, 48], // size of the icon
shadowSize:   [25, 32], // size of the shadow
iconAnchor:   [11, 47], // point of the icon which will correspond to marker's location
shadowAnchor: [4, 32],  // the same for the shadow
popupAnchor:  [-3, -76] // point from which the popup should open relative to the iconAnchor
});

As you can see, the Icons are PNG files (see image resources in the sample database). If you have more than two, you would create a base class for the icons and extend from that to keep the code more sustainable of course.

The library has a function called initMap that will get called in the onClientLoad event of Maps.xsp. InitMap also defines the starting point for the map and the zoom level:


// start the map in Shelburne, Nova Scotia
map.setView(new L.LatLng(43.7612695, -65.3226),10);


Then, I grab our marker data from Domino using the function "askForPlots". In the function askForPlots, I call the JSON Rest Services using a callback:


// request the marker info
markerList = fetchMarkerData.getMarkerData();
markerList.addCallback(function(back){


and add the markers where I distinguish between the icon color, based of a value in the JSON data coming from the service. I also attach a pop up to each of the markers showing the title and the url in this sample.

...
var
typ = plotlist[i].Type;
console.log(typ);
var
plotmark;
if
(typ==("POI")) {
    console.log("--> POI !");
    plotmark = new L.Marker(plotll,{icon:redIcon});
} else {
    console.log("--> Acommodation !")
    plotmark = new L.Marker(plotll,{icon:blueIcon});
}                                }
...

C) Summary:


As always - here's my default disclaimer:

Yes, there are a ton of ways to do this more efficiently. My code may be bloated or inefficient for some of you out there. My goal is to get the idea across - go ahead and improve the sample and send it back to me at hvoigt AT sit.de, I'm happy to learn.

Now the real summary.


It took me three hours from scratch to get this sample up and running. I'm impressed by leaflet.js that IMHO is a great tool to work with as well as by the flexibility of the XPages runtime to really let you do new stuff in a fast and easy way. Everytime I am able to do something like this, I simply don't get, why IBM does not recognize this gem in its portfolio and instead let's us create crippled widgets for a crippled app platform like Connections.

This sample is a first start on how to work with maps but it can easily be extended on all ends. The backing bean gives you for example the opportunity to have only the design of the app in a publicly accessible database and grab the data from another secured database using sessionAsSigner functionality.
Leaflet offers a ton of features, APIs and Plugins to create interactive maps for various use cases. What I personally like is that it does not bring any other dependency to the table e.g. you don't have to fire up jQuery to make use of it. The CSJS Lib gives you some hints on how to attach to events on the map - with that, you can pass values to the service bean and get the marker data for the specific region in the map only. You can extend the marker icons and the content in the pop up and the look and feel by altering the CSS.... . So, lot's of potential.

That's it for today folks, have fun !

Resources and Links:


[1]http://leafletjs.com
[2]https://switch2osm.org/using-tiles/getting-started-with-leaflet
[3]http://www.openstreetmap.org
[4]http://hasselba.ch/blog/?p=1181
Heiko Voigt   |   4 May 2015 11:37:00   |    xpages  javascript  OpenStreetMap  Leaflet    |  
  |   Next Document   |   Previous Document

Discussion for this entry is now closed.

Comments (2)

Patrick Kwinten    http://quintessens.wordpress.com    15.05.2015 8:28:45

hi, thanks for sharing! I have not looked at jsonRpcService. Where can I find good information how to use this incl some examples?

I thought Connections is a solution, not a app platform. In any words: do not touch it and alter it.

Heiko Voigt    http://www.sit.de    20.05.2015 10:59:32

Hi Patrick,regarding Connections, it's a Nail-and-Hammer problem for me right now. As ICS only has two hammers (Connections and Verse) all the problems have to be Nails. And so everything goes inside Connections.