Sergiy Kuzmenko avatar Sergiy Kuzmenko committed 3ef93a2

initial commit

Comments (0)

Files changed (8)

+Copyright (c) 2011 Serigy Kuzmenko
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+# file GENERATED by distutils, do NOT edit
+README.txt
+setup.py
+geocode\__init__.py
+geocode\google-geocoder.py
+geocode\google.py
+geocode\tests.py
+About
+=====
+
+Python-Geocoder is a simple library that uses Google Maps API for geocoding.
+
+
+Usage
+=====
+
+As python library::
+
+    >>> from geocode.google import GoogleGeocoderClient
+    
+    >>> geocoder = GoogleGeocoderClient(False) # must specify sensor parameter explicitely
+    >>> result = geocoder.geocode("massasauga park")
+    
+    >>> result.is_success()
+    True
+    
+    >>> len(result)
+    3
+    
+    # by default all get_*() methods will fetch the first result:
+    >>> result.get_formatted_address()
+    u'The Massasauga Provincial Park, The Archipelago, ON, Canada'
+    
+    # but you can also pass it an index parameter:
+    >>> result.get_formatted_address(2)
+    u'Massasauga Prairie Nature Preserve, Roseville, IL 61417, USA'
+    
+    >>> result.get_location()
+    (Decimal('45.19526590'), Decimal('-80.05372229999999'))
+    
+    >>> result.get_location_type()
+    u'APPROXIMATE'
+    
+    # each result object is an iterator containing nested dictionaries according to
+    # Google geocoding specifications (http://code.google.com/apis/maps/documentation/geocoding/)
+    >>> for r in result: print r["formatted_address"]
+    The Massasauga Provincial Park, The Archipelago, ON, Canada
+    The Massasauga Provincial Park, RR 2, Parry Sound, ON P0G, Canada
+    Massasauga Prairie Nature Preserve, Berwick, IL 61417, USA
+    
+    
+
+geocode/google-geocoder.py is a command line tool. A sample usage::
+
+    $ python geocode/google.py -a "massasauga park"
+    The Massasauga Provincial Park, The Archipelago, ON, Canada (45.19526590, -80.05372229999999) - APPROXIMATE
+    The Massasauga Provincial Park, RR 2, Parry Sound, ON P0G, Canada (45.1969970, -80.0398220) - APPROXIMATE
+    Massasauga Prairie Nature Preserve, Roseville, IL 61417, USA (40.76002530, -90.57780330) - APPROXIMATE
+
+For more usage information run::
+
+    $ python geocode/google.py --help

Empty file added.

geocode/google-geocoder.py

+#!/usr/bin/env python
+"""
+Command line interface fro Google geocoding service.
+"""
+
+from geocode.google import GoogleGeocoder, GoogleGeocoderClient
+
+def main():
+    """Command line interface that outputs geocoding results in json format."""
+    import sys
+    from optparse import OptionParser
+    
+    parser = OptionParser()
+    parser.add_option("-a", "--address", dest="address", default=None)
+    parser.add_option("-p", "--latlng", dest="latlng", default=None, help="comma separated latitude and longitude")
+    parser.add_option("-b", "--bounds", dest="bounds", default=None)
+    parser.add_option("-r", "--region", dest="region", default=None)
+    parser.add_option("-l", "--language", dest="language", default=None)
+    parser.add_option("-s", "--sensor", dest="sensor", default=False)
+    parser.add_option("-o", "--output", dest="output", default="json")
+    parser.add_option("--raw", dest="raw", action="store_true", default=False, help="output raw results in json format.")
+    
+    (options, args) = parser.parse_args()
+    
+    str2tuple = lambda x: tuple(map(lambda y:int(y), x.split(",")))
+    
+    latlng = None
+    bounds = None
+    
+    if options.latlng:
+        latlng = str2tuple(options.latlng)
+    if options.bounds:
+        bounds = map(lambda x:str2tuple(x), options.bounds.split("|"))
+    
+    geocoder = GoogleGeocoderClient(bool(options.sensor))
+    try:
+        json_data = geocoder.geocode_raw(
+            options.output, options.address, options.latlng, options.bounds, \
+            options.region, options.language)
+    except AssertionError, e:
+        sys.stderr.write("ERROR: %s\n"%e)
+        sys.exit(1)
+    
+    if options.raw:
+        print json_data
+    else:
+        results = GoogleGeocoder(json_data)
+        if results.is_success():
+            for result in results:
+                print "%s (%s, %s) - %s" % (result["formatted_address"],
+                                            result["geometry"]["location"]["lat"],
+                                            result["geometry"]["location"]["lng"],
+                                            result["geometry"]["location_type"])
+        else:
+            print results.data["status"]
+
+if __name__ == "__main__":
+    main()

