geopy-0.95.1/ 0000755 € ¢Þ:€ +8~00000000000 12123101075 016631 5 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 geopy-0.95.1/geopy/ 0000755 € ¢Þ:€ +8~00000000000 12123101075 017754 5 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 geopy-0.95.1/geopy/__init__.py 0000644 € ¢Þ:€ +8~00000000527 12123077364 022106 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from geopy.point import Point from geopy.location import Location from geopy import geocoders VERSION = (0, 95, 1) def get_version(): version = '%s.%s' % (VERSION[0], VERSION[1]) if VERSION[2]: version = '%s.%s' % (version, VERSION[2]) if VERSION[3:]: version = '%s.%s' % (version, VERSION[3]) return version geopy-0.95.1/geopy/distance.py 0000644 € ¢Þ:€ +8~00000032341 12117720124 022130 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from math import atan, tan, sin, cos, pi, sqrt, atan2, acos, asin from geopy.units import radians from geopy import units, util from geopy.point import Point # Average great-circle radius in kilometers, from Wikipedia. # Using a sphere with this radius results in an error of up to about 0.5%. EARTH_RADIUS = 6372.795 # From http://www.movable-type.co.uk/scripts/LatLongVincenty.html: # The most accurate and widely used globally-applicable model for the earth # ellipsoid is WGS-84, used in this script. Other ellipsoids offering a # better fit to the local geoid include Airy (1830) in the UK, International # 1924 in much of Europe, Clarke (1880) in Africa, and GRS-67 in South # America. America (NAD83) and Australia (GDA) use GRS-80, functionally # equivalent to the WGS-84 ellipsoid. ELLIPSOIDS = { # model major (km) minor (km) flattening 'WGS-84': (6378.137, 6356.7523142, 1 / 298.257223563), 'GRS-80': (6378.137, 6356.7523141, 1 / 298.257222101), 'Airy (1830)': (6377.563396, 6356.256909, 1 / 299.3249646), 'Intl 1924': (6378.388, 6356.911946, 1 / 297.0), 'Clarke (1880)': (6378.249145, 6356.51486955, 1 / 293.465), 'GRS-67': (6378.1600, 6356.774719, 1 / 298.25) } class Distance(object): def __init__(self, *args, **kwargs): kilometers = kwargs.pop('kilometers', 0) if len(args) == 1: # if we only get one argument we assume # it's a known distance instead of # calculating it first kilometers += args[0] elif len(args) > 1: for a, b in util.pairwise(args): kilometers += self.measure(a, b) kilometers += units.kilometers(**kwargs) self.__kilometers = kilometers def __add__(self, other): if isinstance(other, Distance): return self.__class__(self.kilometers + other.kilometers) else: raise TypeError( "Distance instance must be added with Distance instance." ) def __neg__(self): return self.__class__(-self.kilometers) def __sub__(self, other): return self + -other def __mul__(self, other): return self.__class__(self.kilometers * other) def __div__(self, other): if isinstance(other, Distance): return self.kilometers / other.kilometers else: return self.__class__(self.kilometers / other) def __abs__(self): return self.__class__(abs(self.kilometers)) def __nonzero__(self): return bool(self.kilometers) def measure(self, a, b): raise NotImplementedError def __repr__(self): return 'Distance(%s)' % self.kilometers def __str__(self): return '%s km' % self.__kilometers def __cmp__(self, other): if isinstance(other, Distance): return cmp(self.kilometers, other.kilometers) else: return cmp(self.kilometers, other) @property def kilometers(self): return self.__kilometers @property def km(self): return self.kilometers @property def meters(self): return units.meters(kilometers=self.kilometers) @property def m(self): return self.meters @property def miles(self): return units.miles(kilometers=self.kilometers) @property def mi(self): return self.miles @property def feet(self): return units.feet(kilometers=self.kilometers) @property def ft(self): return self.feet @property def nautical(self): return units.nautical(kilometers=self.kilometers) @property def nm(self): return self.nautical class GreatCircleDistance(Distance): """ Use spherical geometry to calculate the surface distance between two geodesic points. This formula can be written many different ways, including just the use of the spherical law of cosines or the haversine formula. The class attribute `RADIUS` indicates which radius of the earth to use, in kilometers. The default is to use the module constant `EARTH_RADIUS`, which uses the average great-circle radius. """ RADIUS = EARTH_RADIUS def measure(self, a, b): a, b = Point(a), Point(b) lat1, lng1 = radians(degrees=a.latitude), radians(degrees=a.longitude) lat2, lng2 = radians(degrees=b.latitude), radians(degrees=b.longitude) sin_lat1, cos_lat1 = sin(lat1), cos(lat1) sin_lat2, cos_lat2 = sin(lat2), cos(lat2) delta_lng = lng2 - lng1 cos_delta_lng, sin_delta_lng = cos(delta_lng), sin(delta_lng) central_angle = acos( # We're correcting from floating point rounding errors on very-near and exact points here min(1.0, sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos_delta_lng)) # From http://en.wikipedia.org/wiki/Great_circle_distance: # Historically, the use of this formula was simplified by the # availability of tables for the haversine function. Although this # formula is accurate for most distances, it too suffers from # rounding errors for the special (and somewhat unusual) case of # antipodal points (on opposite ends of the sphere). A more # complicated formula that is accurate for all distances is: (below) d = atan2(sqrt((cos_lat2 * sin_delta_lng) ** 2 + (cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_delta_lng) ** 2), sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos_delta_lng) return self.RADIUS * d def destination(self, point, bearing, distance=None): point = Point(point) lat1 = units.radians(degrees=point.latitude) lng1 = units.radians(degrees=point.longitude) bearing = units.radians(degrees=bearing) if distance is None: distance = self if isinstance(distance, Distance): distance = distance.kilometers d_div_r = float(distance) / self.RADIUS lat2 = asin( sin(lat1) * cos(d_div_r) + cos(lat1) * sin(d_div_r) * cos(bearing) ) lng2 = lng1 + atan2( sin(bearing) * sin(d_div_r) * cos(lat1), cos(d_div_r) - sin(lat1) * sin(lat2) ) return Point(units.degrees(radians=lat2), units.degrees(radians=lng2)) class VincentyDistance(Distance): """ Calculate the geodesic distance between two points using the formula devised by Thaddeus Vincenty, with an accurate ellipsoidal model of the earth. The class attribute `ELLIPSOID` indicates which ellipsoidal model of the earth to use. If it is a string, it is looked up in the `ELLIPSOIDS` dictionary to obtain the major and minor semiaxes and the flattening. Otherwise, it should be a tuple with those values. The most globally accurate model is WGS-84. See the comments above the `ELLIPSOIDS` dictionary for more information. """ ELLIPSOID = 'WGS-84' def measure(self, a, b): a, b = Point(a), Point(b) lat1, lng1 = radians(degrees=a.latitude), radians(degrees=a.longitude) lat2, lng2 = radians(degrees=b.latitude), radians(degrees=b.longitude) if isinstance(self.ELLIPSOID, basestring): major, minor, f = ELLIPSOIDS[self.ELLIPSOID] else: major, minor, f = self.ELLIPSOID delta_lng = lng2 - lng1 reduced_lat1 = atan((1 - f) * tan(lat1)) reduced_lat2 = atan((1 - f) * tan(lat2)) sin_reduced1, cos_reduced1 = sin(reduced_lat1), cos(reduced_lat1) sin_reduced2, cos_reduced2 = sin(reduced_lat2), cos(reduced_lat2) lambda_lng = delta_lng lambda_prime = 2 * pi iter_limit = 20 while abs(lambda_lng - lambda_prime) > 10e-12 and iter_limit > 0: sin_lambda_lng, cos_lambda_lng = sin(lambda_lng), cos(lambda_lng) sin_sigma = sqrt( (cos_reduced2 * sin_lambda_lng) ** 2 + (cos_reduced1 * sin_reduced2 - sin_reduced1 * cos_reduced2 * cos_lambda_lng) ** 2 ) if sin_sigma == 0: return 0 # Coincident points cos_sigma = ( sin_reduced1 * sin_reduced2 + cos_reduced1 * cos_reduced2 * cos_lambda_lng ) sigma = atan2(sin_sigma, cos_sigma) sin_alpha = ( cos_reduced1 * cos_reduced2 * sin_lambda_lng / sin_sigma ) cos_sq_alpha = 1 - sin_alpha ** 2 if cos_sq_alpha != 0: cos2_sigma_m = cos_sigma - 2 * ( sin_reduced1 * sin_reduced2 / cos_sq_alpha ) else: cos2_sigma_m = 0.0 # Equatorial line C = f / 16. * cos_sq_alpha * (4 + f * (4 - 3 * cos_sq_alpha)) lambda_prime = lambda_lng lambda_lng = ( delta_lng + (1 - C) * f * sin_alpha * ( sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * ( -1 + 2 * cos2_sigma_m ** 2 ) ) ) ) iter_limit -= 1 if iter_limit == 0: raise ValueError("Vincenty formula failed to converge!") u_sq = cos_sq_alpha * (major ** 2 - minor ** 2) / minor ** 2 A = 1 + u_sq / 16384. * ( 4096 + u_sq * (-768 + u_sq * (320 - 175 * u_sq)) ) B = u_sq / 1024. * (256 + u_sq * (-128 + u_sq * (74 - 47 * u_sq))) delta_sigma = ( B * sin_sigma * ( cos2_sigma_m + B / 4. * ( cos_sigma * ( -1 + 2 * cos2_sigma_m ** 2 ) - B / 6. * cos2_sigma_m * ( -3 + 4 * sin_sigma ** 2 ) * ( -3 + 4 * cos2_sigma_m ** 2 ) ) ) ) s = minor * A * (sigma - delta_sigma) return s def destination(self, point, bearing, distance=None): point = Point(point) lat1 = units.radians(degrees=point.latitude) lng1 = units.radians(degrees=point.longitude) bearing = units.radians(degrees=bearing) if distance is None: distance = self if isinstance(distance, Distance): distance = distance.kilometers ellipsoid = self.ELLIPSOID if isinstance(ellipsoid, basestring): ellipsoid = ELLIPSOIDS[ellipsoid] major, minor, f = ellipsoid tan_reduced1 = (1 - f) * tan(lat1) cos_reduced1 = 1 / sqrt(1 + tan_reduced1 ** 2) sin_reduced1 = tan_reduced1 * cos_reduced1 sin_bearing, cos_bearing = sin(bearing), cos(bearing) sigma1 = atan2(tan_reduced1, cos_bearing) sin_alpha = cos_reduced1 * sin_bearing cos_sq_alpha = 1 - sin_alpha ** 2 u_sq = cos_sq_alpha * (major ** 2 - minor ** 2) / minor ** 2 A = 1 + u_sq / 16384. * ( 4096 + u_sq * (-768 + u_sq * (320 - 175 * u_sq)) ) B = u_sq / 1024. * (256 + u_sq * (-128 + u_sq * (74 - 47 * u_sq))) sigma = distance / (minor * A) sigma_prime = 2 * pi while abs(sigma - sigma_prime) > 10e-12: cos2_sigma_m = cos(2 * sigma1 + sigma) sin_sigma, cos_sigma = sin(sigma), cos(sigma) delta_sigma = B * sin_sigma * ( cos2_sigma_m + B / 4. * ( cos_sigma * ( -1 + 2 * cos2_sigma_m ) - B / 6. * cos2_sigma_m * ( -3 + 4 * sin_sigma ** 2) * ( -3 + 4 * cos2_sigma_m ** 2 ) ) ) sigma_prime = sigma sigma = distance / (minor * A) + delta_sigma sin_sigma, cos_sigma = sin(sigma), cos(sigma) lat2 = atan2( sin_reduced1 * cos_sigma + cos_reduced1 * sin_sigma * cos_bearing, (1 - f) * sqrt( sin_alpha ** 2 + ( sin_reduced1 * sin_sigma - cos_reduced1 * cos_sigma * cos_bearing ) ** 2 ) ) lambda_lng = atan2( sin_sigma * sin_bearing, cos_reduced1 * cos_sigma - sin_reduced1 * sin_sigma * cos_bearing ) C = f / 16. * cos_sq_alpha * (4 + f * (4 - 3 * cos_sq_alpha)) delta_lng = ( lambda_lng - (1 - C) * f * sin_alpha * ( sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * ( -1 + 2 * cos2_sigma_m ** 2 ) ) ) ) final_bearing = atan2( sin_alpha, cos_reduced1 * cos_sigma * cos_bearing - sin_reduced1 * sin_sigma ) lng2 = lng1 + delta_lng return Point(units.degrees(radians=lat2), units.degrees(radians=lng2)) # Set the default distance formula to the most generally accurate. distance = VincentyDistance geopy-0.95.1/geopy/format.py 0000644 € ¢Þ:€ +8~00000005306 12117720124 021627 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from geopy import units # Unicode characters for symbols that appear in coordinate strings. DEGREE = unichr(176) PRIME = unichr(8242) DOUBLE_PRIME = unichr(8243) ASCII_DEGREE = '' ASCII_PRIME = "'" ASCII_DOUBLE_PRIME = '"' LATIN1_DEGREE = chr(176) HTML_DEGREE = '°' HTML_PRIME = '′' HTML_DOUBLE_PRIME = '″' XML_DECIMAL_DEGREE = '°' XML_DECIMAL_PRIME = '′' XML_DECIMAL_DOUBLE_PRIME = '″' XML_HEX_DEGREE = '&xB0;' XML_HEX_PRIME = '&x2032;' XML_HEX_DOUBLE_PRIME = '&x2033;' ABBR_DEGREE = 'deg' ABBR_ARCMIN = 'arcmin' ABBR_ARCSEC = 'arcsec' DEGREES_FORMAT = "%(degrees)d%(deg)s %(minutes)d%(arcmin)s %(seconds)s%(arcsec)s" UNICODE_SYMBOLS = {'deg': DEGREE, 'arcmin': PRIME, 'arcsec': DOUBLE_PRIME} ASCII_SYMBOLS = {'deg': ASCII_DEGREE, 'arcmin': ASCII_PRIME, 'arcsec': ASCII_DOUBLE_PRIME} LATIN1_SYMBOLS = {'deg': LATIN1_DEGREE, 'arcmin': ASCII_PRIME, 'arcsec': ASCII_DOUBLE_PRIME} HTML_SYMBOLS = {'deg': HTML_DEGREE, 'arcmin': HTML_PRIME, 'arcsec': HTML_DOUBLE_PRIME} XML_SYMBOLS = {'deg': XML_DECIMAL_DEGREE, 'arcmin': XML_DECIMAL_PRIME, 'arcsec': XML_DECIMAL_DOUBLE_PRIME} ABBR_SYMBOLS = {'deg': ABBR_DEGREE, 'arcmin': ABBR_ARCMIN, 'arcsec': ABBR_ARCSEC} def format_degrees(degrees, format=DEGREES_FORMAT, symbols=ASCII_SYMBOLS): arcminutes = units.arcminutes(degrees=degrees - int(degrees)) arcseconds = units.arcseconds(arcminutes=arcminutes - int(arcminutes)) format_dict = dict( symbols, degrees=degrees, minutes=abs(arcminutes), seconds=abs(arcseconds) ) return format % format_dict DISTANCE_FORMAT = "%(magnitude)s%(unit)s" DISTANCE_UNITS = { 'km': lambda d: d, 'm': lambda d: units.meters(kilometers=d), 'mi': lambda d: units.miles(kilometers=d), 'ft': lambda d: units.feet(kilometers=d), 'nm': lambda d: units.nautical(kilometers=d), 'nmi': lambda d: units.nautical(kilometers=d) } def format_distance(kilometers, format=DISTANCE_FORMAT, unit='km'): magnitude = DISTANCE_UNITS[unit](kilometers) return format % {'magnitude': magnitude, 'unit': unit} _DIRECTIONS = [ ('north', 'N'), ('north by east', 'NbE'), ('north-northeast', 'NNE'), ('northeast by north', 'NEbN'), ('northeast', 'NE'), ('northeast by east', 'NEbE'), ('east-northeast', 'ENE'), ('east by north', 'EbN'), ('east', 'E'), ('east by south', 'EbS'), ('east-southeast', 'ESE'), ('southeast by east', 'SEbE'), ('southeast', 'SE'), ('southeast by south', 'SEbS'), ] DIRECTIONS, DIRECTIONS_ABBR = zip(*_DIRECTIONS) ANGLE_DIRECTIONS = dict((n * 11.25, d) for n, d in enumerate(DIRECTIONS)) ANGLE_DIRECTIONS_ABBR = dict((n * 11.25, d) for n, d in enumerate(DIRECTIONS_ABBR)) def format_direction(degrees): pass geopy-0.95.1/geopy/geocoders/ 0000755 € ¢Þ:€ +8~00000000000 12123101075 021726 5 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 geopy-0.95.1/geopy/geocoders/__init__.py 0000644 € ¢Þ:€ +8~00000000724 12117720124 024047 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from geopy.geocoders.bing import Bing from geopy.geocoders.google import Google from geopy.geocoders.googlev3 import GoogleV3 from geopy.geocoders.dot_us import GeocoderDotUS from geopy.geocoders.geonames import GeoNames from geopy.geocoders.wiki_gis import MediaWiki from geopy.geocoders.wiki_semantic import SemanticMediaWiki from geopy.geocoders.yahoo import Yahoo from geopy.geocoders.openmapquest import OpenMapQuest from geopy.geocoders.mapquest import MapQuest geopy-0.95.1/geopy/geocoders/base.py 0000644 € ¢Þ:€ +8~00000001643 12117720124 023223 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 class Geocoder(object): def __init__(self, format_string='%s'): self.format_string = format_string def geocode(self, location): raise NotImplementedError def reverse(self, point): raise NotImplementedError def geocode_one(self, location): results = self.geocode(location) first = None for result in results: if first is None: first = result else: raise GeocoderResultError("Geocoder returned more than one result!") if first is not None: return first else: raise GeocoderResultError("Geocoder returned no results!") def geocode_first(self, location): results = self.geocode(location) for result in results: return result return None class GeocoderError(Exception): pass class GeocoderResultError(GeocoderError): pass geopy-0.95.1/geopy/geocoders/bing.py 0000644 € ¢Þ:€ +8~00000006546 12123100364 023232 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 try: import json except ImportError: try: import simplejson as json except ImportError: from django.utils import simplejson as json from urllib import urlencode from urllib2 import urlopen from geopy.geocoders.base import Geocoder from geopy.util import logger, decode_page, join_filter class Bing(Geocoder): """Geocoder using the Bing Maps API.""" def __init__(self, api_key, format_string='%s', output_format=None): """Initialize a customized Bing geocoder with location-specific address information and your Bing Maps API key. ``api_key`` should be a valid Bing Maps API key. ``format_string`` is a string containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. ``output_format`` (DEPRECATED) is ignored """ if output_format != None: from warnings import warn warn('geopy.geocoders.bing.Bing: The `output_format` parameter is deprecated '+ 'and ignored.', DeprecationWarning) self.api_key = api_key self.format_string = format_string self.url = "http://dev.virtualearth.net/REST/v1/Locations?%s" def geocode(self, string, exactly_one=True): if isinstance(string, unicode): string = string.encode('utf-8') params = {'query': self.format_string % string, 'key': self.api_key } url = self.url % urlencode(params) return self.geocode_url(url, exactly_one) def geocode_url(self, url, exactly_one=True): logger.debug("Fetching %s..." % url) page = urlopen(url) return self.parse_json(page, exactly_one) def parse_json(self, page, exactly_one=True): """Parse a location name, latitude, and longitude from an JSON response.""" if not isinstance(page, basestring): page = decode_page(page) doc = json.loads(page) resources = doc['resourceSets'][0]['resources'] if exactly_one and len(resources) != 1: raise ValueError("Didn't find exactly one resource! " \ "(Found %d.)" % len(resources)) def parse_resource(resource): stripchars = ", \n" a = resource['address'] address = a.get('addressLine', '').strip(stripchars) city = a.get('locality', '').strip(stripchars) state = a.get('adminDistrict', '').strip(stripchars) zipcode = a.get('postalCode', '').strip(stripchars) country = a.get('countryRegion', '').strip(stripchars) city_state = join_filter(", ", [city, state]) place = join_filter(" ", [city_state, zipcode]) location = join_filter(", ", [address, place, country]) latitude = resource['point']['coordinates'][0] or None longitude = resource['point']['coordinates'][1] or None if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return (location, (latitude, longitude)) if exactly_one: return parse_resource(resources[0]) else: return [parse_resource(resource) for resource in resources] geopy-0.95.1/geopy/geocoders/dot_us.py 0000644 € ¢Þ:€ +8~00000006172 12123100433 023600 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 import getpass from urllib import urlencode from urllib2 import urlopen from geopy.geocoders.base import Geocoder from geopy import util import csv class GeocoderDotUS(Geocoder): def __init__(self, username=None, password=None, format_string='%s'): if username and (password is None): password = getpass.getpass( "geocoder.us password for %r: " % username ) self.format_string = format_string self.username = username self.__password = password def get_url(self): username = self.username password = self.__password if username and password: auth = '%s@%s:' % (username, password) resource = 'member/service/namedcsv' else: auth = '' resource = 'service/namedcsv' return 'http://%sgeocoder.us/%s' % (auth, resource) def geocode(self, query, exactly_one=True): if isinstance(query, unicode): query = query.encode('utf-8') query_str = self.format_string % query page = urlopen("%s?%s" % ( self.get_url(), urlencode({'address':query_str}) )) reader = csv.reader(page) places = [r for r in reader] # GeoNames only returns the closest match, no matter what. # #if exactly_one and len(places) != 1: # raise ValueError("Didn't find exactly one placemark! " \ # "(Found %d.)" % len(places)) # #if exactly_one: # return self._parse_result(places[0]) #else: # return [self._parse_result(place) for place in places] return self._parse_result(places[0]) @staticmethod def _parse_result(result): # turn x=y pairs ("lat=47.6", "long=-117.426") into dict key/value pairs: place = dict( filter(lambda x: len(x)>1, # strip off bits that aren't pairs (i.e. "geocoder modified" status string") map(lambda x: x.split('=', 1), result) # split the key=val strings into (key, val) tuples )) address = [ place.get('number', None), place.get('prefix', None), place.get('street', None), place.get('type', None), place.get('suffix', None) ] city = place.get('city', None) state = place.get('state', None) zip_code = place.get('zip', None) name = util.join_filter(", ", [ util.join_filter(" ", address), city, util.join_filter(" ", [state, zip_code]) ]) latitude = place.get('lat', None) longitude = place.get('long', None) if latitude and longitude: latlon = float(latitude), float(longitude) else: return None # TODO use Point/Location object API in 0.95 #if latitude and longitude: # point = Point(latitude, longitude) #else: # point = None #return Location(name, point, dict(result)) return name, latlon geopy-0.95.1/geopy/geocoders/geonames.py 0000644 € ¢Þ:€ +8~00000005335 12123100427 024104 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 import xml.dom.minidom from urllib import urlencode from urllib2 import urlopen from geopy import util try: import json except ImportError: try: import simplejson as json except ImportError: from django.utils import simplejson as json from geopy.geocoders.base import Geocoder class GeoNames(Geocoder): def __init__(self, format_string=None, output_format=None, country_bias=None): if format_string != None: from warnings import warn warn('geopy.geocoders.geonames.GeoNames: The `format_string` parameter is deprecated.'+ ' (It has always been ignored for GeoNames.)', DeprecationWarning) if output_format != None: from warnings import warn warn('geopy.geocoders.geonames.GeoNames: The `output_format` parameter is deprecated '+ 'and now ignored.', DeprecationWarning) self.country_bias = country_bias self.url = "http://ws.geonames.org/searchJSON?%s" def geocode(self, string, exactly_one=True): if isinstance(string, unicode): string = string.encode('utf-8') params = { 'q': string } if self.country_bias: params['countryBias'] = self.country_bias url = self.url % urlencode(params) return self.geocode_url(url, exactly_one) def geocode_url(self, url, exactly_one=True): page = urlopen(url) return self.parse_json(page, exactly_one) def parse_json(self, page, exactly_one): if not isinstance(page, basestring): page = util.decode_page(page) doc = json.loads(page) places = doc.get('geonames', []) if not places: return None if exactly_one and len(places) != 1: raise ValueError("Didn't find exactly one code! " \ "(Found %d.)" % len(places)) def parse_code(place): latitude = place.get('lat', None) longitude = place.get('lng', None) if latitude and longitude: latitude = float(latitude) longitude = float(longitude) else: return None placename = place.get('name') state = place.get('adminCode1', None) country = place.get('countryCode', None) location = ', '.join(filter(lambda x: bool(x), [placename, state, country] )) return (location, (latitude, longitude)) if exactly_one: return parse_code(places[0]) else: return [parse_code(place) for place in places] geopy-0.95.1/geopy/geocoders/google.py 0000644 € ¢Þ:€ +8~00000013263 12123100370 023556 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from urllib import urlencode from urllib2 import urlopen try: import json except ImportError: try: import simplejson as json except ImportError: from django.utils import simplejson as json from geopy.geocoders.base import Geocoder,GeocoderError,GeocoderResultError from geopy import Point, Location, util from warnings import warn class Google(Geocoder): """Geocoder using the Google Maps API.""" def __init__(self, api_key=None, domain='maps.googleapis.com', format_string='%s'): """Initialize a customized Google geocoder with location-specific address information and your Google Maps API key. ``api_key`` should be a valid Google Maps API key. Required as per Google Geocoding API V2 docs, but the API works without a key in practice. ``domain`` should be the localized Google Maps domain to connect to. The default is 'maps.google.com', but if you're geocoding address in the UK (for example), you may want to set it to 'maps.google.co.uk' to properly bias results. ``format_string`` is a string containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. """ warn('geopy.geocoders.google: The `geocoders.google.Google` geocoder uses the '+ 'older "V2" API and is deprecated and may be broken at any time. A '+ 'geocoder utilizing the "V3" API is available at '+ '`geocoders.googlev3.GoogleV3` and will become the default in a future '+ 'version. See RELEASES file and http://goo.gl/somDT for usage information.', DeprecationWarning ) if not api_key: raise ValueError( "The `geocoders.google.Google` (V2) API now requires the "+ "`api_key` argument. Please acquire and use an API key "+ "(http://goo.gl/EdoHX) or upgrade to "+ "the V3 API (`geocoders.googlev3.GoogleV3`), which does "+ "not require a key. ---- Please note that the V2 API is " + "deprecated and may not work after March 2013 or September 2013." ) if domain == "maps.google.com": raise ValueError( "The `geocoders.google.Google` (V2) API now requires the "+ "`domain` argument to be set to 'maps.googleapis.com'. Please "+ "change or remove your `domain` kwarg." ) self.api_key = api_key self.domain = domain self.format_string = format_string self.output_format = "json" @property def url(self): domain = self.domain.strip('/') return "http://%s/maps/geo?%%s" % domain def geocode(self, string, exactly_one=True): if isinstance(string, unicode): string = string.encode('utf-8') params = {'q': self.format_string % string, 'output': self.output_format.lower(), 'key': self.api_key, 'sensor': False } url = self.url % urlencode(params) return self.geocode_url(url, exactly_one) def geocode_url(self, url, exactly_one=True): util.logger.debug("Fetching %s..." % url) page = urlopen(url) dispatch = getattr(self, 'parse_' + self.output_format) return dispatch(page, exactly_one) def parse_json(self, page, exactly_one=True): if not isinstance(page, basestring): page = util.decode_page(page) doc = json.loads(page) places = doc.get('Placemark', []) if len(places) == 0: # Got empty result. Parse out the status code and raise an error if necessary. status = doc.get("Status", []) status_code = status["code"] self.check_status_code(status_code) return None elif exactly_one and len(places) != 1: raise ValueError("Didn't find exactly one placemark! " \ "(Found %d.)" % len(places)) def parse_place(place): location = place.get('address') longitude, latitude = place['Point']['coordinates'][:2] return (location, (latitude, longitude)) if exactly_one: return parse_place(places[0]) else: return [parse_place(place) for place in places] def check_status_code(self,status_code): if status_code == 400: raise GeocoderResultError("Bad request (Server returned status 400)") elif status_code == 500: raise GeocoderResultError("Unkown error (Server returned status 500)") elif status_code == 601: raise GQueryError("An empty lookup was performed") elif status_code == 602: raise GQueryError("No corresponding geographic location could be found for the specified location, possibly because the address is relatively new, or because it may be incorrect.") elif status_code == 603: raise GQueryError("The geocode for the given location could be returned due to legal or contractual reasons") elif status_code == 610: raise GBadKeyError("The api_key is either invalid or does not match the domain for which it was given.") elif status_code == 620: raise GTooManyQueriesError("The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time.") class GBadKeyError(GeocoderError): pass class GQueryError(GeocoderResultError): pass class GTooManyQueriesError(GeocoderResultError): pass geopy-0.95.1/geopy/geocoders/googlev3.py 0000644 € ¢Þ:€ +8~00000017661 12123100415 024035 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 '''Google Maps V3 geocoder. Largely adapted from the existing v2 geocoder with modifications made where possible to support the v3 api as well as to clean up the class without breaking its compatibility or diverging its api too far from the rest of the geocoder classes. ''' import base64 import hashlib import hmac from urllib import urlencode from urllib2 import urlopen try: import json except ImportError: try: import simplejson as json except ImportError: from django.utils import simplejson as json from geopy.geocoders.base import Geocoder, GeocoderResultError from geopy import util class GoogleV3(Geocoder): '''Geocoder using the Google Maps v3 API.''' def __init__(self, domain='maps.googleapis.com', protocol='http', client_id=None, secret_key=None): '''Initialize a customized Google geocoder. API authentication is only required for Google Maps Premier customers. ``domain`` should be the localized Google Maps domain to connect to. The default is 'maps.google.com', but if you're geocoding address in the UK (for example), you may want to set it to 'maps.google.co.uk' to properly bias results. ``protocol`` http or https. ``client_id`` Premier account client id. ``secret_key`` Premier account secret key. ''' super(GoogleV3, self).__init__() if protocol not in ('http', 'https'): raise ValueError, 'Supported protocols are http and https.' if client_id and not secret_key: raise ValueError, 'Must provide secret_key with client_id.' if secret_key and not client_id: raise ValueError, 'Must provide client_id with secret_key.' self.domain = domain.strip('/') self.protocol = protocol self.doc = {} if client_id and secret_key: self.client_id = client_id self.secret_key = secret_key self.premier = True else: self.premier = False def get_signed_url(self, params): '''Returns a Premier account signed url.''' params['client'] = self.client_id url_params = {'protocol': self.protocol, 'domain': self.domain, 'params': urlencode(params)} secret = base64.urlsafe_b64decode(self.secret_key) url_params['url_part'] = ( '/maps/api/geocode/json?%(params)s' % url_params) signature = hmac.new(secret, url_params['url_part'], hashlib.sha1) url_params['signature'] = base64.urlsafe_b64encode(signature.digest()) return ('%(protocol)s://%(domain)s%(url_part)s' '&signature=%(signature)s' % url_params) def get_url(self, params): '''Returns a standard geocoding api url.''' return 'http://%(domain)s/maps/api/geocode/json?%(params)s' % ( {'domain': self.domain, 'params': urlencode(params)}) def geocode_url(self, url, exactly_one=True): '''Fetches the url and returns the result.''' util.logger.debug("Fetching %s..." % url) page = urlopen(url) return self.parse_json(page, exactly_one) def geocode(self, string, bounds=None, region=None, language=None, sensor=False, exactly_one=True): '''Geocode an address. ``string`` (required) The address that you want to geocode. ``bounds`` (optional) The bounding box of the viewport within which to bias geocode results more prominently. ``region`` (optional) The region code, specified as a ccTLD ("top-level domain") two-character value. ``language`` (optional) The language in which to return results. See the supported list of domain languages. Note that we often update supported languages so this list may not be exhaustive. If language is not supplied, the geocoder will attempt to use the native language of the domain from which the request is sent wherever possible. ``sensor`` (required) Indicates whether or not the geocoding request comes from a device with a location sensor. This value must be either True or False. ''' if isinstance(string, unicode): string = string.encode('utf-8') params = { 'address': self.format_string % string, 'sensor': str(sensor).lower() } if bounds: params['bounds'] = bounds if region: params['region'] = region if language: params['language'] = language if not self.premier: url = self.get_url(params) else: url = self.get_signed_url(params) return self.geocode_url(url, exactly_one) def reverse(self, point, language=None, sensor=False, exactly_one=False): '''Reverse geocode a point. ``point`` (required) The textual latitude/longitude value for which you wish to obtain the closest, human-readable address ``language`` (optional) The language in which to return results. See the supported list of domain languages. Note that we often update supported languages so this list may not be exhaustive. If language is not supplied, the geocoder will attempt to use the native language of the domain from which the request is sent wherever possible. ``sensor`` (required) Indicates whether or not the geocoding request comes from a device with a location sensor. This value must be either True or False. ''' params = { 'latlng': point, 'sensor': str(sensor).lower() } if language: params['language'] = language if not self.premier: url = self.get_url(params) else: url = self.get_signed_url(params) return self.geocode_url(url, exactly_one) def parse_json(self, page, exactly_one=True): '''Returns location, (latitude, longitude) from json feed.''' if not isinstance(page, basestring): page = util.decode_page(page) self.doc = json.loads(page) places = self.doc.get('results', []) if not places: check_status(self.doc.get('status')) return None elif exactly_one and len(places) != 1: raise ValueError( "Didn't find exactly one placemark! (Found %d)" % len(places)) def parse_place(place): '''Get the location, lat, lng from a single json place.''' location = place.get('formatted_address') latitude = place['geometry']['location']['lat'] longitude = place['geometry']['location']['lng'] return (location, (latitude, longitude)) if exactly_one: return parse_place(places[0]) else: return [parse_place(place) for place in places] def check_status(status): '''Validates error statuses.''' if status == 'ZERO_RESULTS': raise GQueryError( 'The geocode was successful but returned no results. This may' ' occur if the geocode was passed a non-existent address or a' ' latlng in a remote location.') elif status == 'OVER_QUERY_LIMIT': raise GTooManyQueriesError( 'The given key has gone over the requests limit in the 24' ' hour period or has submitted too many requests in too' ' short a period of time.') elif status == 'REQUEST_DENIED': raise GQueryError( 'Your request was denied, probably because of lack of a' ' sensor parameter.') elif status == 'INVALID_REQUEST': raise GQueryError('Probably missing address or latlng.') else: raise GeocoderResultError('Unkown error.') class GQueryError(GeocoderResultError): '''Generic Google query error.''' pass class GTooManyQueriesError(GeocoderResultError): '''Raised when the query rate limit is hit.''' pass geopy-0.95.1/geopy/geocoders/mapquest.py 0000644 € ¢Þ:€ +8~00000005135 12123100537 024145 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 try: import json except ImportError: try: import simplejson as json except ImportError: from django.utils import simplejson as json from urllib import urlencode from urllib2 import urlopen from geopy.geocoders.base import Geocoder from geopy.util import logger, decode_page, join_filter from geopy import util class MapQuest(Geocoder): def __init__(self, api_key='', format_string='%s'): """Initialize a MapQuest geocoder with address information and MapQuest API key. """ self.api_key = api_key self.format_string = format_string self.url = "http://www.mapquestapi.com/geocoding/v1/address" def geocode(self, location, exactly_one=True): if isinstance(location, unicode): location = location.encode('utf-8') params = {'location' : location} data = urlencode(params) page = urlopen(self.url + '?key=' + self.api_key + '&' + data).read() return self.parse_json(page, exactly_one) def parse_json(self, page, exactly_one=True): """Parse display name, latitude, and longitude from an JSON response.""" if not isinstance(page, basestring): page = decode_page(page) resources = json.loads(page) statuscode = resources.get('info').get('statuscode') if statuscode == 403: return "Bad API Key" resources = resources.get('results')[0].get('locations') if exactly_one and len(resources) != 1: from warnings import warn warn("Didn't find exactly one resource!" + \ "(Found %d.), use exactly_one=False\n" % len(resources) ) def parse_resource(resource): city = resource['adminArea5'] county = resource['adminArea4'] state = resource['adminArea3'] country = resource['adminArea1'] latLng = resource['latLng'] latitude, longitude = latLng.get('lat'), latLng.get('lng') location = join_filter(", ", [city, county, state,country]) if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return (location, (latitude, longitude)) if exactly_one: return parse_resource(resources[0]) else: return [parse_resource(resource) for resource in resources] if __name__ == "__main__": mq = MapQuest("Dmjtd%7Clu612007nq%2C20%3Do5-50zah") print mq.geocode('Mount St. Helens') mq = MapQuest("hDmjtd%7Clu612007nq%2C20%3Do5-50zah") print mq.geocode('Mount St. Helens') geopy-0.95.1/geopy/geocoders/openmapquest.py 0000644 € ¢Þ:€ +8~00000004652 12123100526 025030 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 try: import json except ImportError: try: import simplejson as json except ImportError: from django.utils import simplejson as json from urllib import urlencode from urllib2 import urlopen from geopy.geocoders.base import Geocoder from geopy.util import logger, decode_page, join_filter class OpenMapQuest(Geocoder): """Geocoder using the MapQuest Open Platform Web Services.""" def __init__(self, api_key='', format_string='%s'): """Initialize an Open MapQuest geocoder with location-specific address information, no API Key is needed by the Nominatim based platform. ``format_string`` is a string containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. """ self.api_key = api_key self.format_string = format_string self.url = "http://open.mapquestapi.com/nominatim/v1/search?format=json&%s" def geocode(self, string, exactly_one=True): if isinstance(string, unicode): string = string.encode('utf-8') params = {'q': self.format_string % string} url = self.url % urlencode(params) logger.debug("Fetching %s..." % url) page = urlopen(url) return self.parse_json(page, exactly_one) def parse_json(self, page, exactly_one=True): """Parse display name, latitude, and longitude from an JSON response.""" if not isinstance(page, basestring): page = decode_page(page) resources = json.loads(page) if exactly_one and len(resources) != 1: from warnings import warn warn("Didn't find exactly one resource!" + \ "(Found %d.), use exactly_one=False\n" % len(resources) ) def parse_resource(resource): location = resource['display_name'] latitude = resource['lat'] or None longitude = resource['lon'] or None if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return (location, (latitude, longitude)) if exactly_one: return parse_resource(resources[0]) else: return [parse_resource(resource) for resource in resources] geopy-0.95.1/geopy/geocoders/virtual_earth.py 0000644 € ¢Þ:€ +8~00000000271 12117720124 025156 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from warnings import warn warn('geopy.geocoders.virtual_earth: geocoders.virtual_earth is now geocoders.bing',DeprecationWarning) from geopy.geocoders.bing import Bing as VirtualEarth geopy-0.95.1/geopy/geocoders/wiki_gis.py 0000644 € ¢Þ:€ +8~00000004421 12123100512 024101 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from urllib import urlencode from urllib2 import urlopen import xml from xml.parsers.expat import ExpatError from geopy.geocoders.base import Geocoder,GeocoderError,GeocoderResultError from geopy import Point, Location, util class MediaWiki(Geocoder): def __init__(self, format_url, transform_string=None): """Initialize a geocoder that can parse MediaWiki pages with the GIS extension enabled. ``format_url`` is a URL string containing '%s' where the page name to request will be interpolated. For example: 'http://www.wiki.com/wiki/%s' ``transform_string`` is a callable that will make appropriate replacements to the input string before requesting the page. If None is given, the default transform_string which replaces ' ' with '_' will be used. It is recommended that you consider this argument keyword-only, since subclasses will likely place it last. """ self.format_url = format_url if callable(transform_string): self.transform_string = transform_string @classmethod def transform_string(cls, string): """Do the WikiMedia dance: replace spaces with underscores.""" return string.replace(' ', '_') def geocode(self, string): if isinstance(string, unicode): string = string.encode('utf-8') wiki_string = self.transform_string(string) url = self.format_url % wiki_string return self.geocode_url(url) def geocode_url(self, url): util.logger.debug("Fetching %s..." % url) page = urlopen(url) name, (latitude, longitude) = self.parse_xhtml(page) return (name, (latitude, longitude)) def parse_xhtml(self, page): soup = isinstance(page, BeautifulSoup) and page or BeautifulSoup(page) meta = soup.head.find('meta', {'name': 'geo.placename'}) name = meta and meta['content'] or None meta = soup.head.find('meta', {'name': 'geo.position'}) if meta: position = meta['content'] latitude, longitude = util.parse_geo(position) if latitude == 0 or longitude == 0: latitude = longitude = None else: latitude = longitude = None return (name, (latitude, longitude)) geopy-0.95.1/geopy/geocoders/wiki_semantic.py 0000644 € ¢Þ:€ +8~00000007570 12117720124 025144 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 import xml.dom.minidom from geopy.geocoders.base import Geocoder from geopy.point import Point from geopy.location import Location from geopy import util try: from BeautifulSoup import BeautifulSoup except ImportError: util.logger.warn("BeautifulSoup was not found. " \ "The SemanticMediaWiki geocoder will not work.") try: set except NameError: from sets import Set as set class SemanticMediaWiki(Geocoder): def __init__(self, format_url, attributes=None, relations=None, prefer_semantic=False, transform_string=None): self.format_url = format_url self.attributes = attributes self.relations = relations self.prefer_semantic = prefer_semantic self.transform_string = transform_string def get_url(self, string): return self.format_url % self.transform_string(string) def parse_rdf_link(self, page, mime_type='application/rdf+xml'): """Parse the URL of the RDF link from the
of ``page``.""" soup = BeautifulSoup(page) link = soup.head.find('link', rel='alternate', type=mime_type) return link and link['href'] or None def parse_rdf_things(self, data): dom = xml.dom.minidom.parseString(data) thing_map = {} things = dom.getElementsByTagName('smw:Thing') things.reverse() for thing in things: name = thing.attributes['rdf:about'].value articles = thing.getElementsByTagName('smw:hasArticle') things[name] = articles[0].attributes['rdf:resource'].value return (things, thing) def transform_semantic(self, string): """Normalize semantic attribute and relation names by replacing spaces with underscores and capitalizing the result.""" return string.replace(' ', '_').capitalize() def get_relations(self, thing, relations=None): if relations is None: relations = self.relations for relation in relations: relation = self.transform_semantic(relation) for node in thing.getElementsByTagName('relation:' + relation): resource = node.attributes['rdf:resource'].value yield (relation, resource) def get_attributes(self, thing, attributes=None): if attributes is None: attributes = self.attributes for attribute in attributes: attribute = self.transform_semantic(attribute) for node in thing.getElementsByTagName('attribute:' + attribute): value = node.firstChild.nodeValue.strip() yield (attribute, value) def get_thing_label(self, thing): return util.get_first_text(thing, 'rdfs:label') def geocode_url(self, url, attempted=None): if attempted is None: attempted = set() util.logger.debug("Fetching %s..." % url) page = urlopen(url) soup = BeautifulSoup(page) rdf_url = self.parse_rdf_link(soup) util.logger.debug("Fetching %s..." % rdf_url) page = urlopen(rdf_url) things, thing = self.parse_rdf(page) name = self.get_label(thing) attributes = self.get_attributes(thing) for attribute, value in attributes: latitude, longitude = util.parse_geo(value) if None not in (latitude, longitude): break if None in (latitude, longitude): relations = self.get_relations(thing) for relation, resource in relations: url = things.get(resource, resource) if url in tried: # Avoid cyclic relationships. continue tried.add(url) name, (latitude, longitude) = self.geocode_url(url, tried) if None not in (name, latitude, longitude): break return (name, (latitude, longitude)) geopy-0.95.1/geopy/geocoders/yahoo.py 0000644 € ¢Þ:€ +8~00000006077 12123100524 023427 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 """ Wrapper to the Yahoo's new PlaceFinder API. (doc says that the API RELEASE 1.0 (22 JUNE 2010)) """ import xml.dom.minidom from geopy import util from geopy import Point from urllib import urlencode from urllib2 import urlopen from geopy.geocoders.base import Geocoder try: import json except ImportError: try: import simplejson as json except ImportError: from django.utils import simplejson as json class Yahoo(Geocoder): BASE_URL = "http://where.yahooapis.com/geocode?%s" def __init__(self, app_id, format_string='%s', output_format=None): self.app_id = app_id self.format_string = format_string if output_format != None: from warnings import warn warn('geopy.geocoders.yahoo.Yahoo: The `output_format` parameter is deprecated '+ 'and now ignored. JSON will be used internally.', DeprecationWarning) def geocode(self, string, exactly_one=True): if isinstance(string, unicode): string = string.encode('utf-8') params = {'location': self.format_string % string, 'appid': self.app_id, 'flags': 'J' } url = self.BASE_URL % urlencode(params) util.logger.debug("Fetching %s..." % url) return self.geocode_url(url, exactly_one) def geocode_url(self, url, exactly_one=True): page = urlopen(url) return self.parse_json(page, exactly_one) def parse_json(self, page, exactly_one=True): if not isinstance(page, basestring): page = util.decode_page(page) doc = json.loads(page) results = doc.get('ResultSet', []).get('Results', []) if not results: raise ValueError("No results found") elif exactly_one and len(results) != 1: raise ValueError("Didn't find exactly one placemark! " \ "(Found %d.)" % len(results)) def parse_result(place): line1, line2, line3, line4 = place.get('line1'), place.get('line2'), place.get('line3'), place.get('line4') address = util.join_filter(", ", [line1, line2, line3, line4]) city = place.get('city') state = place.get('state') country = place.get('country') location = util.join_filter(", ", [address, city, country]) lat, lng = place.get('latitude'), place.get('longitude') #if lat and lng: # point = Point(floatlat, lng) #else: # point = None return (location, (float(lat), float(lng))) if exactly_one: return parse_result(results[0]) else: return [parse_result(result) for result in results] def reverse(self, coord, exactly_one=True): (lat, lng) = coord params = {'location': '%s,%s' % (lat, lng), 'gflags' : 'R', 'appid': self.app_id, 'flags': 'J' } url = self.BASE_URL % urlencode(params) return self.geocode_url(url, exactly_one) geopy-0.95.1/geopy/geohash.py 0000644 € ¢Þ:€ +8~00000004735 12117720124 021762 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from geopy import Point class Geohash(object): ENCODE_MAP = '0123456789bcdefghjkmnpqrstuvwxyz' DECODE_MAP = dict([(char, i) for i, char in enumerate(ENCODE_MAP)]) def __init__(self, point_class=Point, precision=12): self.point_class = point_class self.precision = precision def encode(self, *args, **kwargs): precision = kwargs.pop('precision', self.precision) point = Point(*args, **kwargs) lat_min, latitude, lat_max = -90, 0, 90 long_min, longitude, long_max = -180, 0, 180 string = '' bytes = [] odd_bit = False for i in xrange(precision): byte = 0 for bit in (16, 8, 4, 2, 1): if odd_bit: if point.latitude >= latitude: byte |= bit lat_min = latitude else: lat_max = latitude latitude = (lat_min + lat_max) / 2. else: if point.longitude >= longitude: byte |= bit long_min = longitude else: long_max = longitude longitude = (long_min + long_max) / 2. odd_bit = not odd_bit bytes.append(byte) return ''.join([self.ENCODE_MAP[byte] for byte in bytes]) def decode(self, string): lat_min, latitude, lat_max = -90, 0, 90 long_min, longitude, long_max = -180, 0, 180 odd_bit = False for char in string: try: byte = self.DECODE_MAP[char] except KeyError: raise ValueError("Invalid hash: unexpected character %r." % (c,)) else: for bit in (16, 8, 4, 2, 1): if odd_bit: if byte & bit: lat_min = latitude else: lat_max = latitude latitude = (lat_min + lat_max) / 2. else: if byte & bit: long_min = longitude else: long_max = longitude longitude = (long_min + long_max) / 2. odd_bit = not odd_bit point = self.point_class((latitude, longitude)) point.error = (lat_max - latitude, long_max - longitude) return point geopy-0.95.1/geopy/location.py 0000644 € ¢Þ:€ +8~00000001522 12117720124 022143 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from geopy.point import Point class Location(object): def __init__(self, name="", point=None, attributes=None, **kwargs): self.name = name if point is not None: self.point = Point(point) if attributes is None: attributes = {} self.attributes = dict(attributes, **kwargs) def __getitem__(self, index): """Backwards compatibility with geopy 0.93 tuples.""" return (self.name, self.point)[index] def __repr__(self): return "Location(%r, %r)" % (self.name, self.point) def __iter__(self): return iter((self.name, self.point)) def __eq__(self, other): return (self.name, self.point) == (other.name, other.point) def __ne__(self, other): return (self.name, self.point) != (other.name, other.point) geopy-0.95.1/geopy/parsers/ 0000755 € ¢Þ:€ +8~00000000000 12123101075 021433 5 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 geopy-0.95.1/geopy/parsers/__init__.py 0000644 € ¢Þ:€ +8~00000000172 12117720124 023551 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from warnings import warn warn('geopy.parsers is deprecated.', DeprecationWarning) from geopy.parsers.base import Parser geopy-0.95.1/geopy/parsers/base.py 0000644 € ¢Þ:€ +8~00000000432 12117720124 022723 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 class Parser(object): def find(self, document): raise NotImplementedError def find_first(self, document): for location in self.find_iter(document): return location def find_all(self, document): return list(self.find(document)) geopy-0.95.1/geopy/parsers/gpx.py 0000644 € ¢Þ:€ +8~00000030537 12117720124 022620 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 from geopy import Point from geopy.parsers.iso8601 import parse_iso8601 import sys, re from xml.etree import ElementTree class VersionError(Exception): pass class Waypoint(Point): ''' A `Waypoint` is a geopy `Point` with additional waypoint metadata as defined by the GPX format specification. ''' @classmethod def from_xml_names(cls, attrs, children): ''' Construct a new Waypoint from dictionaries of attribute and child element names corresponding to GPX waypoint information, as parsed by the `GPX` class. ''' lat = attrs['lat'] lon = attrs['lon'] if 'ele' in children: ele = children['ele'] else: ele = None w = cls(lat, lon, ele) if 'time' in children: w.timestamp = children['time'] if 'name' in children: w.name = children['name'] if 'desc' in children: w.description = children['desc'] if 'cmt' in children: w.comment = children['cmt'] if 'src' in children: w.source = children['src'] if 'sym' in children: w.symbol = children['sym'] if 'type' in children: w.classification = children['type'] if 'fix' in children: w.fix = children['fix'] if 'sat' in children: w.num_satellites = children['sat'] if 'ageofdgpsdata' in children: w.age = children['ageofdgpsdata'] if 'dgpsid' in children: w.dgpsid = children['dgpsid'] return w class _Attr(object): ''' Value wrapper for allowing interfaces to access attribute values with `obj.text` ''' def __init__(self, value): self.text = value class GPX(object): GPX_NS = "http://www.topografix.com/GPX/1/1" FILE_EXT = '.gpx' MIME_TYPE = 'application/gpx+xml' VERSION = '1.1' FIX_TYPES = set(('none', '2d', '3d', 'dgps', 'pps')) DECIMAL_RE = re.compile(r'([+-]?\d*\.?\d+)$') # Each "type tuple" is a tuple of two items: # 1. Dictionary of attributes in the type # 2. Dictionary of child elements that can appear in the type GPX_TYPE = ({'version': 'string', 'creator': 'string'}, { 'metadata': 'metadata', 'wpt': ['waypoint'], 'rte': ['route'], 'trk': ['track'], 'extensions': 'extensions' }) METADATA_TYPE = ({}, { 'name': 'string', 'desc': 'string', 'author': 'person', 'copyright': 'copyright', 'link': ['link'], 'time': 'datetime', 'keywords': 'string', 'bounds': 'bounds', 'extensions': 'extensions' }) WAYPOINT_TYPE = ({'lat': 'decimal', 'lon': 'decimal'}, { 'ele': 'decimal', 'time': 'datetime', 'magvar': 'degrees', 'geoidheight': 'decimal', 'name': 'string', 'cmt': 'string', 'desc': 'string', 'src': 'string', 'link': ['link'], 'sym': 'string', 'type': 'string', 'fix': 'fix', 'sat': 'unsigned', 'hdop': 'decimal', 'vdop': 'decimal', 'pdop': 'decimal', 'ageofdgpsdata': 'decimal', 'dgpsid': 'dgpsid', 'extensions': 'extensions' }) ROUTE_TYPE = ({}, { 'name': 'string', 'cmt': 'string', 'desc': 'string', 'src': 'string', 'link': ['link'], 'number': 'unsigned', 'type': 'string', 'extensions': 'extensions', 'rtept': ['waypoint'] }) TRACK_TYPE = ({}, { 'name': 'string', 'cmt': 'string', 'desc': 'string', 'src': 'string', 'link': ['link'], 'number': 'unsigned', 'type': 'string', 'extensions': 'extensions', 'trkseg': ['segment'] }) TRACK_SEGMENT_TYPE = ({}, {'trkpt': ['waypoint'], 'extensions': 'extensions'} ) COPYRIGHT_TYPE = ( {'author': 'string'}, {'year': 'year', 'license': 'uri'} ) LINK_TYPE = ({'href': 'uri'}, {'text': 'string', 'type': 'string'}) EMAIL_TYPE = ({'id': 'string', 'domain': 'string'}, {}) PERSON_TYPE = ({}, {'name': 'string', 'email': 'email', 'link': 'link'}) POINT_TYPE = ({'lat': 'longitude', 'lon': 'longitude'}, {'ele': 'decimal', 'time': 'datetime'} ) POINT_SEGMENT_TYPE = ({}, {'pt': ['point']}) BOUNDS_TYPE = ({ 'minlat': 'latitude', 'minlon': 'longitude', 'maxlat': 'latitude', 'maxlon': 'longitude' }, {}) def __init__(self, document=None, cache=True): self.cache = cache self._waypoints = {} self._routes = {} self._tracks = {} self.type_handlers = { 'string': lambda e: e.text, 'uri': lambda e: e.text, 'datetime': self._parse_datetime_element, 'decimal': self._parse_decimal, 'dgpsid': self._parse_dgps_station, 'email': self._parse_email, 'link': self._parse_link, 'year': self._parse_int, 'waypoint': self._parse_waypoint, 'segment': self._parse_segment, 'unsigned': self._parse_unsigned, 'degrees': self._parse_degrees, 'fix': self._parse_fix, 'extensions': self._parse_noop, } if document is not None: self.open(document) def open(self, string_or_file): if isinstance(string_or_file, basestring): string_or_file = ElementTree.fromstring(string_or_file) elif not ElementTree.iselement(string_or_file): string_or_file = ElementTree.parse(string_or_file) if string_or_file.getroot().tag == self._get_qname('gpx'): self._root = string_or_file.getroot() @property def version(self): if not hasattr(self, '_version'): version = self._root.get('version') if version == self.VERSION: self._version = version else: raise VersionError("%r" % (version,)) return self._version @property def creator(self): if not hasattr(self, '_creator'): self._creator = self._root.get('creator') return self._creator @property def metadata(self): if not hasattr(self, '_metadata'): metadata_qname = self._get_qname('metadata') metadata = {} element = self._root.find(metadata_qname) if element is not None: single, multi = self.METADATA metadata.update(self._child_dict(element, single, multi)) for tag in ('name', 'desc', 'time', 'keywords'): if tag in metadata: metadata[tag] = metadata[tag] if 'time' in metadata: metadata['time'] = self._parse_datetime(metadata['time']) self._metadata = metadata return self._metadata @property def waypoints(self): tag = self._get_qname('wpt') return self._cache_parsed(tag, self._parse_waypoint, self._waypoints) def _parse_waypoint(self, element): waypoint = {} point = Point(element.get('lat'), element.get('lon')) def _parse_segment(self, element): pass @property def routes(self): tag = self._get_qname('rte') return self._cache_parsed(tag, self._parse_route, self._routes) def _parse_route(self, element): pass @property def route_names(self): for route in self._root.findall(self._get_qname('rte')): yield route.findtext(self._get_qname('name')) @property def waypoints(self): return self.get_waypoints() def get_waypoints(self, route=None): if route is None: root = self._root waypoint_name = self._get_qname('wpt') else: root = self.get_route_by_name(route) waypoint_name = self._get_qname('rtept') for rtept in root.findall(waypoint_name): attrs, children = self._parse_type(rtept, self.WAYPOINT_TYPE) yield Waypoint.from_xml_names(attrs, children) def get_route_by_name(self, route): if isinstance(route, basestring): name = route index = 0 else: name, index = route seen_index = 0 for rte in self._root.findall(self._get_qname('rte')): rname = rte.findtext(self._get_qname('name')) if rname == name: if not seen_index == index: seen_index = seen_index + 1 else: return rte return None @property def tracks(self): tag = self._get_qname('rte') return self._cache_parsed(tag, self._parse_track, self._tracks) def _parse_track(self, element): pass def _parse_type(self, element, type_def): attr_types, child_types = type_def attrs = {} children = {} for attr, handler in attr_types.iteritems(): value = element.get(attr) type_func = self.type_handlers[handler] attrs[attr] = type_func(_Attr(value)) for tag, handler in child_types.iteritems(): values = [] all = False if isinstance(handler, list): all = True type_func = self.type_handlers[handler[0]] else: type_func = self.type_handlers[handler] for e in element.findall(self._get_qname(tag)): values.append(type_func(e)) if len(values) > 0: if all: children[tag] = values else: children[tag] = values[-1] return attrs, children @property def extensions(self): extensions_qname = self._get_qname('extensions') def _cache_parsed(self, tag, parse_func, cache): i = -1 for i in xrange(len(cache)): item = cache[i] if item is not None: yield item for element in self._root: if element.tag == tag: i += 1 item = parse_func(element) if self.cache: cache[i] = item if item is not None: yield item def _parse_decimal(self, element): value = element.text match = re.match(self.DECIMAL_RE, value) if match: return float(match.group(1)) else: raise ValueError("Invalid decimal value: %r" % (value,)) def _parse_degrees(self, element): value = self._parse_decimal(element) if 0 <= value <= 360: return value else: raise ValueError("Value out of range [0, 360]: %r" % (value,)) def _parse_dgps_station(self, element): value = int(element.text) if 0 <= value <= 1023: return value else: raise ValueError("Value out of range [0, 1023]: %r" % (value,)) def _parse_datetime(self, value): return parse_iso8601(value) def _parse_datetime_element(self, element): return self._parse_datetime(element.text) def _parse_email(self, element): value = element.text if not value: name = element.get('id') domain = element.get('domain') if name and domain: return '@'.join((name, domain)) return value or None def _parse_link(self, element): pass def _parse_int(self, element): return int(element.text) def _parse_unsigned(self, element): return int(element.text) def _parse_fix(self, element): value = element.text if value in self.FIX_TYPES: return value else: raise ValueError("Value is not a valid fix type: %r" % (value,)) def _parse_string(self, element): return element.text def _parse_noop(self, element): return element def _child_dict(self, element, single, multi): single = dict([(self._get_qname(tag), tag) for tag in single]) multi = dict([(self._get_qname(tag), tag) for tag in multi]) limit = len(single) d = {} if limit or multi: for child in element: if child.tag in single: name = single.pop(child.tag) d[name] = child limit -= 1 elif child.tag in multi: name = multi[child.tag] d.setdefault(name, []).append(child) if not limit and not multi: break return d def _get_qname(self, name): return "{%s}%s" % (self.GPX_NS, name) geopy-0.95.1/geopy/parsers/html.py 0000644 € ¢Þ:€ +8~00000012761 12117720124 022765 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 import re from BeautifulSoup import BeautifulSoup, SoupStrainer from geopy import Point, Location from geopy.parsers import Parser from geopy.util import unescape FLOAT_RE = re.compile(r'([+-]?\d*\.?\d+)$') class ICBMMetaTag(Parser): META_NAME = 'ICBM' def __init__(self, ignore_invalid=True): self.ignore_invalid = ignore_invalid def find(self, document): strainer = SoupStrainer('meta', attrs={'name': self.META_NAME}) if not isinstance(document, BeautifulSoup): elements = BeautifulSoup(document, parseOnlyThese=strainer) else: elements = document.findAll(strainer) for element in elements: lat_long = element.get('content') if lat_long or not self.ignore_invalid: try: point = Point(unescape(lat_long)) except (TypeError, ValueError): if not self.ignore_invalid: raise else: yield Location(None, point) class GeoMetaTag(Parser): META_NAME = re.compile(r'geo\.(\w+)') def __init__(self, ignore_invalid=True): self.ignore_invalid = ignore_invalid def find(self, document): strainer = SoupStrainer('meta', attrs={'name': self.META_NAME}) if not isinstance(document, BeautifulSoup): elements = BeautifulSoup(document, parseOnlyThese=strainer) else: elements = document.findAll(strainer) attrs = {} for element in elements: meta_name = element['name'] attr_name = re.match(self.META_NAME, meta_name).group(1) value = element.get('content') if attr_name in attrs: location = self._get_location(attrs) if location is not None: yield location attrs.clear() attrs[attr_name] = value and unescape(value) location = self._get_location(attrs) if location is not None: yield location def _get_location(self, attrs): position = attrs.pop('position') name = attrs.pop('placename') if position is not None: if position or not self.ignore_invalid: try: point = Point(position) except (TypeError, ValueError): if not self.ignore_invalid: raise else: return Location(name, point, attrs) class GeoMicroformat(Parser): GEO_CLASS = re.compile(r'\s*geo\s*') LATITUDE_CLASS = re.compile(r'\s*latitude\s*') LONGITUDE_CLASS = re.compile(r'\s*longitude\s*') VALUE_CLASS = re.compile(r'\s*value\s*') SEP = re.compile(r'\s*;\s*') def __init__(self, ignore_invalid=True, shorthand=True, abbr_title=True, value_excerpting=True): self.ignore_invalid = ignore_invalid self.shorthand = shorthand self.abbr_title = abbr_title self.value_excerpting = value_excerpting def find(self, document): strainer = SoupStrainer(attrs={'class': self.GEO_CLASS}) if not isinstance(document, BeautifulSoup): elements = BeautifulSoup(document, parseOnlyThese=strainer) else: elements = document.findAll(strainer) for element in elements: preformatted = element.name == 'pre' lat_element = element.find(attrs={'class': self.LATITUDE_CLASS}) long_element = element.find(attrs={'class': self.LONGITUDE_CLASS}) latitude = None longitude = None if lat_element and long_element: latitude = self._get_value(lat_element, preformatted) longitude = self._get_value(long_element, preformatted) elif self.shorthand: lat_long = re.split(self.SEP, self._get_value(element), 1) if len(lat_long) == 2: latitude, longitude = lat_long if latitude and longitude: lat_match = FLOAT_RE.match(unescape(latitude)) long_match = FLOAT_RE.match(unescape(longitude)) if lat_match and long_match: latitude = float(lat_match.group(1)) longitude = float(long_match.group(1)) text = unescape(self._get_text(element).strip()) name = re.sub('\s+', ' ', text) yield Location(name, (latitude, longitude)) def _get_text(self, element, preformatted=False): if isinstance(element, basestring): if not preformatted: return re.sub('\s+', ' ', element) else: return element elif element.name == 'br': return '\n' else: pre = preformatted or element.name == 'pre' return "".join([self._get_text(node, pre) for node in element]) def _get_value(self, element, preformatted=False): if self.value_excerpting: value_nodes = element.findAll(attrs={'class': self.VALUE_CLASS}) if value_nodes: pre = preformatted or element.name == 'pre' values = [self._get_text(node, pre) for node in value_nodes] return "".join(values) if self.abbr_title and element.name == 'abbr': value = element.get('title') if value is not None: return value return self._get_text(element, preformatted) geopy-0.95.1/geopy/parsers/iso8601.py 0000644 € ¢Þ:€ +8~00000003663 12117720124 023133 0 ustar mtigas PROPUBLICA\Domain Users 0000000 0000000 import re from datetime import datetime, timedelta, tzinfo ISO8601_RE = re.compile(r""" (?P