function getBubbles(url, tile, callback) {
  let rect = tile.extent.rectangle();
  let urlForRequest = url
    .replace('{key}', bubbleAppKey)
    .replace('{bbox}', [rect[1], rect[0], rect[3], rect[2]].join(','))
    .replace('{count}', maxResults);

    const controller = new AbortController();
    fetch(urlForRequest, { signal: controller.signal })
      .then(function(response) {
        if (!response.ok) {
          throw new Error(response.status + ' ' + response.statusText);
        }
        return response.json();
      }).then(function(result) {
        if (!result) {
          callback(null);
        }
        return callback(result || []);
      }).catch(function(err) {
        if (err.name === 'AbortError') {
        // ignore aborted requests, e.g. from duplicate requests while zooming/panning the map
        } else {
          throw new Error(err);
        }
      });
    return controller;
  }
getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).