Embedding Google Maps for 1 or more instances

It’s possible to embed a Google Maps viewer in Relatics where project information of a single element is entered. We can make the visualization even more attractive by showing a collection of information on a map. As a functional designer you have the opportunity to embed a Maps-viewer for both the Overview Presentation as well as the Detail Presentation with only one base-file. The map even scales to fit the provided locations! In this article I describe how to set it up.

Preparations

In this article I expect you have already aquired an API key for the Maps Embed API. Here you can find you the get an API key.

Include data type GPS of a Property in a Report

In Relatics you can set the data type of a property on GPS, as described in the Knowledge Base here. I do notice that GPS is not often used, while this is very valuable! Because the GPS can be set effortlessly with a mobile device, an end user can provide the workspace by GPS coordinates rapidly (whether or not through a Context Portal). This can be very useful for risks, tests, inspections, locations of meetings or observations 'in the field'.

In this article I describe the settings of an Object with a property Location, on which the data type GPS is applied. Based on this property, we're going to embed Google Maps on the Detail presentation of a given Object, in which the Object is indicated with a marker. You can also view this marker in Google Street View! The table shows you how to set the data type. You can then include this in a report query. In the image below you can see how the datatype GPS can be set.

02_datatype_gps

Configuring the Report for the viewer

The report that I use is simple. There are some special details summarized below:

  • OutputExtension: HTML
  • ReportFields:
    • Height (this is used to adjust the height of the viewer)
    • MinZoom (this is used to adjust the minimal zoom of the viewer)
    • APIkey [Default value: YOUR_API_KEY]
  • Query:
    • The root-node makes use of a constraint query that makes sure that only elements with a non-empty location will be shown.
    • You can also use the @ID-parameter in the constraint as I did in the this article for a map on the Detail Presentation

I use the Value-attribute of the property.

03_rapportage_query

Setting up the XSLT-file for the viewer (the components)

The XSLT-file that is ‘behind’ this viewer is an expansion of the one in the previous article, but will be discussed in full here. De viewer utilizes the fact that Relatics is a web-based application. This means that you can make an HTML-page based on the data that is stored in Relatics, which can then be embedded in Relatics. By inserting the Google Maps JavaScript API we can generate a Google Maps viewer.

For this reason we need to add the following code in the XSLT:

XSLT: Load Google Maps JavaScript API

...

  <head>

    <script>

      <xsl:attribute name="src" select="concat('https://maps.googleapis.com/maps/api/js?key=',Report/@APIkey)"/>

    </script>

  </head>

...

Next to HTML we will also generate JavaScript-code with the XSLT. We need this to provoke Google’s API, so this will generate the Google Maps viewer (client-sided). In this we will join our data from our Report.

The code below is a combination of JavaScript and XSLT. The final purpose is to create JavaScript-code (JS) in which our data is included; this will be performed using some XSLT-code. In the code below the JS-code is black and the XSLT-code red. Basically, the entire XSLT can be edited, but the bold marked part depends on your Report. Maybe you can already recognize a pattern from the report query (for example the Location or the report field ‘MinZoom’):

XSLT: Generating the JavaScript code for the Google Maps API

...

  function initialize() {

    <!--Get all locations into an array-->

    var locations = [

    <xsl:for-each select="Report/Data/System_Object/Location[@Location!='']">

      [

      <xsl:value-of select="@Location"/>

      ]

      <xsl:if test="position()!=last()">

      ,

      </xsl:if>

      </xsl:for-each>

    ];

 

    <!--Get the boundaries of the map based on the locations-->

    var bounds = new google.maps.LatLngBounds();

    for (i = 0; i &lt; locations.length; i++) {

      bounds.extend( new google.maps.LatLng(locations[i][0],locations[i][1]));

    }

 

    <!--Add Map-visualisation options-->

    var mapOptions = {

      center: bounds.getCenter()

    };

 

    <!--Load the map-->

    var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

 

    <!--Fit the map to the boundaries and set a minimal zoom-->

    map.fitBounds(bounds);

 

    var listener = google.maps.event.addListenerOnce(map, "idle", function() {

      if (map.getZoom() &gt; <xsl:value-of select="Report/@MinZoom"/>) {

        map.setZoom( <xsl:value-of select="Report/@MinZoom"/>);

      }

    });

 

    <!--Add the markers to the map-->

    var markers =[];

    for (i = 0; i &lt; locations.length; i++) {

      markers[i] = new google.maps.Marker({

        position: new google.maps.LatLng(locations[i][0],locations[i][1]),

        map: map

      });

    }

  }

 

  <!--Start loading Google Maps when page is ready-->

  $(document).ready(function(){

    initialize();

  });

