Find geo-coordinates from address using Batch Apex and Open Street Map

Telegram logo Join our Telegram Channel

Hello Trailblazers! In this post, I am going to share how you can populate Longitude and Lattitude on Salesforce records using the Batch Apex and Open Street Map APIs.

geocoding-in-salesforce-with-batch-apex-openstreetmap


What is OpenStreetMap?

Open Street Map is a free and open-source map of the world. It provides information about the rails, roads, cafes, restaurants, towns, and cities from all over the world. We can use it for both personal and commercial use as long as we credit the creators.

We will use the Nominatim to get the geo-coordinates from the batch apex code. You can try the UI version of Nominatim here before diving into API.


Endpoint URL

https://nominatim.openstreetmap.org/search?<params>

Request Parameters

You can pass the individual URL parameters like:

  • street=<housenumber> <streetname>
  • city=<city>
  • county=<county>
  • state=<state>
  • country=<country>
  • postalcode=<postalcode>

Or concatenated address like

  • q=<query>

Return data format

This API can provide the response in multiple forms, for that you need to specify the format as the URL parameter like format=[xml|json|jsonv2|geojson|geocodejson].

I have used the json format here. Let us see the code.


Sample Code

public class AcountGeocodingJob implements Database.Batchable<sObject>, Database.AllowsCallouts {
    public Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT BillingStreet,BillingCity,BillingState,BillingCountry,BillingPostalCode FROM Account'
        );
    }
    
    public void execute(Database.BatchableContext bc, List<Account> records) {
        List<Account> updateAccounts = new List<Account>();
        for (Account account : records) {
            HttpRequest req = new HttpRequest();
            String urlParameters = '';
            /* Avoid using street as the api does not have info of all streets
             * (String.isNotBlank(account.BillingStreet)){
                urlParameters += '&street='+EncodingUtil.urlEncode(account.BillingStreet, 'UTF-8');
            }*/
            if(String.isNotBlank(account.BillingCity)){
                urlParameters += '&city='+EncodingUtil.urlEncode(account.BillingCity, 'UTF-8');
            }
            if(String.isNotBlank(account.BillingCountry)){
                urlParameters += '&county='+EncodingUtil.urlEncode(account.BillingCountry, 'UTF-8');
            }
            if(String.isNotBlank(account.BillingState)){
                urlParameters += '&state='+EncodingUtil.urlEncode(account.BillingState, 'UTF-8');
            }
            if(String.isNotBlank(account.BillingPostalCode)){
                urlParameters += '&postalcode='+EncodingUtil.urlEncode(account.BillingPostalCode, 'UTF-8');
            }
            
            String url = 'https://nominatim.openstreetmap.org/search?format=json&limit=1' +
                urlParameters;
            
            req.setEndpoint(
                url
            );
            
            req.setMethod('GET');
            
            Http http = new Http();
            HTTPResponse res = http.send(req);
            System.debug(res.getBody());
            
            List<GeoData> geoData;
            try {
                geoData = (List<GeoData>) JSON.deserialize(
                    res.getBody(),
                    List<GeoData>.class
                );  
            } catch (Exception ex) {
                System.debug('Exception : ' + ex);
            }
            if (geoData != null && geoData.size() == 1) {
                updateAccounts.add(
                    new Account(
                        Id = account.Id,
                        BillingLatitude = Decimal.valueOf(geoData[0].lat),
                        BillingLongitude = Decimal.valueOf(geoData[0].lon)
                    )
                );
            }
        }
        
        update updateAccounts;
    }
    
    public void finish(Database.BatchableContext bc) {
        // execute any post-processing operations
    }
    
    public class GeoData {
        public String lat{get;set;}
        public String lon{get;set;}
    }
}


Notes

The Open street map is different from the Google Geocoding, some of the addresses might not be available in that. For example, street and lane details are not as accurate as Google Maps. Providing an invalid value in any parameter leads to no result.


No comments :
Post a Comment

Hi there, comments on this site are moderated, you might need to wait until your comment is published. Spam and promotions will be deleted. Sorry for the inconvenience but we have moderated the comments for the safety of this website users. If you have any concern, or if you are not able to comment for some reason, email us at rahul@forcetrails.com