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.
The map shows all the markers and is not affected by the search result page size
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);
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 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;
}
}
}
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.
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.
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.
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.
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.
Set Location variant in Search Results
Preview the page, now Search Results also show the distance.
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.