/lizmap-tool-demo

Demo repository on how to create a lizmap tool

Primary LanguageJavaScript

lizmap-tool-demo

This repository contains a complete example showing how to create a new tool into Lizmap. The demo is based on Lizmap 3.1.11.

Running the demo

To run this project on your machine, you can clone this repository and run it with docker-compose:

  • git clone https://github.com/opengisch/lizmap-tool-demo.git
  • cd lizmap-tool-demo
  • docker-compose up

The project should be now available at http://localhost:9980/

The docker part

The docker-compose.yml file launch a Lizmap and a QGIS server containers and mount the host directory projects into both containers. In this directory we put the QGIS project and the javascript codes of our tool.

The QGIS part

The demo QGIS project (demo.qgs) into the project directory is a simple project with 2 layers containing some random features. QGIS project

Inside the projects directory there is the Lizmap config file demo.qgs.cfg generated by the Lizmap QGIS plugin

The Lizmap hacking part, step-by-step

In this part, we will create a Lizmap tool that permit to click on a location and shows the attributes of the features (on all the layers, visible and not), on that location.

Create a new minidock

Lizmap will execute the javascripts contained into the projects/media/js/demo directory as explained in the doc.

  • We add the file demoTool.js with the following content:
lizMap.events.on({
    uicreated: function(e) {

        lizMap.addDock(
            'demoTool',
            'Demo Tool',
            'minidock',
            '<div id="divDemoTool" class="lizmapPopupDiv"></div>',
            'icon-wrench'
        );
  }
});

The code is called when the event uicreated is fired by Lizmap and adds a new Lizmap Dock using the function lizmap.addDock. We can specify the name of the tool, the showed label, the type of the dock (minidock, right-dock, dock or bottomdock), the content of the doc (in this case is an empty html div that we will fill after), and the icon. The icon is a bootrstap 2 icon with the prefix icon-. A list of the available icons is here.

The result is a new icon on the tool bar that open an empty minidock. Our minidock

Disable default Lizmap info tool

When a minidock is opened or closed, Lizmap fires a minidockopened respectively minidockclosed event.

We want to disable the standard Lizmap's feature info tool when our tool is open.

  minidockopened: function (e) {
    if(e.id == 'demoTool') {

      // Deactivate lizmap's feature info tool
      lizMap.controls['featureInfo'].deactivate();
    }
  },

  minidockclosed: function (e) {
    if(e.id == 'demoTool') {

      // Re-activate lizmap's feature info tool
      lizMap.controls['featureInfo'].activate();

    }
  }

Get the info of the clicked point

Lizmap uses OpenLayers 2 to display the map. We can use the Openlayers functions to manipulate and interrogate the map.

In this case we use the WMSGetFeatureInfo function that return information of the features on a location. We can choose different return type, depending on the map server. In this case we ask to QGIS server to return xml. Then we extract the data we want from the xml and we put them into an html page. We don't ask to the server to directly return html because is often useful to be able to make calculations or some analysis on returned data.

Our html page:

<div class="">
  <table class="lizmapPopupTable">
    <thead>
      <tr>
        <td>Layer</td>
        <td>Value</td>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td>Triangles</td>
        <td id="triangle_value"></td>
      </tr>
      <tr>
        <td>Rectangles</td>
        <td id="rectangle_value"></td>
      </tr>
    </tbody>
  </table>
</div>

The code to request the information:

var fiurl = OpenLayers.Util.urlAppend(lizUrls.wms,
    OpenLayers.Util.getParameterString(lizUrls.params)
);

var tolerances = {
    'FI_POINT_TOLERANCE': 1,
    'FI_LINE_TOLERANCE': 1,
    'FI_POLYGON_TOLERANCE': 1
};

