/**
  * Transform given address details into geographic coordinates
  *
  * @param bool $log
  *
  * @return mixed
  */
 public function geocode($log = false)
 {
     $Geocoder = RootLocator_GeocoderFactory::createGeocoder();
     $result = $Geocoder->geocode($this->fullAddress());
     if ($result->hasError()) {
         if ($log) {
             $this->update(['addressError' => $result->getErrorKey()]);
         }
         return false;
     }
     $coordinates = $result->getFirstCoordinates();
     return $this->update(['addressLatitude' => $coordinates['latitude'], 'addressLongitude' => $coordinates['longitude'], 'addressError' => null]);
 }
 /**
  * Add additional search parameters to the filter listing
  *
  * @param array $options
  *
  * @return Closure
  */
 private function getFilterWhereCallback(array $options)
 {
     return function (PerchQuery $query) use($options) {
         $radius = isset($options['$range']) ? (int) $options['$range'] : 25;
         // Geocode address to get coordinates
         if (isset($options['address'])) {
             $Geocoder = RootLocator_GeocoderFactory::createGeocoder();
             $result = $Geocoder->geocode($options['address']);
             if ($result->hasError()) {
                 $query->where[] = ' `addressID` IN(-1)';
             } else {
                 $coordinates = $result->getFirstCoordinates();
                 $include = $this->findAddressesByCoordinate($coordinates['latitude'], $coordinates['longitude'], $radius);
                 $query->where[] = ' `addressID` IN(' . implode(',', $include) . ')';
             }
         }
         // Plain coordinates
         if (isset($options['coordinates']) && is_array($options['coordinates'])) {
             list($lat, $lng) = $options['coordinates'];
             $include = $this->findAddressesByCoordinate($lat, $lng, $radius);
             $query->where[] = ' `addressID` IN(' . implode(',', $include) . ')';
         }
         // Exclude item
         if (isset($options['exclude'])) {
             $query->where[] = ' `addressID` <> ' . (int) $this->db->pdb($options['exclude']);
         }
         // Limit
         $query->where[] = ' `addressLatitude` IS NOT NULL AND `addressLongitude` IS NOT NULL';
         return $query;
     };
 }
 /**
  * Run through task queue and mass-geocode
  *
  * @param bool $delay
  *
  * @return int
  */
 public function processQueue($delay = false)
 {
     if (!$this->api) {
         PerchUtil::debug('Locator: Perch API must be set on Tasks class to process queue', 'error');
         return false;
     }
     $Addresses = new RootLocator_Addresses($this->api);
     $Geocoder = RootLocator_GeocoderFactory::createGeocoder();
     $Settings = $this->api->get('Settings');
     $Template = $this->api->get('Template');
     $Template->set('locator/address.html', 'locator');
     $batch = $Settings->get('root_locator_batch_size')->val();
     $tasks = $this->getBatch('address.geocode', $batch);
     $count = 0;
     if (!$tasks) {
         return $count;
     }
     foreach ($tasks as $Task) {
         $Address = $Addresses->find($Task->addressID());
         $result = $Geocoder->geocode($Address->fullAddress());
         if (!$Address) {
             PerchUtil::debug(sprintf('Locator: unable to process address `%s` - no record found', $Task->addressID()), 'error');
             $Task->delete();
             continue;
         }
         // Success, update the address and remove the task
         if (!$result->hasError()) {
             PerchUtil::debug('Locator: Geocoding success - clearing task', 'success');
             $coordinates = $result->getFirstCoordinates();
             $Address->update(['addressLatitude' => $coordinates['latitude'], 'addressLongitude' => $coordinates['longitude'], 'addressError' => null]);
             $Address->index($Template);
             $Task->delete();
             continue;
         }
         // Firstly, if our API limit has been reached then we need to try again tomorrow
         if ($result->hasError() && $result->getErrorKey() === 'quota_exceeded') {
             PerchUtil::debug('Locator: API Quota has been exceeded. Delaying queue.', 'notice');
             $this->delayQueue();
             break;
         }
         // If the task has not failed multiple times we can give it the benefit
         // of the doubt and retry it.
         if ($result->hasError() && !$Task->isLastAttempt()) {
             PerchUtil::debug('Locator: Geocoding failed - task reset for a new attempt', 'notice');
             $Task->requeue();
             continue;
         }
         // Ok, we tried everything and now we really do need
         // to tell the user what's gone wrong.
         if ($result->hasError() && $Task->isLastAttempt()) {
             PerchUtil::debug('Locator: Geocoding failed after multiple attempts. Clearing task and logging error.', 'error');
             $Address->update(['addressLatitude' => null, 'addressLongitude' => null, 'addressError' => $result->getErrorKey()]);
             $Address->index($Template);
             $Task->delete();
             // Admit defeat...
         }
         $count++;
         if ($delay) {
             sleep((int) $delay);
         }
     }
     return $count;
 }