This is the third in a four-part video series on implementing and optimizing Salesforce Maps.
- View Part 1 (Augment Salesforce Objects with Geolocation Data) here
- View Part II (Use Salesforce Maps and a Distance Matrix to Calculate Drive Times Between Locations) here
In the third part of our Salesforce Developer Deep Dive series into Salesforce Maps, Senior Salesforce Developer Mike Chandler demos two strategies for implementing distance and drive time lookups between locations. He starts by detailing the simplest implementation: a real-time, API lookup. Then, Mike moves on to demo a more complicated implementation in which data is saved to a junction object to avoid making repeat requests to the Salesforce Maps API. Plus, Mike goes under the hood to demo a new, soon-to-be-released open source tool called TSS Maps (stay tuned for more on that!).
TRANSCRIPT:
Hi, I’m Mike, a Salesforce developer with TruSummit Solutions. In this video, I’ll show you more than one way to manage driving distance and driving time lookups between locations using Salesforce maps and what you need to consider to help produce the right solution for your needs. This video is part three in a series of development focused Salesforce Maps tutorials. In our previous video in the series, we closely examined the distance matrix, how to request a matrix from the Salesforce Maps API, and how to parse that data to derive distance and drive times. In this video, we’ll talk about two strategies for implementing distance and drive time lookups between locations. We’ll start by covering the simplest implementation, a real-time, API lookup. After that, we’ll cover a more complicated implementation involving saving distance and drive time lookup data to a junction object to avoid repeat requests to the Salesforce maps API.
Let’s start with scenario number one, a real-time lookup. If you recall from my previous video, the Salesforce maps documentation for the distance matrix request indicated that performance can be a factor when implementing solutions that leverage this data. That something to keep in mind as we discussed this solution. On my screen, I have a simple component that allows me to define a single source location and a single destination location. So I’m just going to go ahead and identify a starting point here of Disneyland and I’ll select that. And as a destination location, I’m going to select not Berry Farm, and there it is. Once I’ve selected a location in each of my location fields, then I can go ahead and click the calculate distance button.
Okay, my results are displayed on the screen indicating that the process is done, and I got what I was asking for. In this instance, I got results in a relatively timely manner. I’d say it was maybe less than one second in a series of tests I ran earlier. I got results back for this query, usually in about one second, give or take a few milliseconds. And while that’s not terrible, it’s also not super great. If we were to add some additional locations to our distance matrix request, this would be a lot slower than it is in this demo. But for conducting a real-time lookup between only two locations, this actually seems acceptable to me at least in this particular use case. So let’s go ahead look at some code and see if I can share how I’m doing this and demonstrate how the implementation’s relatively straightforward.
Okay, now this is the HTML layer of the LWC. We don’t need to dig too deeply into this, really. It’s just two lightning record pickers, and then it’s a button that prompts a request to my Apex controller. So let’s look at that instead. So here we are in the JavaScript. Let me actually, let’s see. I’ll scroll all the way down and I’ll show you the handle search function. This is the function that sends our source ID and our destination ID to our Apex controller, which responds with a distance response structure consisting of my distance in miles and my time in minutes. So let’s just get right to the really interesting stuff and we’ll dig into the apex. Okay, so my first move in the Apex controller is to query for the account records for both the source and the target destination. I not only want to make sure that I can differentiate between which of these is the source and which is the destination, but I also want to make sure that I format each location the way that the distance matrix request is expecting them.
So that’s why you see down here in this create Maps API location. I’m doing exactly that. So I’m defining a location as a map parameterized with string object, and I’ve specified the location id, the latitude and the longitude. So this leaves us with two critical steps to complete the process with our locations formatted and ready. We want to use them as an argument to get the distance matrix. That’s what’s going on right here. And then once we have the distance matrix, then we want to return some structured data to our LWC, specifically the distance between our locations and miles and the drive time in minutes, and that’s what you see here. So I’ll just show you what this is right here. We got the distance matrix. We are getting our location by id, our destination by id. We get the distance. We specify that we want it in miles and on the drive time, we are indicating that once we have a handle on the destination, we want to get the traffic window.
If you recall from my previous video, there’s multiple traffic windows. We’re focused on traffic window three, and then once we have that, we want to produce the data in minutes. Now, before we get too far, let’s talk about how we’re invoking the distance matrix request, which you can see up here on this line. In this example, I’m leveraging a wrapper class that was developed internally to simplify, extracting and manipulating data from a distance matrix data structure. This class called TSS Maps is not part of a common Salesforce, API or Salesforce maps API. It’s actually unique to my development org. In this example, under the hood, this class is working with our raw distance matrix data, and it provides us with a variety of classes and helper methods to make working with the data a lot easier, a lot more expressive, and just a lot simpler to read.
Now in the days that come, I intend to release this class as an open source tool, entirely free to use for anyone in the community who’s working with Salesforce Maps. So subscribe to our channel and watch the next video in the series, which is going to do a deep dive into this class and provide you with the details necessary to get your hands on it. A deep dive into this class goes beyond the scope of this video. Okay, back to our Apex controller. In summary, once again, we’ve formatted our locations and we’ve made a call to the Salesforce Maps API in real time. The performance is acceptable for this use case, but a more complicated set of requirements could disqualify this implementation. This brings us to scenario number two, pre-calculated drive times saved to a junction object. Your org may have situations where a single location needs distances and drive times calculated for dozens or maybe even hundreds of locations.
In a case like that, various API restrictions and performance bottlenecks require you to get creative. Conducting requests of that size against the Salesforce Maps API is going to be slow and painful as a real-time event. Every time we’ll try to mitigate some of these challenges by persisting driving calculation data to a junction object. There are key factors to bear in mind here. Salesforce warns us in the documentation that performance can be a factor. We’re retrieving data calculated on the fly from a third party API, beyond the realm of the Salesforce technology stack. So really no amount of support tickets to Salesforce is going to make Salesforce maps faster than it is because it’s driven by Google Maps data. So calculating distance and drive times for large volumes of records, asynchronously is your best bet. From a performance standpoint, each request that we make for a distance matrix is limited to no more than 20 locations per request.
That means if we need to calculate distances between a location that has 30 nearby locations of interest, we need to handle that in multiple requests to the API. If you plan to calculate drive distances and times by way of an update or insert trigger, then you need to consider housekeeping tasks such as clearing junction records with outdated distances and times before inserting new ones. And then lastly, while we’re not implementing callouts in Apex per se, the managed Apex classes that we delegate these request actions to make use of callouts under the hood. To that end, we need to make sure that our implementation avoids inadvertent uncommitted work exceptions, and that our asynchronous classes allow for callouts. Okay, let’s go back to the org and talk about this solution. I’m an object manager and we’re looking at a custom object called Distance Junction. The field’s account and destination represent a starting point location and a single destination account effectively creating the relationship between the two account records as being nearby one another.
I’ll scroll all the way down, and I’ve got a field down here called Travel Distance, which accepts the value in meters between the account and the destination. Scrolling up a little bit, I’ve got a series of fields prefixed with the word seconds, and these fields represent the number of travel time seconds between an account and a destination separated by their traffic windows. So you recall from our previous videos that there are eight traffic windows, and this is tracking the number of seconds for each of those eight windows. Then I’ve got a series of time fields that I intend to use to represent the traffic window. Start time details, though I’m not going to be using these fields in my example, my objective here is to create a junction object for every account and destination where a distance and drive time calculation is required whenever an account is created with geolocation data or updated with geolocation data, but there could be thousands of records in the system and that could represent a very large transaction after a record is updated.
So to that end, let’s just clarify our details for this solution. In this made up scenario, let’s say that our client has a use case to provide distance and drive time calculations for locations that are 20 miles away or less from one another. That small detail is crucial and it helps define the scope of data that we’ll be working with. Let’s look at some code to make sense of that. This is an apex trigger on the account record. It should be noted that we’re not implementing a trigger framework for this demo or focusing too closely on trigger best practices. Here we’re focusing instead on interacting with the Salesforce Maps API. But hopefully this gives you some idea of how to proceed for after insert or after update events on an account. We’re evaluating the record to see if longitude or latitude data has been changed.
If it has, that means we’ve got some work to do. In this example, we’re omitting housekeeping tasks and we’re getting right to the heart of the issue. But for a complete implementation, you’ll definitely want to make sure that you don’t need to eliminate any outdated calculations based on any old geolocation data that’s being updated or replaced here. If you look here at the implementation, we’re going to instantiate an instance of a batch Apex class. We’re specifying a radius of 20 miles and a batch size of 19. Let’s hop into that batch apex class and just explain these decisions. First, let’s talk about the 20 mile radius. Our client only requires distance and time calculations for records with 20 miles or within 20 miles of each other. So our query locator is going to leverage the distance function to ensure that we’re only looking at records that are 20 miles or closer.
Anything beyond that isn’t needed because it goes beyond the scope of our customer’s requirements. So that’s going to limit the reach of your implementation to only the data that your customer cares about. Speeding the process up quite a bit by avoiding calculations where they don’t matter or they won’t be looked at. And the 19 count on the batch size, if you recall, we’re limited to only 20 locations in a single distance matrix lookup. Our source account record counts as one location. So tacking on an additional 19 records per batch brings us to our 20 location count limit. That’s important to remember. You’re not going to know that you’ve exceeded that 20 count limit until you attempt to exceed it at runtime. So you have to keep that in mind and make sure that you put those guardrails in place in your implementation. Let’s dive a bit deeper into this code.
First note, the interfaces that we’re implementing up here at the very, very top, obviously Batchable is here, but allow callouts is necessary as well. Moving down a bit, we construct our batch Apex class with key data and our query locator retrieves accounts within 20 miles of our subject or our principal account. We format our principal account in the location format that the distance matrix requires. We also format our 19 nearby accounts the same way with all of our locations formatted. We can make our call to the distance matrix. This process concludes by resolving these calculations. So let’s just jump into this function. We’re using our handy distance matrix wrapper class to retrieve all of the destinations for this location. We iterate over each one and we create that junction object. The last thing we do before terminating the transaction is we save the new junction object records to the database.
That’s what we got going on down here on line 74. And if there’s no more batches to the process, or rather if there are more batches to the process, then we cycle and start over on the next batch. And with that, if your Salesforce Maps base object is updated with geolocation data or the geolocation data changes, you’ll get all of the relevant nearby account driving distances and driving times calculated and persisted in the database ready to act upon to report on or whatever your use case calls for. So let’s sum up real-Time Distance matrix requests can be slow, which is a major consideration in your final design. If you’re working with a single source and a single destination, you may be okay, but if you know you need to calculate distances and drive times for many locations and a single request, it’s going to be slow and relying on a real-time callout may be an aggravating user experience.
On the other hand, if you know you need to calculate distances and times from many locations, then leveraging apex triggers and batch processing may be the way to go. The distance matrix request is a callout, so you need to be cautious about ensuring your batch class allows for callouts and that you avoid uncommitted work exceptions when persisting data, the 20 location count limit is real, and it’s largely on you to make sure that you handle it. There’s no compiler level insight that’s going to stop you from formatting a request with more than 20 locations. It’s not going to be enforced until you actually go to run it at runtime or test it at runtime. So be mindful of that going into your development so that you can just be sure it’s handled while you’re implementing. And then lastly, the TSS Maps class that we use in both of our example implementations is super helpful for managing the return data structure and can greatly simplify your implementation and your time to completion. So be sure to subscribe to our channel so you can get the details on when that becomes widely available and how you can use it at no cost. We intend to release that class as an open source free to use tool before the end of this video series. That’s going to conclude this video. Thank you so much for watching, and I will see you in the next one.
Need help leveraging the latest Salesforce features and functionalities? Our developers are here for you! You know where to find TruSummit Solutions.
Featured Articles
How to Dynamically Set Einstein Bot Language in Messaging for In-App and Web
When deploying Einstein Bots in Salesforce’s Messaging for In-App and Web, providing a localized and personalized experience for your users…
TruSummit Solutions Achieves Salesforce Crest Consulting Partner Status
At TruSummit Solutions, we’re constantly striving to deliver exceptional value to our clients and push the boundaries of what’s possible…
Boosting Agent Productivity in Salesforce: Keeping Conversations in View with Pinned Regions
Salesforce’s new Messaging for In-App and Web feature offers a seamless way to engage customers across multiple channels, but one…