var featureInfo = new OpenLayers.Control.WMSGetFeatureInfo({
  url: fiurl,
  title: 'Identify features by clicking',
  queryVisible: false,
  maxFeatures: 1,
  infoFormat: 'text/xml',
  vendorParams: tolerances,
  type: OpenLayers.Control.TYPE_TOOL,
  eventListeners: {
    getfeatureinfo: function(event) {


      var mediaLink = OpenLayers.Util.urlAppend(lizUrls.media, OpenLayers.Util.getParameterString(lizUrls.params));
      var demoToolSrc = mediaLink +'&path=/media/js/demo/demoToolDialog.html';

      // Load demoToolDialog.html and when finished execute the rest
      $("#divDemoTool").load(demoToolSrc, function(){

        if (window.DOMParser)
        {
          parser = new DOMParser();
          xmlDoc = parser.parseFromString(event.text, "text/xml");
        }
        else // Old versions of Internet Explorer
        {
          xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
          xmlDoc.async = false;
          xmlDoc.loadXML(event.text);
        }

        var layers = xmlDoc.getElementsByTagName("Layer");

        for (var i = 0; i < layers.length; i++){
          if(layers[i].getAttribute("name")=="triangles") {
            var attributes = layers[i].getElementsByTagName("Attribute");
            for (var j = 0; j < attributes.length; j++) {
              if (attributes[j].getAttribute("name") == "value"){
                document.getElementById("triangle_value").innerHTML = attributes[j].getAttribute("value");
              }
            }
          }

          if(layers[i].getAttribute("name")=="rectangles") {
            var attributes = layers[i].getElementsByTagName("Attribute");
            for (var j = 0; j < attributes.length; j++) {
              if (attributes[j].getAttribute("name") == "value"){
                document.getElementById("rectangle_value").innerHTML = attributes[j].getAttribute("value");
              }
            }
          }
        }
      });
    }
  }
});

And we tell to Lizmap to add the control when our minidock is opened.

lizMap.events.on({
  uicreated: function(e) {
    lizMap.addDock(
      'demoTool',
      'Demo Tool',
      'minidock',
      '<div id="divDemoTool" class="lizmapPopupDiv"></div>',
      'icon-wrench'
    );
  },

  minidockopened: function (e) {
    if(e.id == 'demoTool') {

      lizMap.map.addControl(featureInfo);
      featureInfo.activate();

      // Deactivate lizmap's feature info tool
      lizMap.controls['featureInfo'].deactivate();
    }
  },

  minidockclosed: function (e) {
    if(e.id == 'demoTool') {

      // Re-activate lizmap's feature info tool
      lizMap.controls['featureInfo'].activate();

    }
  }

});

And the result is a working tool:

Working tool

Show the clicked point

The only problem now is that don't see where on the map we clicked. To fix this, we can add a special OpenLayers layer, called marker layer, to show the choose location.

var markersLayer = new OpenLayers.Layer.Markers( "Markers" );
var marker;

And into the WMSGetFeatureInfo definition, we draw the marker.

        if(marker){
            markersLayer.removeMarker(marker);
        }

        var position = lizMap.map.getLonLatFromPixel(event.xy);
        var size = new OpenLayers.Size(25,25);
        var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);

        var markerIcon = mediaLink + '&path=media/js/demo/marker.png';
        var icon = new OpenLayers.Icon(markerIcon, size, offset);
        marker = new OpenLayers.Marker(position, icon);
        markersLayer.addMarker(marker);

And we add or remove the layer when the minidock is opened respectively closed.

  minidockopened: function (e) {
    if(e.id == 'demoTool') {

      $("#divDemoTool").html('<div id="divDemoTool" class="lizmapPopupDiv">Click a point on the map...</div>')

      lizMap.map.addControl(featureInfo);
      featureInfo.activate();

      lizMap.map.addLayer(markersLayer);

      // Deactivate lizmap's feature info tool
      lizMap.controls['featureInfo'].deactivate();
    }
  },

  minidockclosed: function (e) {
    if(e.id == 'demoTool') {

      if(marker){
          markersLayer.removeMarker(marker);
      }
      lizMap.map.removeLayer(markersLayer);

      // Re-activate lizmap's feature info tool
      lizMap.controls['featureInfo'].activate();

    }
  }

});

And the final result is:

Working tool