Get the Geographical Coordinates of Addresses in Batch
Signature
1Map<String, Object> maps.API.BatchGeocode(Map<String, Object>)- maps is the namespace that's available after you install Salesforce Maps.
- API is the class that contains the global methods exposed to developers.
- BatchGeocode() is the method.
API Call Allocations
- For geocoding, submit up to 120 requests per minute.
- Send batches of up to 50 addresses per request.
Sample Code
This code returns the geographical coordinates of the White House and Washington Monument. The output also returns the formatted address and adds missing information, such as postal code and country, if available.
Input Format of an Address
1String address = '{HouseNumber} {Street}, {City}, {State} {PostalCode} {Country}';
2String address = '{HouseNumber} {Street}, {City}, {State}';Example
1public with sharing class exampleBatchGeocode {
2 public exampleBatchGeocode() {}
3
4 public exampleBatchGeocodeWithSFMaps(){
5 List<Account> accountList = [SELECT Id, Name, BillingAddress FROM Account WHERE Name like 'W%' AND BillingLongitude = Null];
6 Map<String, Account> accounts = new Map<String, Account>(); // Map of the Accounts to link the results back to
7 Map<String, Map<String, String>> addresses = new Map<String, Map<String, String>>();
8 for (Account acc : accounts) {
9 // It is recommended to prevent sending null fields. Not done here for illustrative purposes.
10 Map<String, String> address = new Map<String, String>{
11 'address' => acc.BillingAddress.getStreet() + ', ' + acc.BillingAddress.getCity() + ', ' + acc.BillingAddress.getPostalCode() + ', ' + acc.BillingAddress.getCountry()
12 };
13 String idstr = Id.valueOf(acc.id);
14 addresses.put(idstr, address);
15 accounts.put(idstr.toLowerCase(), acc);// Ids will be lowercase in results.
16 // Illustrative Sample: Three accounts in list.
17 // [{
18 // id: '005B0000005LDrUIA1',
19 // name: 'White House',
20 // address: '1600 Pennsylvania Ave NW, Washington, DC 20500, United States'
21 // }, {
22 // id: '005B0000005LDrUIA2',
23 // name: 'Washington Monument'
24 // address: '2 15th St NW, Washington, null, United States'
25 // }, {
26 // id: '005B0000005LDrUIA3',
27 // name: 'Wash and Ride',
28 // address: 'null, null, null, United States'
29 // }]
30 }
31
32 Map<String, Object> response; // Deserialized Untyped Response
33 try {
34 response = maps.API.BatchGeocode(new Map<String, Object> {
35 'version' => '2', // Required. Hardcode value to 2. Has no impact on results or process.
36 'address_info' => JSON.serialize(addresses)
37 });
38 }
39 catch (Exception ex) {
40 // Handle exception. Simple log for illustrative purposes.
41 System.debug(ex);
42 return;
43 }
44 List<Account> accountsWithBadData = new List<Account>();
45 Boolean callSuccess = (Boolean)response.get('success');
46 if(callSuccess != null){
47 Map<String, Object> results = (Map<String, Object>)response.get('results');
48 for(String accountId: results.keySet()){
49 Account acc = accounts.get(accountId);
50 Map<String, Object> record = (Map<String, Object>)results.get(accountId);
51 if((Boolean) record.get('success')){
52 Map<String, Object> data = (Map<String, Object>)record.get('data');
53 Map<String, Object> position = (Map<String, Object>) data.get('position');
54 acc.BillingLatitude = (Decimal) position.get('lat');
55 acc.BillingLongitude = (Decimal) position.get('lng');
56 if((Integer) data.get('score') < 50) {
57 // Low confidence in address match. Marking the accounts needed for review.
58 accountsWithBadData.add(acc);
59 }
60 }
61 else {
62 // Unable to match address provided.
63 accountsWithBadData.add(acc);
64 }
65 }
66 update accountList; // Update records with Latitude and Longitude.
67 reviewBadAccounts(accountsWithBadData); // Pushing accounts with bad data to another process to be handled.
68 }
69 else {
70 // Three primary types of failed requests.
71 String errorCode = (String) response.get('error_code');
72 if(errorCode == 'GC-0500'){
73 // Internal Server Error
74 // Retry the request at a later time and/or reach out to support.
75 return;
76 }
77 else if(errorCode == 'GC-0429'){
78 // Too many requests. Perform an exponential back off for the retries.
79 // Bad requests count against limits. Be sure to not waste calls on bad addresses and improperly created requests.
80 return;
81 }
82 else if(errorCode.startsWith('GC-1')){
83 System.debug(response);
84 // Improperly created requests. Fix errors before trying again.
85 return;
86 }
87 else {
88 // Fail safe catch for future error codes.
89 System.debug(response);
90 return;
91 }
92 }
93 }
94
95}Sample Response
Although the return value is an Apex Map<String, Object> object, this JSON response illustrates the essential data you receive in the resulting map.
If you invoke this method within a flow, process builder, or trigger and want to use the data from the JSON response, implement logic to retrieve that data. For example, you want to save the latitude and longitude coordinates from the JSON response to your records.
- False, which indicates no match.
- Partial, which indicates scores less than 100.
- Full, which indicates scores of 100.
1{
2 "baseUrl": "https://sfmapsgateway-uengage1.sfdc-lywfpd.svc.sfdcfc.net/core/batchgeocode/3",
3 "results": {
4 "001ru00000ca1teiad": {
5 "message": "Invalid address",
6 "success": false
7 },
8 "001ru00000ca1tdiad": {
9 "success": true,
10 "source": "http",
11 "data": {
12 "city": "Washington",
13 "country": "USA",
14 "fullAddress": "2 15th St NW, Washington, DC 20004, United States",
15 "houseNumber": "2",
16 "matchLevel": "Address",
17 "position": {
18 "lat": 38.889043,
19 "lng": -77.0330958
20 },
21 "postal": "20004",
22 "score": 86,
23 "state": "DC",
24 "street": "15th St NW",
25 }
26 },
27 "001ru00000ca1tciad": {
28 "data": {
29 "city": "Washington",
30 "country": "USA",
31 "fullAddress": "1600 Pennsylvania Ave NW, Washington, DC 20500, United States",
32 "houseNumber": 1600,
33 "matchLevel": "Address",
34 "position": {
35 "lat": 38.89768,
36 "lng": -77.03655
37 },
38 "postal": "20500",
39 "score": 100,
40 "state": "DC",
41 "street": "Pennsylvania Ave NW"
42 }
43 }
44 },
45 "success": true
46}Sample Error Response
If you submit improperly created requests, expect a response similar to this.
1{
2 "api_info": {
3 "method": "POST",
4 "request_id": "fcad404b-ea52-429e-8989-40df1daccf4b",
5 "uri": "/core/batchgeocode/3"
6 },
7 "baseUrl": "https://sfmapsgateway-uengage1.sfdc-lywfpd.svc.sfdcfc.net/core/batchgeocode/3",
8 "error": {
9 "data": {
10 "minimum": 1,
11 "property": "root map"
12 },
13 "message": "Property mimimum not met"
14 },
15 "error_code": "GC-1011"
16}