diff --git a/CHANGES.txt b/CHANGES.txt index 6d7c116..92f6c35 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,8 +12,10 @@ v1.1.0, 12 Feb 2013 -- Replaced TwitterAPI with puttytat for Twitter requests. v1.1.1, 19 Feb 2013 -- Geocoder uses viewport instead of bounds. -v2.0.1, 14 Jun 2013 -- Switch to TwitterAPI and renamed to TwitterGeoPics +v2.0.1, 14 Jun 2013 -- Switch to TwitterAPI and renamed to TwitterGeoPics. -v2.0.2, 14 Jun 2013 -- Included pygeocoder source until fixed for python 3 +v2.0.2, 14 Jun 2013 -- Included pygeocoder source until fixed for python 3. v2.0.3, 25 Jun 2013 -- Fixed printing. + +v2.1.0, 17 Sep 2013 -- Requires TwitterAPI 3. diff --git a/README.md b/README.md index b060e6f..71c6601 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,12 @@ -# TwitterGeoPics # +[![Downloads](https://pypip.in/d/TwitterGeoPics/badge.png)](https://crate.io/packages/TwitterGeoPics) +[![Downloads](https://pypip.in/v/TwitterGeoPics/badge.png)](https://crate.io/packages/TwitterGeoPics) -_Scripts for geocoding tweets and for downloading images embedded in tweets._ +TwitterGeoPics +============== -### Contributors ### +Scripts for geocoding tweets and for downloading images embedded in tweets. -Jonas Geduldig \ No newline at end of file +Contributors +------------ + +* Jonas Geduldig \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b8d4624 --- /dev/null +++ b/README.txt @@ -0,0 +1 @@ +Scripts for geocoding tweets and for downloading images embedded in tweets. diff --git a/TwitterGeoPics/Geocoder.py b/TwitterGeoPics/Geocoder.py index 6b8c857..871ff79 100755 --- a/TwitterGeoPics/Geocoder.py +++ b/TwitterGeoPics/Geocoder.py @@ -10,10 +10,12 @@ import sys import time + SOCKET_TIMEOUT = 3 # seconds -- need to set a timeout or connection can hang indefinitely THROTTLE_INCR = .1 # seconds -- the time by which to dynamically increase between successive requests DEFAULT_CACHE_FILE = 'geocode.cache' + class Geocoder: """Wrapper for pygeocoder with Twitter helper methods and Google Maps throttling and caching. @@ -46,7 +48,7 @@ def __init__(self, cache_file=None): if cache_file is None: cache_file = DEFAULT_CACHE_FILE - # cache is a persistent dict with place address as key and lat/lng and count as value + # cache is a persistent dict (place address is key, lat/lng and count is value) self.cache = fridge.Fridge(cache_file) def _throttle(self): @@ -118,7 +120,7 @@ def latlng_to_address(self, lat, lng): try: self.count_request += 1 socket.setdefaulttimeout(SOCKET_TIMEOUT) - place = pygeocoder.Geocoder.latlng_to_address(lat, lng) + place = pygeocoder.Geocoder.reverse_geocode(lat, lng).formatted_address self.count_request_ok += 1 return place except pygeocoder.GeocoderError as e: @@ -132,7 +134,7 @@ def address_to_latlng(self, place): try: self.count_request += 1 socket.setdefaulttimeout(SOCKET_TIMEOUT) - lat, lng = pygeocoder.Geocoder.address_to_latlng(place) + lat, lng = pygeocoder.Geocoder.geocode(place).coordinates self.count_request_ok += 1 return lat, lng except pygeocoder.GeocoderError as e: @@ -157,7 +159,7 @@ def geocode_tweet(self, status): """ # start off with the location in the user's profile (it may be empty) place = status['user']['location'] - if status['coordinates'] is not None: + if status['coordinates']: # the status is geocoded (swapped lat/lng), so use the coordinates to get the address lng, lat = status['coordinates']['coordinates'] place = self.latlng_to_address(float(lat), float(lng)) @@ -171,19 +173,19 @@ def geocode_tweet(self, status): lat, lng = coord.strip().split(',', 1) elif ' ' in coord: lat, lng = coord.strip().split(' ', 1) - if lat is not None and lng is not None: + if lat and lng: try: lat, lng = lat.strip(), lng.strip() place = self.latlng_to_address(float(lat), float(lng)) self.count_has_location += 1 except ValueError or TypeError: pass - elif place is not None and place != '': + elif place and place != '': # there is a location in the user profile, so see if it is usable # cache key is the place stripped of all punctuation and lower case key = ' '.join(''.join(e for e in place if e.isalnum() or e == ' ').split()).lower() cached_data = None - if self.cache is not None and key in self.cache: + if self.cache and key in self.cache: # see if the place name is in our cache cached_data = self.cache[key] lat, lng = cached_data[0], cached_data[1] @@ -192,8 +194,7 @@ def geocode_tweet(self, status): # see if Google can interpret the location lat, lng = self.address_to_latlng(place) cached_data = ( lat, lng, 1 ) - if self.cache is not None: - self.cache[key] = cached_data + self.cache[key] = cached_data self.count_has_location += 1 else: lat, lng = None, None diff --git a/TwitterGeoPics/GetNewGeo.py b/TwitterGeoPics/GetNewGeo.py index 0cd275f..3681609 100644 --- a/TwitterGeoPics/GetNewGeo.py +++ b/TwitterGeoPics/GetNewGeo.py @@ -3,22 +3,11 @@ __license__ = "MIT" import argparse -import codecs -from Geocoder import Geocoder +from .Geocoder import Geocoder import sys from TwitterAPI import TwitterAPI, TwitterOAuth -try: - # python 3 - sys.stdout = codecs.getwriter('utf8')(sys.stdout.buffer) - sys.stderr = codecs.getwriter('utf8')(sys.stderr.buffer) -except: - # python 2 - sys.stdout = codecs.getwriter('utf8')(sys.stdout) - sys.stderr = codecs.getwriter('utf8')(sys.stderr) - - GEO = Geocoder() @@ -26,12 +15,12 @@ def parse_tweet(status, region): """Print tweet, location and geocode.""" try: geocode = GEO.geocode_tweet(status) - sys.stdout.write('\n%s: %s\n' % (status['user']['screen_name'], status['text'])) - sys.stdout.write('LOCATION: %s\n' % status['user']['location']) - sys.stdout.write('GEOCODE: %s\n' % geocode) + print('\n%s: %s' % (status['user']['screen_name'], status['text'])) + print('LOCATION: %s' % status['user']['location']) + print('GEOCODE: %s' % geocode) except Exception as e: if GEO.quota_exceeded: - sys.stderr.write('*** GEOCODER QUOTA EXCEEDED: %s\n' % GEO.count_request) + print('*** GEOCODER QUOTA EXCEEDED: %s\n' % GEO.count_request) raise @@ -43,20 +32,19 @@ def stream_tweets(api, list, region): params['track'] = words if region is not None: params['locations'] = '%f,%f,%f,%f' % region - sys.stdout.write('REGION %s\n' % region) + print('REGION %s' % str(region)) while True: try: - api.request('statuses/filter', params) - iter = api.get_iterator() + r = api.request('statuses/filter', params) while True: - for item in iter: + for item in r.get_iterator(): if 'text' in item: parse_tweet(item, region) elif 'disconnect' in item: raise Exception('Disconnect: %s' % item['disconnect'].get('reason')) except Exception as e: # reconnect on 401 errors and socket timeouts - sys.stderr.write('*** MUST RECONNECT %s\n' % e) + print('*** MUST RECONNECT %s\n' % e) if __name__ == '__main__': @@ -78,13 +66,13 @@ def stream_tweets(api, list, region): else: latC, lngC, latSW, lngSW, latNE, lngNE = GEO.get_region_box(args.location) region = (lngSW, latSW, lngNE, latNE) - sys.stdout.write('Google found region at %f,%f and %f,%f\n' % region) + print('Google found region at %f,%f and %f,%f' % region) else: region = None try: stream_tweets(api, args.words, region) except KeyboardInterrupt: - sys.stderr.write('\nTerminated by user\n') + print('\nTerminated by user\n') GEO.print_stats() \ No newline at end of file diff --git a/TwitterGeoPics/GetNewPics.py b/TwitterGeoPics/GetNewPics.py index a7a3f22..7a85048 100644 --- a/TwitterGeoPics/GetNewPics.py +++ b/TwitterGeoPics/GetNewPics.py @@ -3,24 +3,13 @@ __license__ = "MIT" import argparse -import codecs -from Geocoder import Geocoder +from .Geocoder import Geocoder import os import sys from TwitterAPI import TwitterAPI, TwitterOAuth import urllib -try: - # python 3 - sys.stdout = codecs.getwriter('utf8')(sys.stdout.buffer) - sys.stderr = codecs.getwriter('utf8')(sys.stderr.buffer) -except: - # python 2 - sys.stdout = codecs.getwriter('utf8')(sys.stdout) - sys.stderr = codecs.getwriter('utf8')(sys.stderr) - - GEO = Geocoder() @@ -32,21 +21,21 @@ def parse_tweet(status, photo_dir, stalk): if media['type'] == 'photo': photo_count += 1 if photo_count == 1: - sys.stdout.write('\n%s: %s\n' % (status['user']['screen_name'], status['text'])) + print('\n%s: %s' % (status['user']['screen_name'], status['text'])) if stalk and not GEO.quota_exceeded: try: geocode = GEO.geocode_tweet(status) - sys.stdout.write('LOCATION: %s\n' % status['user']['location']) - sys.stdout.write('GEOCODE: %s\n' % geocode) + print('LOCATION: %s' % status['user']['location']) + print('GEOCODE: %s' % geocode) except Exception as e: if GEO.quota_exceeded: - sys.stderr.write('*** GEOCODER QUOTA EXCEEDED: %s\n' % GEO.count_request) + print('*** GEOCODER QUOTA EXCEEDED: %s\n' % GEO.count_request) if photo_dir: photo_url = media['media_url_https'] screen_name = status['user']['screen_name'] file_name = os.path.join(photo_dir, screen_name) + '.' + photo_url.split('.')[-1] urllib.urlretrieve(photo_url, file_name) - sys.stdout.write(screen_name + '\n') + print(screen_name) def stream_tweets(api, list, photo_dir, region, stalk, no_retweets): @@ -57,13 +46,12 @@ def stream_tweets(api, list, photo_dir, region, stalk, no_retweets): params['track'] = words if region is not None: params['locations'] = '%f,%f,%f,%f' % region - sys.stdout.write('REGION %s\n' % region) + print('REGION %s' % str(region)) while True: try: - api.request('statuses/filter', params) - iter = api.get_iterator() + r = api.request('statuses/filter', params) while True: - for item in iter: + for item in r.get_iterator(): if 'text' in item: if not no_retweets or not item.has_key('retweeted_status'): parse_tweet(item, photo_dir, stalk) @@ -71,7 +59,7 @@ def stream_tweets(api, list, photo_dir, region, stalk, no_retweets): raise Exception('Disconnect: %s' % item['disconnect'].get('reason')) except Exception as e: # reconnect on 401 errors and socket timeouts - sys.stderr.write('*** MUST RECONNECT %s\n' % e) + print('*** MUST RECONNECT %s\n' % e) if __name__ == '__main__': @@ -96,13 +84,13 @@ def stream_tweets(api, list, photo_dir, region, stalk, no_retweets): else: latC, lngC, latSW, lngSW, latNE, lngNE = GEO.get_region_box(args.location) region = (lngSW, latSW, lngNE, latNE) - sys.stdout.write('Google found region at %f,%f and %f,%f\n' % region) + print('Google found region at %f,%f and %f,%f' % region) else: region = None try: stream_tweets(api, args.words, args.photo_dir, region, args.stalk, args.no_retweets) except KeyboardInterrupt: - sys.stderr.write('\nTerminated by user\n') + print('\nTerminated by user\n') GEO.print_stats() \ No newline at end of file diff --git a/TwitterGeoPics/GetOldGeo.py b/TwitterGeoPics/GetOldGeo.py index 4ee0619..0c8dd4e 100644 --- a/TwitterGeoPics/GetOldGeo.py +++ b/TwitterGeoPics/GetOldGeo.py @@ -3,22 +3,10 @@ __license__ = "MIT" import argparse -import codecs -from Geocoder import Geocoder -import sys +from .Geocoder import Geocoder from TwitterAPI import TwitterAPI, TwitterOAuth, TwitterRestPager -try: - # python 3 - sys.stdout = codecs.getwriter('utf8')(sys.stdout.buffer) - sys.stderr = codecs.getwriter('utf8')(sys.stderr.buffer) -except: - # python 2 - sys.stdout = codecs.getwriter('utf8')(sys.stdout) - sys.stderr = codecs.getwriter('utf8')(sys.stderr) - - GEO = Geocoder() @@ -26,9 +14,9 @@ def parse_tweet(status): """Print tweet, location and geocode.""" try: geocode = GEO.geocode_tweet(status) - sys.stdout.write('\n%s: %s\n' % (status['user']['screen_name'], status['text'])) - sys.stdout.write('LOCATION: %s\n' % status['user']['location']) - sys.stdout.write('GEOCODE: %s\n' % geocode) + print('\n%s: %s' % (status['user']['screen_name'], status['text'])) + print('LOCATION: %s' % status['user']['location']) + print('GEOCODE: %s' % geocode) except Exception as e: if GEO.quota_exceeded: raise @@ -41,15 +29,15 @@ def search_tweets(api, list, region): if region: params['geocode'] = '%f,%f,%fkm' % region # lat,lng,radius while True: - iter = TwitterRestPager(api, 'search/tweets', params).get_iterator() - for item in iter: + pager = TwitterRestPager(api, 'search/tweets', params) + for item in pager.get_iterator(): if 'text' in item: parse_tweet(item) elif 'message' in item: if item['code'] == 131: continue # ignore internal server error elif item['code'] == 88: - sys.stderr.write('Suspend search until %s\n' % search.get_quota()['reset']) + print('Suspend search until %s' % search.get_quota()['reset']) raise Exception('Message from twiter: %s' % item['message']) @@ -67,7 +55,7 @@ def search_tweets(api, list, region): try: if args.location: lat, lng, radius = GEO.get_region_circle(args.location) - sys.stdout.write('Google found region at %f,%f with a radius of %s km\n' % (lat, lng, radius)) + print('Google found region at %f,%f with a radius of %s km' % (lat, lng, radius)) if args.radius: radius = args.radius region = (lat, lng, radius) @@ -75,8 +63,8 @@ def search_tweets(api, list, region): region = None search_tweets(api, args.words, region) except KeyboardInterrupt: - sys.stdout.write('\nTerminated by user\n') + print('\nTerminated by user\n') except Exception as e: - sys.stdout.write('*** STOPPED %s\n' % e) + print('*** STOPPED %s\n' % e) GEO.print_stats() \ No newline at end of file diff --git a/TwitterGeoPics/GetOldPics.py b/TwitterGeoPics/GetOldPics.py index 2f9f9dd..406b8b8 100644 --- a/TwitterGeoPics/GetOldPics.py +++ b/TwitterGeoPics/GetOldPics.py @@ -3,24 +3,12 @@ __license__ = "MIT" import argparse -import codecs -from Geocoder import Geocoder +from .Geocoder import Geocoder import os -import sys from TwitterAPI import TwitterAPI, TwitterOAuth, TwitterRestPager import urllib -try: - # python 3 - sys.stdout = codecs.getwriter('utf8')(sys.stdout.buffer) - sys.stderr = codecs.getwriter('utf8')(sys.stderr.buffer) -except: - # python 2 - sys.stdout = codecs.getwriter('utf8')(sys.stdout) - sys.stderr = codecs.getwriter('utf8')(sys.stderr) - - GEO = Geocoder() @@ -32,21 +20,21 @@ def parse_tweet(status, photo_dir, stalk): if media['type'] == 'photo': photo_count += 1 if photo_count == 1: - sys.stdout.write('\n%s: %s\n' % (status['user']['screen_name'], status['text'])) + print('\n%s: %sprint' % (status['user']['screen_name'], status['text'])) if stalk and not GEO.quota_exceeded: try: geocode = GEO.geocode_tweet(status) - sys.stdout.write('LOCATION: %s\n' % status['user']['location']) - sys.stdout.write('GEOCODE: %s\n' % geocode) + print('LOCATION: %s' % status['user']['location']) + print('GEOCODE: %s' % geocode) except Exception as e: if GEO.quota_exceeded: - sys.stderr.write('GEOCODER QUOTA EXCEEDED: %s\n' % GEO.count_request) + print('GEOCODER QUOTA EXCEEDED: %s' % GEO.count_request) if photo_dir: photo_url = media['media_url_https'] screen_name = status['user']['screen_name'] file_name = os.path.join(photo_dir, screen_name) + '.' + photo_url.split('.')[-1] urllib.urlretrieve(photo_url, file_name) - sys.stdout.write(screen_name + '\n') + print(screen_name) def search_tweets(api, list, photo_dir, region, stalk, no_retweets): @@ -56,8 +44,8 @@ def search_tweets(api, list, photo_dir, region, stalk, no_retweets): if region: params['geocode'] = '%f,%f,%fkm' % region # lat,lng,radius while True: - iter = TwitterRestPager(api, 'search/tweets', params).get_iterator() - for item in iter: + pager = TwitterRestPager(api, 'search/tweets', params) + for item in pager.get_iterator(): if 'text' in item: if not no_retweets or not item.has_key('retweeted_status'): parse_tweet(item, photo_dir, stalk) @@ -65,7 +53,7 @@ def search_tweets(api, list, photo_dir, region, stalk, no_retweets): if item['code'] == 131: continue # ignore internal server error elif item['code'] == 88: - sys.stderr.write('Suspend search until %s\n' % search.get_quota()['reset']) + print('Suspend search until %s' % search.get_quota()['reset']) raise Exception('Message from twiter: %s' % item['message']) @@ -86,7 +74,7 @@ def search_tweets(api, list, photo_dir, region, stalk, no_retweets): try: if args.location: lat, lng, radius = GEO.get_region_circle(args.location) - sys.stdout.write('Google found region at %f,%f with a radius of %s km\n' % (lat, lng, radius)) + print('Google found region at %f,%f with a radius of %s km' % (lat, lng, radius)) if args.radius: radius = args.radius region = (lat, lng, radius) @@ -94,8 +82,8 @@ def search_tweets(api, list, photo_dir, region, stalk, no_retweets): region = None search_tweets(api, args.words, args.photo_dir, region, args.stalk, args.no_retweets) except KeyboardInterrupt: - sys.stderr.write('\nTerminated by user\n') + print('\nTerminated by user\n') except Exception as e: - sys.stderr.write('*** STOPPED %s\n' % e) + print('*** STOPPED %s\n' % e) GEO.print_stats() \ No newline at end of file diff --git a/TwitterGeoPics/__init__.py b/TwitterGeoPics/__init__.py index 7df2784..b44488d 100644 --- a/TwitterGeoPics/__init__.py +++ b/TwitterGeoPics/__init__.py @@ -1,5 +1,5 @@ __title__ = 'TwitterGeoPics' -__version__ = '2.0.3' +__version__ = '2.1.0' __author__ = 'Jonas Geduldig' __license__ = 'MIT' __copyright__ = 'Copyright 2013 Jonas Geduldig' diff --git a/TwitterGeoPics/pygeocoder.py b/TwitterGeoPics/pygeocoder.py deleted file mode 100644 index dd4798c..0000000 --- a/TwitterGeoPics/pygeocoder.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env python -# -# Xiao Yu - Montreal - 2010 -# Based on googlemaps by John Kleint -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -""" -Python wrapper for Google Geocoding API V3. - -* **Geocoding**: convert a postal address to latitude and longitude -* **Reverse Geocoding**: find the nearest address to coordinates - -""" - -import requests -import functools -import base64 -import hmac -import hashlib -from pygeolib import GeocoderError, GeocoderResult -#from __version__ import VERSION - -try: - import json -except ImportError: - import simplejson as json - -__all__ = ['Geocoder', 'GeocoderError', 'GeocoderResult'] - - -# this decorator lets me use methods as both static and instance methods -class omnimethod(object): - def __init__(self, func): - self.func = func - - def __get__(self, instance, owner): - return functools.partial(self.func, instance) - - -class Geocoder(object): - """ - A Python wrapper for Google Geocoding V3's API - - """ - - GEOCODE_QUERY_URL = 'https://maps.google.com/maps/api/geocode/json?' - - def __init__(self, client_id=None, private_key=None): - """ - Create a new :class:`Geocoder` object using the given `client_id` and - `referrer_url`. - - :param client_id: Google Maps Premier API key - :type client_id: string - - Google Maps API Premier users can provide his key to make 100,000 requests - a day vs the standard 2,500 requests a day without a key - - """ - self.client_id = client_id - self.private_key = private_key - - @omnimethod - def get_data(self, params={}): - """ - Retrieve a JSON object from a (parameterized) URL. - - :param params: Dictionary mapping (string) query parameters to values - :type params: dict - :return: JSON object with the data fetched from that URL as a JSON-format object. - :rtype: (dict or array) - - """ - request = requests.Request('GET', - url = Geocoder.GEOCODE_QUERY_URL, - params = params, - headers = { - 'User-Agent': 'pygeocoder/' + VERSION + ' (Python)' - }) - - if self and self.client_id and self.private_key: - self.add_signature(request) - - response = requests.Session().send(request.prepare()) - if response.status_code == 403: - raise GeocoderError("Forbidden, 403", response.url) - response_json = response.json() - - if response_json['status'] != GeocoderError.G_GEO_OK: - raise GeocoderError(response_json['status'], response.url) - return response_json['results'] - - @omnimethod - def add_signature(self, request): - decoded_key = base64.urlsafe_b64decode(str(self.private_key)) - signature = hmac.new(decoded_key, request.url, hashlib.sha1) - encoded_signature = base64.urlsafe_b64encode(signature.digest()) - request.params['client'] = str(self.client_id) - request.params['signature'] = encoded_signature - - @omnimethod - def geocode(self, address, sensor='false', bounds='', region='', language=''): - """ - Given a string address, return a dictionary of information about - that location, including its latitude and longitude. - - :param address: Address of location to be geocoded. - :type address: string - :param sensor: ``'true'`` if the address is coming from, say, a GPS device. - :type sensor: string - :param bounds: The bounding box of the viewport within which to bias geocode results more prominently. - :type bounds: string - :param region: The region code, specified as a ccTLD ("top-level domain") two-character value for biasing - :type region: string - :param language: The language in which to return results. - :type language: string - :returns: `geocoder return value`_ dictionary - :rtype: dict - :raises GeocoderError: if there is something wrong with the query. - - For details on the input parameters, visit - http://code.google.com/apis/maps/documentation/geocoding/#GeocodingRequests - - For details on the output, visit - http://code.google.com/apis/maps/documentation/geocoding/#GeocodingResponses - - """ - - params = { - 'address': address, - 'sensor': sensor, - 'bounds': bounds, - 'region': region, - 'language': language, - } - if self is not None: - return GeocoderResult(self.get_data(params=params)) - else: - return GeocoderResult(Geocoder.get_data(params=params)) - - @omnimethod - def reverse_geocode(self, lat, lng, sensor='false', bounds='', region='', language=''): - """ - Converts a (latitude, longitude) pair to an address. - - :param lat: latitude - :type lat: float - :param lng: longitude - :type lng: float - :return: `Reverse geocoder return value`_ dictionary giving closest - address(es) to `(lat, lng)` - :rtype: dict - :raises GeocoderError: If the coordinates could not be reverse geocoded. - - Keyword arguments and return value are identical to those of :meth:`geocode()`. - - For details on the input parameters, visit - http://code.google.com/apis/maps/documentation/geocoding/#GeocodingRequests - - For details on the output, visit - http://code.google.com/apis/maps/documentation/geocoding/#ReverseGeocoding - - """ - params = { - 'latlng': "%f,%f" % (lat, lng), - 'sensor': sensor, - 'bounds': bounds, - 'region': region, - 'language': language, - } - - if self is not None: - return GeocoderResult(self.get_data(params=params)) - else: - return GeocoderResult(Geocoder.get_data(params=params)) - -if __name__ == "__main__": - import sys - from optparse import OptionParser - - def main(): - """ - Geocodes a location given on the command line. - - Usage: - pygeocoder.py "1600 amphitheatre mountain view ca" [YOUR_API_KEY] - pygeocoder.py 37.4219720,-122.0841430 [YOUR_API_KEY] - - When providing a latitude and longitude on the command line, ensure - they are separated by a comma and no space. - - """ - usage = "usage: %prog [options] address" - parser = OptionParser(usage, version=VERSION) - parser.add_option("-k", "--key", dest="key", help="Your Google Maps API key") - (options, args) = parser.parse_args() - - if len(args) != 1: - parser.print_usage() - sys.exit(1) - - query = args[0] - gcoder = Geocoder(options.key) - - try: - result = gcoder.geocode(query) - except GeocoderError as err: - sys.stderr.write('%s\n%s\nResponse:\n' % (err.url, err)) - json.dump(err.response, sys.stderr, indent=4) - sys.exit(1) - - print(result) - print(result.coordinates) - main() diff --git a/TwitterGeoPics/pygeolib.py b/TwitterGeoPics/pygeolib.py deleted file mode 100644 index 540c7e7..0000000 --- a/TwitterGeoPics/pygeolib.py +++ /dev/null @@ -1,174 +0,0 @@ -import sys -import collections - -class GeocoderResult(collections.Iterator): - """ - A geocoder resultset to iterate through address results. - Exemple: - - results = Geocoder.geocode('paris, us') - for result in results: - print(result.formatted_address, result.location) - - Provide shortcut to ease field retrieval, looking at 'types' in each - 'address_components'. - Example: - result.country - result.postal_code - - You can also choose a different property to display for each lookup type. - Example: - result.country__short_name - - By default, use 'long_name' property of lookup type, so: - result.country - and: - result.country__long_name - are equivalent. - """ - - attribute_mapping = { - "state": "administrative_area_level_1", - "province": "administrative_area_level_1", - "city": "locality", - "county": "administrative_area_level_2", - } - - def __init__(self, data): - """ - Creates instance of GeocoderResult from the provided JSON data array - """ - self.data = data - self.len = len(self.data) - self.current_index = 0 - self.current_data = self.data[0] - - def __len__(self): - return self.len - - def __iter__(self): - return self - - def return_next(self): - if self.current_index >= self.len: - raise StopIteration - self.current_data = self.data[self.current_index] - self.current_index += 1 - return self - - def __getitem__(self, key): - """ - Accessing GeocoderResult by index will return a GeocoderResult - with just one data entry - """ - return GeocoderResult([self.data[key]]) - - def __unicode__(self): - return self.formatted_address - - if sys.version_info[0] >= 3: # Python 3 - def __str__(self): - return self.__unicode__() - - def __next__(self): - return self.return_next() - else: # Python 2 - def __str__(self): - return self.__unicode__().encode('utf8') - - def next(self): - return self.return_next() - - @property - def count(self): - return self.len - - @property - def coordinates(self): - """ - Return a (latitude, longitude) coordinate pair of the current result - """ - location = self.current_data['geometry']['location'] - return location['lat'], location['lng'] - - @property - def latitude(self): - return self.coordinates[0] - - @property - def longitude(self): - return self.coordinates[1] - - @property - def raw(self): - """ - Returns the full result set in dictionary format - """ - return self.data - - @property - def valid_address(self): - """ - Returns true if queried address is valid street address - """ - return self.current_data['types'] == [u'street_address'] - - @property - def formatted_address(self): - return self.current_data['formatted_address'] - - def __getattr__(self, name): - lookup = name.split('__') - attribute = lookup[0] - - if (attribute in GeocoderResult.attribute_mapping): - attribute = GeocoderResult.attribute_mapping[attribute] - - try: - prop = lookup[1] - except IndexError: - prop = 'long_name' - - for elem in self.current_data['address_components']: - if attribute in elem['types']: - return elem[prop] - - -class GeocoderError(Exception): - """Base class for errors in the :mod:`pygeocoder` module. - - Methods of the :class:`Geocoder` raise this when something goes wrong. - - """ - #: See http://code.google.com/apis/maps/documentation/geocoding/index.html#StatusCodes - #: for information on the meaning of these status codes. - G_GEO_OK = "OK" - G_GEO_ZERO_RESULTS = "ZERO_RESULTS" - G_GEO_OVER_QUERY_LIMIT = "OVER_QUERY_LIMIT" - G_GEO_REQUEST_DENIED = "REQUEST_DENIED" - G_GEO_MISSING_QUERY = "INVALID_REQUEST" - - def __init__(self, status, url=None, response=None): - """Create an exception with a status and optional full response. - - :param status: Either a ``G_GEO_`` code or a string explaining the - exception. - :type status: int or string - :param url: The query URL that resulted in the error, if any. - :type url: string - :param response: The actual response returned from Google, if any. - :type response: dict - - """ - Exception.__init__(self, status) # Exception is an old-school class - self.status = status - self.url = url - self.response = response - - def __str__(self): - """Return a string representation of this :exc:`GeocoderError`.""" - return 'Error %s\nQuery: %s' % (self.status, self.url) - - def __unicode__(self): - """Return a unicode representation of this :exc:`GeocoderError`.""" - return unicode(self.__str__()) diff --git a/setup.py b/setup.py index 3f20ac4..4024296 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,19 @@ from distutils.core import setup +import TwitterGeoPics +import io + +def read(*filenames, **kwargs): + encoding = kwargs.get('encoding', 'utf-8') + sep = kwargs.get('sep', '\n') + buf = [] + for filename in filenames: + with io.open(filename, encoding=encoding) as f: + buf.append(f.read()) + return sep.join(buf) setup( name='TwitterGeoPics', - version='2.0.3', + version=TwitterGeoPics.__version__, author='Jonas Geduldig', author_email='boxnumber03@gmail.com', packages=['TwitterGeoPics'], @@ -12,5 +23,6 @@ license='MIT', keywords='twitter, geocode', description='Command line scripts for geocoding tweets from twitter.com and for downloading embedded photos.', - install_requires = ['TwitterAPI', 'fridge'] -) + long_description=read('README.txt'), + install_requires = ['TwitterAPI>=2.1', 'fridge'] +