Geoclue.Base

Package Geoclue :: Module Base |
[hide private]
[frames] | no frames]

# Source Code for Module Geoclue.Base

      1  # -*- coding: utf-8 -*-
      2  # Copyright (c) 2009 - Paulo Cabido <paulo.cabido@gmail.com>
      3  #
      4  # This program is free software: you can redistribute it and/or modify it under
      5  # the terms of the GNU General Public License as published by the Free Software
      6  # Foundation, either version 3 of the License, or (at your option) any later
      7  # version.
      8  #
      9  # This program is distributed in the hope that it will be useful, but WITHOUT
     10  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     11  # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     12  # details.
     13  #
     14  # You should have received a copy of the GNU General Public License along with
     15  # this program.  If not, see <http://www.gnu.org/licenses/>.
     16
     17  import os
     18  import sys
     19  import math
     20
     21  import dbus
     22  from dbus.mainloop.glib import DBusGMainLoop
     23  import gobject
     24
     25  import geoclue
     26  from Signal import Signal
     27
     28  DBusGMainLoop(set_as_default=True)
     29
     30 -class DiscoverLocation:
     31      """ Discovers the location form the best available provider
     32
     33      L{DiscoverLocation} is a object that provides a nice API interface
     34      for Geoclue.
     35      """
     36
     37 -    def __init__(self, providers_path="/usr/share/geoclue-providers"):
     38          """Construct a L{DiscoverLocation} object.
     39
     40          @param providers_path: The path to the providers. The default
     41          path to the providers is /usr/share/geoclue-providers.
     42          """
     43          self.bus = dbus.SessionBus()
     44
     45          self.signal = Signal()
     46
     47          # stores the location info
     48          self.location_info = {}
     49
     50          # Insipered by Pierre-Luc Beaudoin - geoclue_properties.py
     51          # TODO: add an exception to this part of the code in case of the wrong
     52          # or nonexisting dir
     53          self.providers = []
     54
     55          dir = os.listdir(providers_path)
     56
     57          for filename in dir:
     58              (name, ext) = os.path.splitext(filename)
     59
     60              if ext == ".provider":
     61                  complete = os.path.join(providers_path, filename)
     62                  provider = geoclue.GeoclueProvider (complete)
     63                  self.providers.append([provider,
     64                    provider.name,
     65                    provider.interfaces & geoclue.INTERFACE_ADDRESS,
     66                    provider.interfaces & geoclue.INTERFACE_POSITION,
     67                    provider.interfaces & geoclue.INTERFACE_GEOCODE,
     68                    provider.interfaces & geoclue.INTERFACE_REVERSE_GEOCODE,
     69                    provider.service,
     70                    provider.path
     71                    ])
     72
     73 -    def init(self, accuracy=geoclue.ACCURACY_LEVEL_COUNTRY, resource=geoclue.RESOURCE_NETWORK):
     74          """Initializes Geoclue.
     75
     76          @param accuracy:  The desired accuracy.
     77          @param resource: The resource to be used.
     78          """
     79          self.accuracy = accuracy
     80          self.resource = resource
     81
     82          try:
     83              self.master = self.bus.get_object(geoclue.MASTER_IFACE, geoclue.MASTER_PATH)
     84              self.client = self.bus.get_object(geoclue.MASTER_IFACE, self.master.Create())
     85
     86              # connects to detect changes on the address and position providers
     87              self.client.connect_to_signal("AddressProviderChanged", self.on_address_provider_changed)
     88              self.client.connect_to_signal("PositionProviderChanged", self.on_position_provider_changed)
     89
     90              self.address = dbus.Interface(self.client, dbus_interface=geoclue.ADDRESS_IFACE)
     91              self.address.connect_to_signal("AddressChanged", self.on_address_changed)
     92              self.client.AddressStart()
     93
     94              self.position = dbus.Interface(self.client, dbus_interface=geoclue.POSITION_IFACE)
     95              self.position.connect_to_signal("PositionChanged", self.on_position_changed)
     96              self.client.PositionStart()
     97
     98              self.client.SetRequirements(self.accuracy, 0, True, self.resource)
     99
    100              try:
    101                  self.on_address_changed(*self.address.GetAddress())
    102              except Exception, e:
    103                  return False
    104
    105              try:
    106                  self.on_position_changed(*self.position.GetPosition())
    107              except Exception, e:
    108                  return False
    109
    110              return True
    111          except Exception, e:
    112              print "Error: %s" % e
    113              return False
    114
    115 -    def provider_status(self, provider):
    116          """Checks a provider's status.
    117
    118          @param provider: A provider instance.
    119          @return: The status.
    120          """
    121          obj = dbus.Interface(provider.get_proxy(), dbus_interface=geoclue.GEOCLUE_IFACE)
    122          status = obj.GetStatus()
    123
    124          if status == geoclue.STATUS_ERROR:
    125              return "error"
    126          elif status == geoclue.STATUS_UNAVAILABLE:
    127              return "unavailable"
    128          elif status == geoclue.STATUS_ACQUIRING:
    129              return "acquiring"
    130          elif status == geoclue.STATUS_AVAILABLE:
    131              return "available"
    132          else:
    133              return "error"
    134
    135 -    def provider_info(self, provider):
    136          """Returns the provider's Info.
    137
    138          @return: A dictionary with the provider's name and descripiton.
    139          """
    140          obj = dbus.Interface(provider.get_proxy(), dbus_interface=geoclue.GEOCLUE_IFACE)
    141          info = obj.GetProviderInfo()
    142          tmp = {}
    143          tmp['name'] = str(info[0])
    144          tmp['description'] = str(info[1])
    145          return tmp
    146
    147 -    def set_requirements(self, accuracy, time, require_updates, resource):
    148          """Set the client requirements.
    149
    150          @param accuracy: The required minimum accuracy.
    151          @param time: The minimum time between update signals.
    152          @param require_updates: C{True} if updates are required or C{False} if updates
    153          are not required.
    154          @param resource: The resources that are allowed to be used.
    155          """
    156          self.accuracy = accuracy
    157          self.resource = resource
    158          self.client.SetRequirements(accuracy, time, require_updates, allowed_resources)
    159
    160      # provider changed methods, not really being used but it's useful to have
    161      # them here just in case
    162 -    def on_address_provider_changed(self, name, description, service, path):
    163          #print "Address provider changed"
    164          pass
    165
    166 -    def on_position_provider_changed(self, name, description, service, path):
    167          #print "Position provider changed"
    168          pass
    169
    170 -    def update_location_address(self, address):
    171          """Updates the the location's address with the given C{address}.
    172
    173          @address: The new address.
    174          """
    175          if address.has_key('street'):
    176              self.location_info['street'] = address['street']
    177
    178          # TODO: postalcode ?
    179
    180          if address.has_key('area'):
    181              self.locatio_infon['area'] = address['area']
    182
    183          if address.has_key('locality'):
    184              self.location_info['locality'] = address['locality']
    185
    186          if address.has_key('country'):
    187              self.location_info['country'] = address['country']
    188
    189          if address.has_key('region'):
    190              self.location_info['region'] = address['region']
    191
    192          if address.has_key('countrycode'):
    193              self.location_info['countrycode'] = address['countrycode']
    194
    195 -    def on_address_changed(self, timestamp, address, accuracy):
    196          """When the address changes the location info dictionary is updated.
    197
    198          @param timestamp: The timestamp.
    199          @param address: The new address.
    200          @accuracy: The accuracy.
    201          """
    202          self.location_info['address_timestamp'] = timestamp
    203          self.update_location_address(address)
    204          self.signal()
    205
    206 -    def on_position_changed(self, fields, timestamp, latitude, longitude, altitude, accuracy):
    207          """When the position changes the location info dictionary is updated.
    208
    209          @param fields: The fields.
    210          @param timestamp: The timestamp.
    211          @param latitude: The new latitude.
    212          @param longitude: The new longitude.
    213          @param altitude: The new altitude.
    214          @param accuracy: The accuracy.
    215          """
    216          #print accuracy # I used this print to check the accuracy format
    217          self.location_info['position_timestamp'] = timestamp
    218          self.location_info['latitude'] = latitude
    219          self.location_info['longitude'] = longitude
    220          self.location_info['altitude'] = altitude
    221          self.signal()
    222
    223      # returns the current values for location and position
    224 -    def get_location_info(self):
    225          """Returns the location info dictionary.
    226
    227          @return: The location info dictionary.
    228          """
    229          return self.location_info
    230
    231 -    def get_available_providers(self):
    232          """Returns the available providers.
    233
    234          @return: A list of dictionarys,
    235          [PROVIDER, ADDRESS, POSITION, GEOCODING, REVERSE GEOCODING],
    236          with the name and True of False for supporting each of them
    237          """
    238          current_providers = []
    239          for provider in self.providers:
    240              tmp = {}
    241              tmp['name'] = provider[1]
    242              if provider[2] != 0:
    243                  tmp['address'] = True
    244              else:
    245                  tmp['address'] = False
    246
    247              if provider[3] != 0:
    248                  tmp['position'] = True
    249              else:
    250                  tmp['position'] = False
    251
    252              if provider[4] != 0:
    253                  tmp['geocoding'] = True
    254              else:
    255                  tmp['geocoding'] = False
    256
    257              if provider[5] != 0:
    258                  tmp['revgeocoding'] = True
    259              else:
    260                  tmp['revgeocoding'] = False
    261
    262              tmp['object'] = provider[0]
    263              tmp['service'] = provider[6]
    264              tmp['path'] = provider[7]
    265
    266              current_providers.append(tmp)
    267
    268          return current_providers
    269
    270 -    def set_position_provider(self, provider_name):
    271          """Set the position provider to a given C{provider_name} (if exists).
    272
    273          @param provider_name: The provider's name
    274          @return: C{True} if the provider exists or C{False} if a provider
    275          does not exist.
    276          """
    277          provider_exists = False
    278          for provider in self.providers:
    279              if provider[1].lower() == provider_name.lower() and provider[3] != 0:
    280                  current_provider = provider
    281                  provider_exists = True
    282                  break
    283
    284          if not provider_exists:
    285              return False
    286
    287          try:
    288              tmp_provider = current_provider[0].get_proxy()
    289              self.position = dbus.Interface(tmp_provider, dbus_interface=geoclue.POSITION_IFACE)
    290          except Exception, e:
    291              print "D-Bus error: %s" % e
    292              return False
    293
    294          try:
    295              self.on_position_changed(*self.position.GetPosition())
    296          except Exception, e:
    297              print e
    298
    299          return True
    300
    301 -    def validate_address(self, address):
    302          """Receives the address and validates/corrects it.
    303
    304          @param address: The address dictionary.
    305          @return: The address (with possible corrections).
    306          """
    307          tmp_address = {}
    308          if address.has_key('street'):
    309              tmp_address['street'] = address['street']
    310          else:
    311              tmp_address['street'] = ""
    312
    313          # TODO: postalcode ?
    314
    315          if address.has_key('area'):
    316                  tmp_address['area'] = address['area']
    317          else:
    318              tmp_address['area'] = ""
    319
    320          if address.has_key('locality'):
    321                  tmp_address['locality'] = address['locality']
    322          else:
    323              tmp_address['locality'] = ""
    324
    325          if address.has_key('country'):
    326              tmp_address['country'] = address['country']
    327          else:
    328              tmp_address['country'] = ""
    329
    330          if address.has_key('region'):
    331              tmp_address['region'] = address['region']
    332          else:
    333              tmp_address['region'] = ""
    334
    335          if address.has_key('countrycode'):
    336              tmp_address['countrycode'] = address['countrycode']
    337          else:
    338              tmp_address['countrycode'] = ""
    339
    340          return tmp_address
    341
    342      # TODO: add "valid-for" to continue to use the given provider
    343 -    def set_address_provider(self, provider_name, address=None):
    344          """Set the address provider
    345
    346          @param provider_name: the provider's name
    347          @param address: the new address (for Manual and Localnet providers)
    348          @returns:  C{True} if the provider exists or C{False} if a provider
    349          does not exist.
    350          """
    351          provider_exists = False
    352          for provider in self.providers:
    353              if provider[1].lower() == provider_name.lower() and provider[2] != 0:
    354                  current_provider = provider
    355                  provider_exists = True
    356                  break
    357
    358          if not provider_exists:
    359              return False
    360
    361          try:
    362              if (provider_name.lower() == "manual" or provider_name.lower() == "localnet") and address != None:
    363                  tmp_provider = current_provider[0].get_proxy()
    364                  tmp_provider.SetAddress(0, self.validate_address(address))
    365                  self.address = dbus.Interface(tmp_provider, dbus_interface=geoclue.ADDRESS_IFACE)
    366              elif (provider_name.lower() == "manual" or provider_name.lower() == "localnet") and address == None:
    367                  return False
    368              else:
    369                  self.address = dbus.Interface(current_provider[0].get_proxy(), dbus_interface=geoclue.ADDRESS_IFACE)
    370          except Exception, e:
    371              print "D-Bus error: %s" % e
    372              return False
    373
    374          try:
    375              self.on_address_changed(*self.address.GetAddress())
    376          except Exception, e:
    377              print e
    378
    379          return True
    380
    381 -    def get_position_provider(self):
    382          """Returns the name of the current position provider.
    383
    384          @return: The name of the current position provider.
    385          """
    386          return self.client.GetPositionProvider()[0]
    387
    388 -    def get_address_provider(self):
    389          """Returns the name of the current address provider.
    390
    391          @return: The name of the current address provider.
    392          """
    393          return self.client.GetAddressProvider()[0]
    394
    395 -    def compare_position(self, latitude, longitude, proximity_factor=None):
    396          """Compare the current position to a given position.
    397
    398          Note: ploum's contribution
    399
    400          @param latitude: latitude of the position
    401          @param longitude: longitude of the position
    402          @param proximity_factor: the near by proximity factor. ie, 0.5 is 500 meters
    403          """
    404          if proximity_factor == None:
    405              # 500 meters
    406              dis_max = 0.5
    407          else:
    408              dis_max = proximity_factor
    409
    410          # todo_later : (calibration must be done with a well known distance )
    411          #This method assumes a spheroidal model for the earth with
    412          #an average radius of 6364.963 km.
    413          #The formula is estimated to have an accuracy of about 200 metres
    414          #over 50 km, but may deteriorate with longer distances.
    415          #source : http://www.ga.gov.au/geodesy/datums/distance.jsp
    416          dl = self.location_info['latitude'] - latitude
    417          dg = self.location_info['longitude'] - longitude
    418          term1 = 111.08956 * (dl + 0.000001)
    419          term2 = math.cos(self.location_info['latitude'] + (dl/2))
    420          term3 = (dg + 0.000001) / (dl + 0.000001)
    421          d = term1 / math.cos(math.atan(term2 * term3))
    422          result =  math.fabs(d) < dis_max
    423          return result
    424
    425 -    def reverse_position(self, latitude, longitude, accuracy):
    426          """Returns an address that corresponds to a given position.
    427
    428          @param latitude: The position's latitude.
    429          @param longitude: The position's longitude.
    430          @param accuracy: The accuracy.
    431          @return: An address.
    432          """
    433          provider_exists = False
    434          for provider in self.providers:
    435              if provider[1].lower() == "Geonames Provider".lower():
    436                  current_provider = provider
    437                  provider_exists = True
    438                  break
    439
    440          if not provider_exists:
    441              return None
    442
    443          try:
    444              revgeocoder = dbus.Interface(current_provider[0].get_proxy(), geoclue.REVERSE_IFACE)
    445              revaddress = revgeocoder.PositionToAddress(float(latitude), float(longitude), (accuracy, 0, 0))
    446
    447              #add the values to the address of the location variable
    448              tmp_address = {}
    449              for key, item in revaddress[0].items():
    450                  tmp_address[unicode(key)] = unicode(item)
    451
    452              return self.validate_address(tmp_address)
    453          except Exception, e:
    454              print "D-Bus error: %s" % e
    455              return None
    456
    457 -    def connect(self, func):
    458          """Connects a given function to the signal.
    459          The signal action if any change to the info location dictionary.
    460
    461          @param func: The function to connect to the signal.
    462          """
    463          self.signal.connect(func)
    464
    465 -    def disconnect(self, func):
    466          """Disconnects a given function from the signal.
    467
    468          @param func: The function to disconnect from the signal.
    469          """
    470          self.signal.disconnect(func)
    471