Displaying a Customizable Map on a Power Apps portals Web Page

A popular out-of-the-box feature of Power Apps portals that came from the original Adxstudio product is the ability to display a map on a web page based off of data from an entity list.

In order to render the map, you need to configure an entity list to display the map and point data as well as host the entity list on a webpage based on a rewrite page template and not based on a web template.

Entity List Map View

I wrote steps on previous blog post to render a map (or calendar) on a Power Apps portals page using that legacy feature;

Show Calendar and Maps on Dynamics 365 Online Portals

Issues

Using the out of the box feature presents a couple of issues. Since we no longer have the ability to modify the underlying aspx file, we are stuck with the out-of-the-box features which are limited to driving directions and changing zoom options.

I have been asked on how to implement other use cases such as drilling down to get more details as well as other functionality and display choices.

Mapping APIs

There are a number of web based mapping APIs available that can be called from JavaScript to render a map in a web page. For my examples, I will be using the Bing Maps API, however you could apply similar steps to Google, ESRI or other mapping libraries.

Showing a Map and Plotting Points on a Power Apps portals Web Page

In the following example, I am going to replicate the base functionality of the existing feature. However, since it is all code driven, you should be able to expand and extend it for your own requirements.

Latitude and Longitude on the Account record

Like the original feature, we will place a point on a map based on a record in the Common Data Service (in our example, location of Account records). For this we will need to populate the data with the corresponding latitude and longitude values. The Common Data Model Account and Contact entities already have latitude and longitude fields. Feel free to update them manually, however I wrote a companion post that explains how to write a Power Automate flow to update the latitude and longitude values based on the address fields. Check out the post here;

Get Geo Co-ordinates from Address values in Common Data Service Records

Now we have the co-ordinate data in our records.

If you followed the steps in my post about update the co-ordinates using Power Automate then you will also have created a Bing Maps Developer account. If you haven’t done so, please refer to that post on how to set up the Bing Maps account. While you will have a certain amount of usage for testing and development, you may need to get a paid subscription depending on your usage.

Getting the Data to plot on a map

In order to retrieve the data, we will use a technique provided by Colin Vermander on using a Power Apps portal page as Rest API endpoint. We will set up a page that will retrieve the relevant data from CDS (in this case, Account records) and render it as JSON. We will call the API page from JavaScript when we need the data.

The first step is to create a new web template.

From the Portals Management App, navigate to Web Templates and add a new template record. Name the template something like getAccounts (or other name that fits).

getAccounts Web Template

Here is the code snippet to copy:

{% fetchxml accounts %}
<fetch>
  <entity name="account">
    <attribute name="name" />
    <attribute name="address1_longitude" />
    <attribute name="address1_latitude" />
  </entity>
</fetch>
{% endfetchxml %}
[
{% for account in accounts.results.entities -%}
  {
    "name": "{{ account.name }}",
    "long": {{ account.address1_longitude }},
    "lat": {{ account.address1_latitude }}
  }{% unless forloop.last %},{% endunless %}
{% endfor -%}
]

The following is a code breakdown;

The first section of code will use the Liquid FetchXML tag to retrieve the account records and the name, latitude and longitude attributes. We could have setup some additional filtering here.

The second section of code will iterate through the data stream and render the records in a JSON format (key-value pairs).

The next step is to create a corresponding Page Template. Be sure to uncheck the “Use Website Header and Footer”

getAccounts Page Template

Next create a web page record. We will do this in the Portal Management app but we could have added it through the portal studio or the legacy front side editing tools.

getAccounts Web page

In order for a portal visitor to be able to view the data on a page, we will need to setup Entity Permissions or else the API will not return any data.

Create an entity permission for the Account entity (or whichever entity you are using). Set the scope to Global and the Privileges to Read. However, you may want to adjust the configuration to fit your requirements (e.g. logged in user only see map points related to data they are allow to see).

Account Entity Permission

We will also need to link appropriate Web Roles to the Entity Permission record. For our example we will allow all portal visitors to access the data so we will add both the Anonymous Users and Authenticated Users web roles.

Adding Web Roles to Entity Permission

Navigate to https://<<your portal>>.powerappsportals.com/getAccounts/ and you should see the records rendered as a long JSON string;

JSON Data

We will use this data in our Map page.

Adding a Map Page

Now that we have a datasource, the next step is to create a Web Template that we can configure to display the map and whatever else we want to show. This is where we can customize the layout to our liking and add other features not possible using the out-of-the-box maps using Entity Lists.

Create a new Web Template (I called my Account Map)

Account Map Web Template

Paste in the following code;

{% extends 'Full Page without Child Links' %}
{% block main %}
  <div class="row sectionBlockLayout" style="display: flex; flex-wrap: wrap; text-align: left; min-height: 100px; padding: 8px; margin: 0px;">
    <div class="container" style="display: flex; flex-wrap: wrap;">
      <div id="myMap" style="position: relative; width: 600px; height: 400px;"></div>
    </div>
  </div>  
  <script>
        function GetMap(rData) {

          var map = new Microsoft.Maps.Map('#myMap', {
            credentials: '<<YOUR BING API KEY>>',
            center: new Microsoft.Maps.Location(rData[1].lat, rData[1].long),
            mapTypeId: Microsoft.Maps.MapTypeId.canvasLight,
            zoom: 4
          });

          var i;
          for(i = 0; i < rData.length; i++)
          {
            var location = new Microsoft.Maps.Location(rData[i].lat, rData[i].long);
            var pin = new Microsoft.Maps.Pushpin(location, {title: rData[i].name, subTitle: "", text: i});
            map.entities.push(pin);
          }

        }
        $(document).ready(function() {
          $.get('/getAccounts', GetMap, 'json');
        });
  </script>
  https://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=YOUR%20BING%20API%20KEY

