-
Notifications
You must be signed in to change notification settings - Fork 451
/
Methods for geocoding multiple addresses.html
351 lines (297 loc) · 14.3 KB
/
Methods for geocoding multiple addresses.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
<!DOCTYPE html>
<html lang="en">
<head>
<title>Methods for geocoding multiple addresses - Azure Maps Web SDK Samples</title>
<meta charset="utf-8" />
<link rel="shortcut icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="This sample shows two different methods for geocoding a bunch of addresses quickly from within a web app." />
<meta name="keywords" content="Microsoft maps, map, gis, API, SDK, services, module, geolocation, search, fuzzy, geocode, geocoding, points of interest, POI, address, addresses, places, batch, batch geocode" />
<meta name="author" content="Microsoft Azure Maps" />
<meta name="version" content="2.0" />
<meta name="screenshot" content="screenshot.jpg" />
<!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
<link href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.css" rel="stylesheet" />
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.js"></script>
<!-- Add a reference to the Azure Maps Rest Helper JavaScript file. -->
<script src="https://samples.azuremaps.com/lib/azure-maps/azure-maps-helper.min.js"></script>
<script>
//Batch service documentation: https://docs.microsoft.com/en-us/rest/api/maps/search/postsearchaddressbatch
var map, datasource, searchURL;
var start, end, isBusy = false;
var results = [];
var searchOptions = {
view: 'Auto',
limit: 1 //Only need one result per address.
};
//Set of addresses to geocode.
var addresses = [
'7014 E Camelback Rd, Scottsdale, AZ, US',
'1640 Redwood Highway, Corte Madera, CA, US',
'3333 Bristol Street, Costa Mesa, CA, US',
'100 West Broadway, Glendale, CA, US',
'10250 Santa Monica Blvd., Los Angeles, CA, US',
'28032 Marguerite Pkwy, Mission Viejo, CA, US',
'186A Stanford Shopping Center, Palo Alto, CA, US',
'7007 Friars Rd, San Diego, CA, US',
'845 Market St, San Francisco, CA, US',
'865 Market Street, San Francisco, CA, US',
'2855 Stevens Creek Blvd., Santa Clara, CA, US',
'3000 East 1st Avenue, Denver, CO, US',
'8505 Park Meadows Center Dr, Lone Tree, CO, US',
'7 Backus Avenue, Danbury, CT, US',
'500 Westfarms Mall, Farmington, CT, US',
'137 Christiana Mall, Newark, DE, US',
'19501 Biscayne Boulevard, Aventura, FL, US',
'7535 Dadeland Mall, Miami, FL, US',
'8001 South Orange Blossom Trail, Orlando, FL, US',
'3393 Peachtree Road NE, Atlanta, GA, US',
'4400 Ashford Dunwoody Road, Atlanta, GA, US',
'1450 Ala Moana Blvd 2238, Honolulu, HI, US',
'520 N Michigan Ave, Chicago, IL, US',
'49 Oakbrook Center, Oak Brook, IL, US',
'J-106 Woodfield Mall, Schaumburg, IL, US',
'8702 Keystone Crossing, Indianapolis, IN, US',
'11467 West 95th Street, Overland Park, KS, US',
'7101 Democracy Boulevard, Bethesda, MD, US',
'10300 Little Patuxent Parkway, Columbia, MD, US',
'800 Boylston Street, Boston, MA, US',
'250 Granite St, Braintree, MA, US',
'1245 Worcester Street, Natick, MA, US',
'2800 West Big Beaver Road, Troy, MI, US',
'162 South Ave, Bloomington, MN, US',
'1155 Saint Louis Galleria, St. Louis, MO, US',
'2417 St Louis Galleria, St. Louis, MO, US',
'3200 Las Vegas Blvd S, Las Vegas, NV, US',
'99 Rockingham Park Blvd, Salem, NH, US',
'400 Commons Way, Bridgewater, NJ, US',
'3710 Route 9 South, Freehold, NJ, US',
'One Garden State Plaza Way, Paramus, NJ, US',
'630 Old Country Rd, Garden City, NY, US',
'160 Walt Whitman Road, Huntington Station, NY, US',
'10 Columbus Circle, New York, NY, US',
'125 Westchester Avenue, White Plains, NY, US',
'4400 Sharon Road, Charlotte, NC, US',
'6910 Fayetteville Road, Durham, NC, US',
'26300 Cedar Rd, Beachwood, OH, US',
'7875 Montgomery Rd, Cincinnati, OH, US',
'1901 Nw Expressway, Oklahoma City, OK, US',
'7021 S Memorial Dr, Tulsa, OK, US',
'330 SW Yamhill Street, Portland, OR, US',
'9585 SW Washington Square Rd, Portland, OR, US',
'2126 Abbott Martin Road, Nashville, TN, US',
'3309 Esperanza Crossing, Austin, TX, US',
'2601 Preston Road, Frisco, TX, US',
'5015 Westheimer Road, Houston, TX, US',
'7400 San Pedro San Antonio, San Antonio, TX, US',
'15900 La Cantera Parkway, San Antonio, TX, US',
'1201 Lake Woodlands Dr, The Woodlands, TX, US',
'51 South Main Street, Salt Lake City, UT, US',
'1100 S Hayes Street, Arlington, VA, US',
'1961 Chain Bridge Road, Tysons Corner, VA, US',
'116 Bellevue Square, Bellevue, WA, US',
'2624 NE University Village Street, Seattle, WA, US',
'6455 Macleod Trail SW, Calgary, AB, CA',
'8882 170 Street Northwest, Edmonton, AB, CA',
'4700 Kingsway, Burnaby, BC, CA',
'701 West Georgia Street , Vancouver, BC, CA',
'220 Yonge Street, Toronto, ON, CA',
'3401 Dufferin Street, Toronto, ON, CA',
'100 City Centre Drive, Mississauga, ON, CA',
'525 FD Roosevelt Avenue, San Juan, PR',
'253-259 Regent Street, London, GB',
'Cnr Pitt St Mall and Market St, Sydney, NSW, AU'
];
// Your Azure Maps client id for accessing your Azure Maps account.
var azureMapsClientId = 'e6b6ab59-eb5d-4d25-aa57-581135b927f0';
// URL to your authentication service that retrieves an Microsoft Entra ID Token.
var tokenServiceUrl = 'https://samples.azuremaps.com/api/GetAzureMapsToken';
// Function to retrieve an Azure Maps access token.
function getToken(resolve, reject, map) {
fetch(tokenServiceUrl).then(response => {
if (response.ok) {
return response.text();
}
throw new Error('Failed to retrieve Azure Maps token.');
}).then(token => resolve(token)).catch(error => reject(error));
}
function getMap() {
//Initialize a map instance.
map = new atlas.Map('myMap', {
view: 'Auto',
// Add authentication details for connecting to Azure Maps.
authOptions: {
// Use Microsoft Entra ID authentication.
authType: 'anonymous',
clientId: azureMapsClientId,
getToken: getToken
// Alternatively, use an Azure Maps key.
// Get an Azure Maps key at https://azure.com/maps.
// NOTE: The primary key should be used as the key.
//authType: 'subscriptionKey',
//subscriptionKey: '[YOUR_AZURE_MAPS_KEY]'
}
});
//Wait until the map resources are ready.
map.events.add('ready', function () {
//Create a data source to add your data to.
datasource = new atlas.source.DataSource();
map.sources.add(datasource);
//Add a layer for rendering data.
map.layers.add(new atlas.layer.BubbleLayer(datasource));
});
}
var searchMethod;
function startSearch(method) {
if (!isBusy) {
isBusy = true;
searchMethod = method;
datasource.clear();
results = [];
start = window.performance.now();
switch (method) {
case 'parallel':
parallelGeocode();
break;
case 'batchSync':
syncBatchGeocode();
break;
default:
asyncBatchGeocode();
break;
}
}
}
/**
* This method will iterate through all the locations and make multiple parallel requests.
* The browser will limit the number of concurrent requests to the same domain.
*/
async function parallelGeocode() {
var requests = [];
//Create the request promises.
for (var i = 0; i < addresses.length; i++) {
requests.push(searchURL.searchAddress(atlas.service.Aborter.timeout(10000), addresses[i], searchOptions));
}
//Process the promises in parallel.
var responses = await Promise.all(requests);
//Extract the GeoJSON feature results.
responses.forEach(r => {
var fc = r.geojson.getFeatures();
if (fc.features.length > 0) {
results.push(fc.features[0]);
}
});
//Done.
endSearch();
}
async function syncBatchGeocode() {
//Get the post body request that contains the addresses to batch geocode.
var body = getBatchRequestBody();
var requestUrl = 'https://{azMapsDomain}/search/address/batch/sync/json?api-version=1.0';
var res = await processPostRequest(requestUrl, body);
var r = await res.json();
//Process the response.
processBatchResponse(r);
//Done.
endSearch();
}
async function asyncBatchGeocode() {
//Get the post body request that contains the addresses to batch geocode.
var body = getBatchRequestBody();
var requestUrl = 'https://{azMapsDomain}/search/address/batch/json?api-version=1.0';
var res = await processPostRequest(requestUrl, body);
//Ge tthe batch status URL from the "location" response header.
var batchStatusUrl = res.headers.get('location');
//Check the statuc of the batch job.
var res2 = await fetch(batchStatusUrl, {
headers: new Headers(requestParams.headers)
});
//Check to see if batch process if complete.
if (res2.status === 200) {
var r = await res2.json();
//Process the response.
processBatchResponse(r);
//Done.
endSearch();
}
}
function endSearch() {
end = window.performance.now();
var t = end - start;
var addPerSec = Math.round(addresses.length / t * 1000);
var qps = (searchMethod.indexOf('batch') > -1) ? 1 : addPerSec;
document.getElementById('status').innerHTML = `<b>Status:</b><br/><ul><li>Processing time: ${Math.round(t)}ms</li><li>QPS: ${qps}</li><li>Addresses per second: ${addPerSec}</li></ul>`;
datasource.setShapes(results);
//Set the camera to the bounds of the results.
map.setCamera({
bounds: atlas.data.BoundingBox.fromData(results),
padding: 40
});
isBusy = false;
}
function getSearchOptionsQueryParams() {
//Creates a formatted key-value pair query string from a json object.
return Object.keys(searchOptions).map(function (key) {
return key + '=' + encodeURIComponent(searchOptions[key]);
}).join('&');
}
function getBatchRequestBody() {
var opts = getSearchOptionsQueryParams();
var batchItems = [];
//Create an array of address geocode requests.
for (var i = 0; i < addresses.length; i++) {
batchItems.push({ query: '?query=' + encodeURIComponent(addresses[i]) + '&' + opts });
}
return JSON.stringify({ batchItems: batchItems });
}
function processBatchResponse(r) {
//Convert the response into GeoJSON objects.
for (var i = 0; i < r.batchItems.length; i++) {
for (var j = 0; j < r.batchItems[i].response.results.length; j++) {
var l = r.batchItems[i].response.results[j];
results.push(new atlas.data.Feature(new atlas.data.Point([
l.position.lon,
l.position.lat
]), l));
}
}
}
</script>
<style>
#myMap {
position: relative;
width: 100%;
min-width: 290px;
height: 600px;
}
.sidePanel {
position: absolute;
top: 10px;
left: 10px;
border-radius: 10px;
background-color: white;
padding: 10px;
}
</style>
</head>
<body onload="getMap()">
<div id="myMap"></div>
<div class="sidePanel">
Geocode method:
<br /><br />
<input type="button" value="Parallel" onclick="startSearch('parallel')" />
<input type="button" value="Batch (Sync)" onclick="startSearch('batchSync')" />
<input type="button" value="Batch (Async)" onclick="startSearch('batchAsync')" />
<br /><br />
<div id="status"></div>
</div>
<fieldset>
<legend>Methods for geocoding multiple addresses</legend>
This sample shows three different methods for geocoding a bunch of addresses quickly from within a web app.
The first method makes multiple parallel requests to the address search service. This generates a high number of queries per second and only recommended when working with a small set of addresses.
The second method uses the synchronous batch address search service and can process up to 100 addresses in a single request.
The third method uses the asynchronous batch address search service and can process up to 10,000 addresses.
The batch services are great ways to geocode a large number of addresses while keeping the volume of queries per second low.
</fieldset>
</body>
</html>