/** * 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; }