{% endblock %}

Let’s break down the code;

The first line is just pulling in the standard “Page with Title” template so we can utilize that structure.

{% extends 'Full Page without Child Links' %}

The next DIV section aligns the page similar to other portal pages by adding a single column DIV. We also added a DIV section with the Id = “myMap” in order for the mapping code to know where to render the map. You can play around with the configuration to fit the pay layout you need.

  <div class="row sectionBlockLayout" style="display: flex; flex-wrap: wrap; text-align: left; min-height: 100px; padding: 8px; margin: 0px;">
    <div class="container" style="display: flex; flex-wrap: wrap;">
      <div id="myMap" style="position: relative; width: 600px; height: 400px;"></div>
    </div>
  </div> 

The next section is the JavaScript where the mapping magic happens.

At the bottom of the code we have the line that indicates the script source for the Bing Maps API, you will need to add your Bing Map developer API key here in the code;

https://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=YOUR%20BING%20API%20KEY

Just above that line we have the line that will run just after the page loads. It will call our Rest API page (getAccounts) and then call the GetMap function as a callback with the understanding that the data is coming in a JSON format.

        $(document).ready(function() {
          $.get('/getAccounts', GetMap, 'json');
        });

Now let’s examine the GetMap function.

function GetMap(rData) {

          var map = new Microsoft.Maps.Map('#myMap', {
            credentials: '<<YOUR BING API KEY>>',
            center: new Microsoft.Maps.Location(rData[1].lat, rData[1].long),
            mapTypeId: Microsoft.Maps.MapTypeId.canvasLight,
            zoom: 4
          });

          var i;
          for(i = 0; i < rData.length; i++)
          {
            var location = new Microsoft.Maps.Location(rData[i].lat, rData[i].long);
            var pin = new Microsoft.Maps.Pushpin(location, {title: rData[i].name, subTitle: "", text: i});
            map.entities.push(pin);
          }

        }

We are passing the JSON data in the rData object.

We also instantiate a new bing maps object and indicate that we want to render it in the myMap DIV section. We are setting the center of our map to one of the data points (note it was a random choice). We are showing a simple map view (canvaslight) but there are other options (like aerial view, etc) and finally setting the zoom to a factor of 4. I couldn’t find a lot of info on the zoom parameter, but 1 seems to be the world and 4 is roughly the size of the United States.

Check out Microsoft Docs for more info on the map control.

The for loop will interate through the list of data in the JSON object. It will create a location variable from the Microsoft.Maps.Location method and then create a pushpin object built on the location and name and finally places the pin on the map.

In order to access our new Web Template, we will need to create a corresponding Page Template record;

Account Map Page Template

NOTE: Web Templates vs Page Templates. If you new to portals, this will appear as a strange and redundant concept. This is due to maintaining legacy portals. Short version is that Web Templates will hold the code, the Page Template will point to code either in a Web Template or to aspx page on the server. A web page will look to a page template for the structure.

Creating the Web Page to Show the Map

The next step will be to create a new web page using the Portals Studio.

Create a new web page and set the title and partial URL. Use the Account Map template we created above.

Create web page in the portals studio

Browse to your portal. If everything works properly then you should see a map rendered on the page with all the plot points added.

Success!

Security

Obviously you don’t want someone to look at the page source and use your Bing Maps API key. What you can do is in the Bing Maps Developer portal is to restrict access to certain URLs or IP ranges. In this example I limited access to just the page that is showing the map;

portal security

Next Steps

You can continue to configure the web template with Liquid, CSS or JavaScript code to get the functionality you are looking for. For example, you might want to drill down on meeting locations. Potentially tag certain points on the map and use the new portals webAPI create trackable points directly in the Common Data Service. The possibilities are endless.

Summary

Power Apps portals continues to evolve and is becoming a powerful option to build externally facing web applications. There are many different web services and libraries that can be incorporated into projects. Adding mapping features to your portals can provide incredible value to your external audiences.

Cover Photo by T.H. Chia on Unsplash

Nick Doelman is a Microsoft Business Applications MVP, a Microsoft Certified Trainer and loves maps and GPS applications, and never gets lost… usually. Check out Nick’s course on Building Power Apps Portals on https://365.Training and use the code 365Nick for a 30% discount (until December 31, 2020). Follow Nick on Twitter @readyxrm

8 thoughts on “Displaying a Customizable Map on a Power Apps portals Web Page

  1. Thanks for sharing the code i used the same code for different entity while loading page i am also not getting error just checked in developer console, but map is no displayed. Could you please help me if i am missing any configuration

    Like

  2. hi The above code doesn’t work due to asynchronous call to Bing Map API. The error shows Microsft.Maps.Map undefined object. To overcome this error please modify the code of Account Map template. Let the script load fully. Replace the following line
    https://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=YOUR%20BING%20API%20KEY
    with
    https://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=YOUR%20BING%20API%20KEY&gt; async defer and it will work fine.

    Like

Leave a comment