Skip to main content

Sitecore

SXA Map component Part 3 Show distance in POI Marker

Resources

Hi Folks! Welcome back. In the previous post, we had set up the Map with all the markers supplied in the data source, and to not get affected by the Search Results on the page, we provided the Search Results Signature to Map Control Properties. And have placed the Location Finder. In this case, the distance is not shown. The central point inputs on the Map data source are used to adjust the Map view and do not contribute to evaluating the distance if the g query string parameter is not present. g hold the geolocation coordinates and are used to evaluate distance by the Search API. In this post, let’s explore how to provide the distance in such a case.

This is the third post in the series of an SXA Map component.

  1. SXA Map component
  2. SXA Map component Part 2 With Search results and Location Finder
  3. SXA Map component Part 3 Show distance in POI Marker
  4. SXA Map component Part 4 Show POI markers for the same coordinates
  5. SXA Map component Part 5 Filter locations with Radius Filter or Custom Filter Slider components

 

1 Map With All The Marker

The map shows all the markers and is not affected by the search result page size

2 Map With All The Marker With No Distance

No distance is shown

To show distance, there can be multiple solutions. One approach is to save the searched location in the cookie and read this cookie in a custom embedded function for the Scriban template that helps in evaluating the distance.

Following is the code implementing the above idea.

Create a JS file locally and copy the below code or download GeolocationCookieManager.js. Upload the file to the scripts folder of the site theme. e.g /sitecore/media library/Themes/tenant/site/themename/Scripts.

XA.component.geolocationcookiemanger = (function ($, document) {

    "use strict";
    var api = {},
        scriptsLoaded = false;
        
    api.init = function() {
        if ($("body").hasClass("on-page-editor")) {
        return;
        }

        if(!scriptsLoaded)
        {
            scriptsLoaded = true;
            XA.component.search.vent.on("hashChanged", function(hash) {
                var reloadPage = false;
                
                var components = $('*[data-properties]');
                _.each(components, function(elem) {
                var $el = $(elem),
                properties = $el.data("properties");
                    var signature = properties.searchResultsSignature;
                    if (typeof signature !== "undefined")
                    {
                        var gsign = signature.length > 0 && signature !== "" ? signature + "_g" : "g";
                        var gsignVal = hash[gsign];
                        if (typeof gsignVal !== "undefined" && gsignVal !== null && gsignVal !== "") {
                            if(XA.cookies.readCookie(gsign) !== gsignVal)
                            {
                                XA.cookies.createCookie(gsign, gsignVal);
                                reloadPage = true;
                            }
                        }		
                    }
                });
                if(reloadPage)
                {					
                    window.location.reload(); //needed to send the newly updated cookie to server so the map loads with the distance value
                }
            });		
        }	
    };	
  
    return api;

}(jQuery, document));

XA.register("geolocationcookiemanger", XA.component.geolocationcookiemanger);

Add a class file to your existing VS project suitable for a custom embedded function for the Scriban template. Copy the below code or download the source code from MapExtension Repository.

using System;
using Scriban.Runtime;
using Sitecore.Data.Items;
using Sitecore.XA.Foundation.Scriban.Pipelines.GenerateScribanContext;
using Sitecore.ContentSearch.Data;
using Sitecore.XA.Foundation.Search.Models;
using Sitecore.XA.Foundation.SitecoreExtensions.Extensions;
using Sitecore.Mvc.Presentation;
using Sitecore.Data.Fields;
using System.Linq;
using System.Web;
using Sitecore.XA.Foundation.SitecoreExtensions.Interfaces;
using Sitecore.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

namespace CustomSXA.Foundation.MapExtension
{
    public class GetGeospatial : IGenerateScribanContextProcessor
    {
        private delegate Geospatial GetGeospatialModel(Item item, string distanceUnit);

        public void Process(GenerateScribanContextPipelineArgs args)
        {
            var getGetGeospatialModelImplementation = new GetGeospatialModel(GetGeospatialModelImplementation);
            args.GlobalScriptObject.Import("sc_geospatial", getGetGeospatialModelImplementation);
        }

        public Geospatial GetGeospatialModelImplementation(Item item, string distanceUnit = "Miles")
        {
            if (item != null && item.InheritsFrom(Sitecore.XA.Foundation.Geospatial.Templates.IPoi.ID))
            {
                var centre = this.SetLocationCentre();
                if (centre != null)
                {
                    return new Geospatial(item, centre, (Unit)Enum.Parse(typeof(Unit), distanceUnit));
                }
            }
            return null;
        }