...

In addition to the previous code, which takes care of loading the viewer, we will also need to add an HTML-element that functions as a ‘photo frame‘. Do you recognize the ‘map-canvas’ from the previous code in the code below?

XSLT: Adding the div-element for the actual viewer

...

              <div id="map-canvas">

                <xsl:attribute name="style" select="concat('height:',Report/@Height,';width:100%;border: 1px solid black;')"/>

              </div>

...

Setting up the XSLT-file for the viewer (the complete code)

When we combine the earlier described chunks of code (and add a small bit of general XSLT-code), we will come to the following result:

Final XSLT: Embedding Google Maps on a Detail Presentation of an element

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">

  <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/">

      <html>

        <head>

          <script>

            <xsl:attribute name="src" select="concat('https://maps.googleapis.com/maps/api/js?key=',Report/@APIkey)"/>

          </script>

        </head>

        <body>

          <div class="map">

            <xsl:if test="exists(Report/Data/System_Object/Location[@Location!='']) ">

              <script>

                function initialize() {

                  <!--Get all locations into an array-->

                  var locations = [

                  <xsl:for-each select="Report/Data/System_Object/Location[@Location!='']">

                    [

                    <xsl:value-of select="@Location"/>

                    ]

                    <xsl:if test="position()!=last()">

                    ,

                    </xsl:if>

                    </xsl:for-each>

                  ];

 

                  <!--Get the boundaries of the map based on the locations-->

                  var bounds = new google.maps.LatLngBounds();

                  for (i = 0; i &lt; locations.length; i++) {

                    bounds.extend( new google.maps.LatLng(locations[i][0],locations[i][1]));

                  }

 

                  <!--Add Map-visualisation options-->

                  var mapOptions = {

                    center: bounds.getCenter()

                  };

 

                  <!--Load the map-->

                  var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

 

                  <!--Fit the map to the boundaries and set a minimal zoom-->

                  map.fitBounds(bounds);

 

                  var listener = google.maps.event.addListenerOnce(map, "idle", function() {

                    if (map.getZoom() &gt; <xsl:value-of select="Report/@MinZoom"/>) {

                      map.setZoom( <xsl:value-of select="Report/@MinZoom"/>);

                    }

                  });

 

                  <!--Add the markers to the map-->

                  var markers =[];

                  for (i = 0; i &lt; locations.length; i++) {

                    markers[i] = new google.maps.Marker({

                      position: new google.maps.LatLng(locations[i][0],locations[i][1]),

                      map: map

                    });

                  }

                }

 

                <!--Start loading Google Maps when page is ready-->

                $(document).ready(function(){

                  initialize();

                });

              </script>

              <div id="map-canvas">

                <xsl:attribute name="style" select="concat('height:',Report/@Height,';width:100%;border: 1px solid black;')"/>

              </div>

            </xsl:if>

          </div>

        </body>

      </html>

   </xsl:template>

</xsl:stylesheet>

Configuring the report on an Overview Presentation

Once you have uploaded the XSLT you can embed the report in the Overview Presentation. This can be achieved through an ‘HTMLFragment’. Example:

04_ontsluiten_van_rapportage

The final result

When we check the Overview Presentation we will see our interactive Google Maps viewer.

05_eindresultaat

Application of the previous article

Have you made a small map on the Detail presentation by using the previous article? Then you can use this XSLT for that one too. De query-structure of the report has remained the same, with only two changes to the report field necessary:

  • Add ‘px’ after your value in the Height field.
  • Change Zoom to MinZoom

Downloads

Upload the RCS-file in your Relatics environment to view the examples in this article yourself. In the ZIP you will also find the XSLT-file, so you can start right away in your own workspace.

About Tom Rupke

After finishing his study in Civil Engineering, with a masters in Hydraulic Structures, Tom has been working as Specialist Risk Analysis & Contract Management at an engineering company. Since 2016, he works at Relatics as a Business Information Consultant. Tom enjoys working on new ideas and innovations to make the most out of Relatics. He also likes transferring know-how and specific knowledge to end-users and functional designers (both on projects and with customers as by teaching different training courses and workshops).

Relatics Portret-127 (Small)

Contact

Do you need help in applying the techniques and tips in your own case? Or do you have questions, comments or suggestions about this article? If so, don’t hesitate to contact one of our consultants