summaryrefslogtreecommitdiff
path: root/addons/base_geolocalize/models/base_geocoder.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/base_geolocalize/models/base_geocoder.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/base_geolocalize/models/base_geocoder.py')
-rw-r--r--addons/base_geolocalize/models/base_geocoder.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/addons/base_geolocalize/models/base_geocoder.py b/addons/base_geolocalize/models/base_geocoder.py
new file mode 100644
index 00000000..f3e8d785
--- /dev/null
+++ b/addons/base_geolocalize/models/base_geocoder.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import requests
+import logging
+
+from odoo import api, fields, models, tools, _
+from odoo.exceptions import UserError
+
+
+_logger = logging.getLogger(__name__)
+
+
+class GeoProvider(models.Model):
+ _name = "base.geo_provider"
+ _description = "Geo Provider"
+
+ tech_name = fields.Char()
+ name = fields.Char()
+
+
+class GeoCoder(models.AbstractModel):
+ """
+ Abstract class used to call Geolocalization API and convert addresses
+ into GPS coordinates.
+ """
+ _name = "base.geocoder"
+ _description = "Geo Coder"
+
+ @api.model
+ def _get_provider(self):
+ prov_id = self.env['ir.config_parameter'].sudo().get_param('base_geolocalize.geo_provider')
+ if prov_id:
+ provider = self.env['base.geo_provider'].browse(int(prov_id))
+ if not prov_id or not provider.exists():
+ provider = self.env['base.geo_provider'].search([], limit=1)
+ return provider
+
+ @api.model
+ def geo_query_address(self, street=None, zip=None, city=None, state=None, country=None):
+ """ Converts address fields into a valid string for querying
+ geolocation APIs.
+ :param street: street address
+ :param zip: zip code
+ :param city: city
+ :param state: state
+ :param country: country
+ :return: formatted string
+ """
+ provider = self._get_provider().tech_name
+ if hasattr(self, '_geo_query_address_' + provider):
+ # Makes the transformation defined for provider
+ return getattr(self, '_geo_query_address_' + provider)(street, zip, city, state, country)
+ else:
+ # By default, join the non-empty parameters
+ return self._geo_query_address_default(street=street, zip=zip, city=city, state=state, country=country)
+
+ @api.model
+ def geo_find(self, addr, **kw):
+ """Use a location provider API to convert an address string into a latitude, longitude tuple.
+ Here we use Openstreetmap Nominatim by default.
+ :param addr: Address string passed to API
+ :return: (latitude, longitude) or None if not found
+ """
+ provider = self._get_provider().tech_name
+ try:
+ service = getattr(self, '_call_' + provider)
+ result = service(addr, **kw)
+ except AttributeError:
+ raise UserError(_(
+ 'Provider %s is not implemented for geolocation service.'
+ ) % provider)
+ except UserError:
+ raise
+ except Exception:
+ _logger.debug('Geolocalize call failed', exc_info=True)
+ result = None
+ return result
+
+ @api.model
+ def _call_openstreetmap(self, addr, **kw):
+ """
+ Use Openstreemap Nominatim service to retrieve location
+ :return: (latitude, longitude) or None if not found
+ """
+ if not addr:
+ _logger.info('invalid address given')
+ return None
+ url = 'https://nominatim.openstreetmap.org/search'
+ try:
+ headers = {'User-Agent': 'Odoo (http://www.odoo.com/contactus)'}
+ response = requests.get(url, headers=headers, params={'format': 'json', 'q': addr})
+ _logger.info('openstreetmap nominatim service called')
+ if response.status_code != 200:
+ _logger.error('Request to openstreetmap failed.\nCode: %s\nContent: %s' % (response.status_code, response.content))
+ result = response.json()
+ except Exception as e:
+ self._raise_query_error(e)
+ geo = result[0]
+ return float(geo['lat']), float(geo['lon'])
+
+ @api.model
+ def _call_googlemap(self, addr, **kw):
+ """ Use google maps API. It won't work without a valid API key.
+ :return: (latitude, longitude) or None if not found
+ """
+ apikey = self.env['ir.config_parameter'].sudo().get_param('base_geolocalize.google_map_api_key')
+ if not apikey:
+ raise UserError(_(
+ "API key for GeoCoding (Places) required.\n"
+ "Visit https://developers.google.com/maps/documentation/geocoding/get-api-key for more information."
+ ))
+ url = "https://maps.googleapis.com/maps/api/geocode/json"
+ params = {'sensor': 'false', 'address': addr, 'key': apikey}
+ if kw.get('force_country'):
+ params['components'] = 'country:%s' % kw['force_country']
+ try:
+ result = requests.get(url, params).json()
+ except Exception as e:
+ self._raise_query_error(e)
+
+ try:
+ if result['status'] == 'ZERO_RESULTS':
+ return None
+ if result['status'] != 'OK':
+ _logger.debug('Invalid Gmaps call: %s - %s',
+ result['status'], result.get('error_message', ''))
+ error_msg = _('Unable to geolocate, received the error:\n%s'
+ '\n\nGoogle made this a paid feature.\n'
+ 'You should first enable billing on your Google account.\n'
+ 'Then, go to Developer Console, and enable the APIs:\n'
+ 'Geocoding, Maps Static, Maps Javascript.\n') % result.get('error_message')
+ raise UserError(error_msg)
+ geo = result['results'][0]['geometry']['location']
+ return float(geo['lat']), float(geo['lng'])
+ except (KeyError, ValueError):
+ _logger.debug('Unexpected Gmaps API answer %s', result.get('error_message', ''))
+ return None
+
+ @api.model
+ def _geo_query_address_default(self, street=None, zip=None, city=None, state=None, country=None):
+ address_list = [
+ street,
+ ("%s %s" % (zip or '', city or '')).strip(),
+ state,
+ country
+ ]
+ address_list = [item for item in address_list if item]
+ return tools.ustr(', '.join(address_list))
+
+ @api.model
+ def _geo_query_address_googlemap(self, street=None, zip=None, city=None, state=None, country=None):
+ # put country qualifier in front, otherwise GMap gives wrong# results
+ # e.g. 'Congo, Democratic Republic of the' => 'Democratic Republic of the Congo'
+ if country and ',' in country and (
+ country.endswith(' of') or country.endswith(' of the')):
+ country = '{1} {0}'.format(*country.split(',', 1))
+ return self._geo_query_address_default(street=street, zip=zip, city=city, state=state, country=country)
+
+ def _raise_query_error(self, error):
+ raise UserError(_('Error with geolocation server:') + ' %s' % error)