        public Coordinate SetLocationCentre()
        {
            IRendering rendering = ServiceLocator.ServiceProvider.GetService<IRendering>();
            if (rendering == null)
                return null;
            string sign = rendering.Parameters["Signature"];
            string coordinates = string.Empty;
            double lat, lon;
            if (System.Web.HttpContext.Current.Request.Cookies[$"{sign}_g"] != null)
            {   //Map component signature g value
                coordinates = System.Web.HttpContext.Current.Request.Cookies[$"{sign}_g"].Value;
            }
            else if (System.Web.HttpContext.Current.Request.Cookies["g"] != null)
            {   //regular default g value 
                coordinates = System.Web.HttpContext.Current.Request.Cookies["g"].Value;
            }
            else if (!string.IsNullOrWhiteSpace(RenderingContext.CurrentOrNull?.Rendering.DataSource))
            {   //coordinates from map data source
                Item dataSource = Sitecore.Context.Database.GetItem(RenderingContext.CurrentOrNull.Rendering.DataSource);
                ReferenceField field = dataSource.Fields["Central point mode"];
                if (field != null && field.TargetItem["Value"] == "Auto")
                {
                    string vLat = dataSource["Central point latitude"];
                    string vLon = dataSource["Central point longitude"];
                    if (!string.IsNullOrWhiteSpace(vLat) && !string.IsNullOrWhiteSpace(vLon))
                    {
                        lat = Convert.ToDouble(vLat);
                        lon = Convert.ToDouble(vLon);
                        return new Coordinate(lat, lon);
                    }
                }
            }

            if (string.IsNullOrWhiteSpace(coordinates))
            {   //signature g value from the first component found
                string gcookieKey = HttpContext.Current.Request.Cookies.AllKeys.ToList().FirstOrDefault(k => k.EndsWith("_g"));
                if (HttpContext.Current.Request.Cookies[gcookieKey] != null)
                {
                    coordinates = HttpContext.Current.Request.Cookies[gcookieKey].Value;
                }
            }

            if (!string.IsNullOrWhiteSpace(coordinates))
            {
                string[] coordinatesValues = coordinates.Split('|');
                if (coordinatesValues.Length == 2)
                {
                    lat = Convert.ToDouble(coordinatesValues[0]);
                    lon = Convert.ToDouble(coordinatesValues[1]);
                    return new Coordinate(lat, lon);
                }
            }
            return null;
        }
    }
}

Create a patch config file named CustomSXA.Foundation.MapExtension.config in App_Config\Include\Foundation folder of your project and update it with the below configuration.

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
      <pipelines>
          <generateScribanContext>
              <processor type="CustomSXA.Foundation.MapExtension.GetGeospatial, CustomSXA.Foundation.MapExtension" resolve="true" />
          </generateScribanContext>
      </pipelines>
  </sitecore>
</configuration>

Install the required Sitecore and Scriban NuGet packages. Build the project, copy the CustomSXA.Foundation.MapExtension.dll to webroot\bin and Foundation\CustomSXA.Foundation.MapExtension.config to your webroot\App_Config\Include\Foundation folder.

Let’s create a new custom variant for POI and use the custom embedded scirban function. Traverse to /sitecore/content/tenant/site/Presentation/Rendering Variants/POI/Default.

Duplicate this item and name it as Marker.

In the new variant, update the Template field of item /sitecore/content/tenant/site/Presentation/Rendering Variants/POI/Marker/Distance, with below code.

{{ if o_geospatial }}
    Distance: {{ o_geospatial.distance | math.round 1 }} {{ o_geospatial.unit }}
{{else}}
    {{o_geospatial = sc_geospatial i_item 'Kilometers'}}
    {{ if o_geospatial }}
        Distance: {{ o_geospatial.distance | math.round 1 }} {{ o_geospatial.unit }}
    {{end}}
{{end}}

Traverse to /sitecore/content/tenant/site/Presentation/POI Types/Simple POI

Change Default variant to Marker. Save it.

Preview the item where all the components are configured. Below is the output where it shows the distance in Markers.

03 Distance Shown In Map Markers

Distances are shown in the markers

Same Distance Scriban can be used for the Search Results component. 

Traverse to the Search Results variant. e.g. /sitecore/content/demotenant/sitecorethinker/Presentation/Rendering Variants/Search Results/vertical

Duplicate this item and name it as Locations.

Copy the Distance scriban item /sitecore/content/tenant/site/Presentation/Rendering Variants/POI/Marker/Distance under this new Locations Variant so it looks like below.

04 Distance In Search Results

Distance in Search Results Variant

In the case of the Search Results component, o_geospatial is available and hence is able to provide the distance. Else part can be removed. Save it. Change the variant to Locations on the Search Results component.

05 Change Variant

Set Location variant in Search Results

Preview the page, now Search Results also show the distance.

06 Distance In Search Result

Distance in both Markers and Search Results

Hope this helps.

Check out the SXA Map component Part 4 on how to show POI markers for the same coordinates.

Happy SXA Learning 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Sandeepkumar Gupta

Sandeepkumar Gupta is a Lead Technical Consultant at Perficient. He enjoys research and development work in Sitecore. He has contributed to Sitecore projects based on SXA, ASP.NET MVC, and Web Forms. He has also contributed to Sitecore upgrade projects.

More from this Author

Categories
Follow Us