geocode/google.py

+from decimal import Decimal
+
+try:
+    from urllib2 import urlopen
+except ImportError:
+    from urllib import urlopen # urllib.urlopen was deprecated in 2.6
+from urllib import quote_plus
+
+try:
+    import json # 2.6 or newer
+except ImportError:
+    import simplejson as json
+
+GOOGLE_GEOCODING_API_URL = "http://maps.googleapis.com/maps/api/geocode/"
+LAT = 0 # latitude
+LNG = 1 # longitude
+
+
+class GoogleGeocoder(object):
+    """
+    GoogleGeocoder is a convenience class defining a number of shortcut
+    methods for reading geocoding data in json format.
+    
+    http://code.google.com/apis/maps/documentation/geocoding/#Results
+    
+    """
+    def __init__(self, data):
+        """
+        You can initialize GoogleGeododer manually by feeding it raw json data
+        or initialize it implicitely from a geocode() call to an instance of
+        GoogleGeocoderClient:
+        
+        >>> client = GoogleGeocoderClient(False)
+        >>> result = client.geocode("massasauga park")
+        
+        AssertionError will be raised if data is not a valid Google geocoding
+        JSON serialization.
+        """
+        
+        self.data = json.loads(data, parse_float=Decimal)
+        try:
+            assert "status" in self.data and "results" in self.data
+        except AssertionError:
+            raise AssertionError("Invalid JSON data: expecting dictionary with 'status' and 'results' keys")
+        
+    def __iter__(self):
+        return iter(self.results)
+    
+    def __len__(self):
+        return len(self.results)
+    
+    def __getitem__(self, i):
+        return self.results[i]
+    
+    def is_success(self):
+        """Whether or not geocoding was succesfull."""
+        return self.data["status"] == "OK"
+    
+    @property
+    def results(self):
+        """
+        Returns results list. For details see
+        http://code.google.com/apis/maps/documentation/geocoding/#Results
+        """
+        return self.data["results"]
+    
+    def get_formatted_address(self, id=0):
+        """
+        Returns formatted address for result specified by id (defaults to  the
+        first result):
+        
+        >>> result = GoogleGeocoderClient(False).geocode("massasauga park")
+        >>> result.get_formatted_address()
+        u'The Massasauga Provincial Park, The Archipelago, ON, Canada'
+        >>> result.get_formatted_address(-1) # last result
+        u'Massasauga Prairie Nature Preserve, Roseville, IL 61417, USA'
+        """
+        return self.data["results"][id]["formatted_address"]
+    
+    def get_location(self, id=0):
+        """
+        Returns a tuple of Decimals (lat, lng) representing address location
+        for result specified by id (first result by default):
+        
+        >>> result = GoogleGeocoderClient(False).geocode("massasauga park")
+        >>> result.get_location()
+        (Decimal('45.19526590'), Decimal('-80.05372229999999'))
+        """
+        return (
+            Decimal(self.data["results"][id]["geometry"]["location"]["lat"]),
+            Decimal(self.data["results"][id]["geometry"]["location"]["lng"]),
+            )
+    
+    def get_location_type(self, id=0):
+        """
+        Returns location type for result specified by id (first result by default):
+        
+        >>> result = GoogleGeocoderClient(False).geocode("massasauga park")
+        >>> result.get_location_type()
+        u'APPROXIMATE'
+        """
+        return self.data["results"][id]["geometry"]["location_type"]
+    
+    def get_address_component(self, component_type, id=0, long_name=False):
+        """
+        Returns the first matching address component of specified type. If id is
+        not specified, the first result will be examined. If component type is
+        not found None will be returned:
+        
+        >>> result = GoogleGeocoderClient(False).geocode("massasauga park")
+        >>> result.get_address_component("country")
+        u'CA'
+        
+        >>> result.get_address_component("country", long_name=True)
+        u'Canada'
+        
+        >>> result.get_address_component("invalid compoment") # returns None
+        """
+        key = "short_name"
+        if long_name:
+            key = "long_name"
+        for ac in self.data["results"][id]["address_components"]:
+            for t in ac["types"]:
+                if component_type == t:
+                    return ac[key]
+        return None
+
+    def get_address_components(self, component_type, id=0, long_name=False):
+        """
+        Like get_address_component but returns all matching components:
+        
+        >>> result = GoogleGeocoderClient(False).geocode("massasauga park")
+        >>> result.get_address_components("political")
+        [u'The Archipelago', u'Parry Sound District', u'ON', u'CA']
+        
+        >>> result.get_address_components("political", long_name=True)
+        [u'The Archipelago', u'Parry Sound District', u'Ontario', u'Canada']
+        
+        >>> result.get_address_components("invalid compoment")
+        []
+        """
+        results = []
+        key = "short_name"
+        if long_name:
+            key = "long_name"
+        for ac in self.data["results"][id]["address_components"]:
+            for t in ac["types"]:
+                if component_type == t:
+                    results.append(ac[key])
+        return results
+
+class GoogleGeocoderClient(object):
+    
+    def __init__(self, sensor):
+        """
+        Sensor parameter is boolean, for details see:
+        http://code.google.com/apis/maps/documentation/geocoding/
+        """
+        self.sensor = sensor
+    
+    def geocode_raw(self, output="json", address=None, latlng=None, bounds=None, region=None, language=None):
+        """ Returns raw geocoded address information in json or xml format.
+        Arguments:
+        output - "json" or "xml"
+        address - street address (e.g., 123 Main Street)
+        latlng - a tuple for latiture/longitude (expressed as Decimal or float)
+        bounds - an iterable whose elements are latlng tuples
+        region - ccTLD country/region name (e.g., "CA" for Canada)
+        language - language in which return results (e.g., "fr" for French)
+        
+        Either address or latlng (but not both) parameters are required. the rest is optional.
+        """
+        if (not (address or latlng)) or (address and latlng):
+            raise AssertionError("Either address or latlang is required (but not both)")
+        assert output in ("json", "xml")
+        
+        tuple2str = lambda x: "%s,%s" % (x[0], x[1])
+        
+        bool2str = lambda x:str(bool(x)).lower()
+        
+        params = ["sensor=" + bool2str(self.sensor)]
+        
+        if address:
+            if isinstance(address, unicode):
+                params.append("address=" + quote_plus(address.encode("utf8")))
+            else:
+                params.append("address=" + quote_plus(address))
+        if latlng:
+            params.append("latlng=" + tuple2str(latlng))
+        if bounds:
+            params.append("bounds=" + "|".join(map(lambda x:tuple2str(x), bounds)))
+        if region:
+            params.append("region=" + quote_plus(region))
+        if language:
+            params.append("language=" + quote_plus(language))
+        
+        url = GOOGLE_GEOCODING_API_URL + output + "?" + "&".join(params)
+        
+        handler = urlopen(url)
+        return handler.read()
+    
+    def geocode(self, address=None, latlng=None, bounds=None, region=None, language=None):
+        """ Returns an instance of GoogleGeocoder. """
+        return GoogleGeocoder(self.geocode_raw("json", address, latlng, bounds, region, language))
+
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+import unittest
+from decimal import Decimal
+from geocode.google import GoogleGeocoder, GoogleGeocoderClient
+
+class GoogleGeocoderValidAddressTests(unittest.TestCase):
+    """
+    Tests will fail if there is a network problem, such as misconfigured
+    proxy or no connection.
+    """
+    
+    def setUp(self):
+        self.address = "1 Front Street West, Toronto, ON"
+        self.region = "CA"
+        
+        client = GoogleGeocoderClient(sensor=False)
+        data = client.geocode_raw("json", self.address, region=self.region)
+        
+        self.geocoder = GoogleGeocoder(data)
+    
+    def test_success(self):
+        self.assertTrue(self.geocoder.is_success())
+        
+    def test_number_of_results(self):
+        self.assertTrue(len(self.geocoder.results) == 1)
+    
+    def test_formatted_address(self):
+        self.assertTrue("Toronto" in self.geocoder.get_formatted_address())
+    
+    def test_location_type(self):
+        location_type = self.geocoder.get_location_type()
+        expected_location_type = "ROOFTOP"
+        self.assertTrue(location_type == expected_location_type,
+                        "Unexpected location_type: got `%s` but expecting `%s`" % \
+                        (location_type, expected_location_type))
+        
+    def test_location(self):
+        (lat, lng) = self.geocoder.get_location()
+        expected_lat = Decimal("43.6463685")
+        expected_lng = Decimal("-79.3770610")
+        self.assertTrue(lat == expected_lat, "Expecting latitude `%s` but got `%s`" % (expected_lat, lat))
+        self.assertTrue(lng == expected_lng, "Expecting longitude `%s` but got `%s`" % (expected_lng, lng))
+
+
+if __name__ == '__main__':
+    unittest.main()
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+setup(
+    name="python-geocoder",
+    version="0.1",
+    description="A simple geocoding library using Google Maps APIs",
+    long_description=open("README.txt").read(),
+    author="Sergiy Kuzmenko",
+    author_email="sergiy@kuzmenko.org",
+    url="https://bitbucket.org/shelldweller/python-geocoder",
+    packages=["geocode"],
+    classifiers=[
+        'Development Status :: 3 - Alpha',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: MIT License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Internet',
+        'Topic :: Internet :: WWW/HTTP',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+    ]
+)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.