diff --git a/CRM/Utils/Geocode/Google.php b/CRM/Utils/Geocode/Google.php index ad670f6c94ffe796fde3d12bba175f70a4b98ca8..0885a3d3ae6a59daa4d31dc7e8a707c33b05af3e 100644 --- a/CRM/Utils/Geocode/Google.php +++ b/CRM/Utils/Geocode/Google.php @@ -51,8 +51,6 @@ class CRM_Utils_Geocode_Google { return FALSE; } - $config = CRM_Core_Config::singleton(); - $add = ''; if (!empty($values['street_address'])) { @@ -99,6 +97,37 @@ class CRM_Utils_Geocode_Google { $add .= '+' . urlencode(str_replace('', '+', $values['country'])); } + $coord = self::makeRequest($add); + + $values['geo_code_1'] = $coord['geo_code_1'] ?? 'null'; + $values['geo_code_2'] = $coord['geo_code_2'] ?? 'null'; + + if (isset($coord['geo_code_error'])) { + $values['geo_code_error'] = $coord['geo_code_error']; + } + + return isset($coord['geo_code_1'], $coord['geo_code_2']); + } + + /** + * @param string $address + * Plain text address + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public static function getCoordinates($address) { + return self::makeRequest(urlencode($address)); + } + + /** + * @param string $add + * Url-encoded address + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + */ + private static function makeRequest($add) { + + $config = CRM_Core_Config::singleton(); if (!empty($config->geoAPIKey)) { $add .= '&key=' . urlencode($config->geoAPIKey); } @@ -115,7 +144,7 @@ class CRM_Utils_Geocode_Google { if ($xml === FALSE) { // account blocked maybe? CRM_Core_Error::debug_var('Geocoding failed. Message from Google:', $string); - return FALSE; + return ['geo_code_error' => $string]; } if (isset($xml->status)) { @@ -126,23 +155,22 @@ class CRM_Utils_Geocode_Google { ) { $ret = $xml->result->geometry->location->children(); if ($ret->lat && $ret->lng) { - $values['geo_code_1'] = (float) $ret->lat; - $values['geo_code_2'] = (float) $ret->lng; - return TRUE; + return [ + 'geo_code_1' => (float) $ret->lat, + 'geo_code_2' => (float) $ret->lng, + ]; } } elseif ($xml->status == 'ZERO_RESULTS') { // reset the geo code values if we did not get any good values - $values['geo_code_1'] = $values['geo_code_2'] = 'null'; - return FALSE; + return []; } else { CRM_Core_Error::debug_var("Geocoding failed. Message from Google: ({$xml->status})", (string ) $xml->error_message); - $values['geo_code_1'] = $values['geo_code_2'] = 'null'; - $values['geo_code_error'] = $xml->status; - return FALSE; + return ['geo_code_error' => $xml->status]; } } + return []; } } diff --git a/Civi/Api4/Action/Address/GetCoordinates.php b/Civi/Api4/Action/Address/GetCoordinates.php new file mode 100644 index 0000000000000000000000000000000000000000..86bcd16a49026ab6095a476409dfd9e1c14d3589 --- /dev/null +++ b/Civi/Api4/Action/Address/GetCoordinates.php @@ -0,0 +1,43 @@ +<?php + +/* + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC. All rights reserved. | + | | + | This work is published under the GNU AGPLv3 license with some | + | permitted exceptions and without any warranty. For full license | + | and copyright information, see https://civicrm.org/licensing | + +--------------------------------------------------------------------+ + */ + +namespace Civi\Api4\Action\Address; + +use Civi\Api4\Generic\Result; + +/** + * Converts an address string to lat/long coordinates. + * + * @method $this setAddress(string $address) + * @method string getAddress() + */ +class GetCoordinates extends \Civi\Api4\Generic\AbstractAction { + + /** + * Address string to convert to lat/long + * + * @var string + * @required + */ + protected $address; + + public function _run(Result $result) { + $coord = \CRM_Utils_Geocode_Google::getCoordinates($this->address); + if (isset($coord['geo_code_1'], $coord['geo_code_2'])) { + $result[] = $coord; + } + elseif (!empty($coord['geo_code_error'])) { + throw new \API_Exception('Geocoding failed. ' . $coord['geo_code_error']); + } + } + +} diff --git a/Civi/Api4/Address.php b/Civi/Api4/Address.php index a4ec60aeb31eefeeac3fa2cd34681d97bd8f3877..c9db2a80150d569211bca93f329a8c14855c192f 100644 --- a/Civi/Api4/Address.php +++ b/Civi/Api4/Address.php @@ -54,4 +54,13 @@ class Address extends Generic\DAOEntity { ->setCheckPermissions($checkPermissions); } + /** + * @param bool $checkPermissions + * @return Action\Address\GetCoordinates + */ + public static function getCoordinates($checkPermissions = TRUE) { + return (new Action\Address\GetCoordinates(__CLASS__, __FUNCTION__)) + ->setCheckPermissions($checkPermissions); + } + } diff --git a/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php b/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php index f3a5048caa26d68f2e6038cba0c223336a9b5abb..42765f3b15135e8aebd582868b3b5268c75e3e53 100644 --- a/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php +++ b/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php @@ -12,6 +12,7 @@ namespace Civi\Api4\Service\Spec\Provider; +use Civi\Api4\Address; use Civi\Api4\Query\Api4SelectQuery; use Civi\Api4\Service\Spec\FieldSpec; use Civi\Api4\Service\Spec\RequestSpec; @@ -64,6 +65,12 @@ class AddressGetSpecProvider implements Generic\SpecProviderInterface { $distance = $distance * 1000.00; } + if (!isset($value['geo_code_1'], $value['geo_code_2'])) { + $value = Address::getCoordinates(FALSE) + ->setAddress($value['address']) + ->execute()->first(); + } + if ( isset($value['geo_code_1']) && is_numeric($value['geo_code_1']) && isset($value['geo_code_2']) && is_numeric($value['geo_code_2']) @@ -75,7 +82,6 @@ class AddressGetSpecProvider implements Generic\SpecProviderInterface { explode('.', $fieldAlias)[0] ); } - // Todo: If address string given without lat/long, convert it. return '(0)'; }