├── .gitignore ├── LICENSE ├── README.md ├── docs └── api.md ├── requirements.txt └── src ├── geolocate.py ├── mapquest.py ├── twitter.py └── what_three_words.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | #Credentials 132 | src/mapkey.txt 133 | src/wordkey.txt 134 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 David J. Kowalk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://www.buymeacoffee.com/davidkowalk) 2 | 3 | # Twitter Geolocate 4 | Twitter Geolocation is a console app that generates twitter search querries for a certain geolocation and opens them in your standard webbrowser. Provide either GPS Coordinates, a What3Words-address (requires a free account) or use the mapquest API to search for a location (requires a free account). 5 | 6 | ## Usage 7 | 8 | As a python script this does not need to be installed but to run it you will require python 3.7 or higher and the following packages: 9 | 10 | - requests 11 | - json* 12 | - webbrowser* 13 | 14 | \* = should be provided with python3 15 | 16 | ## Search by coordinates 17 | 18 | ``` 19 | $ python3 geolocate.py -c 20 | ``` 21 | 22 | Example: 23 | ``` 24 | $ python3 geolocate.py -c 52.5186202 13.3761872 0.2km 25 | ``` 26 | 27 | ## Search by What3Words address. 28 | 29 | To use the "What3Words" api you will ned an api key provided with a free account which will grant you 1000 requests per month. 30 | 31 | You can read more here: https://developer.what3words.com/public-api 32 | 33 | Once you have an api key you can use it to search to convert a querry to coordinates. 34 | 35 | ``` 36 | $ python3 geolocate.py -w 37 | ``` 38 | 39 | The three-word-address will automatically be converted to the corresponding coordinates on the southwest corner of the square. 40 | 41 | If no key is provided in the console the script will look for a file `wordkey.txt` in the current folder and read the first line of that file as a key. If no file is found it will throw an error. 42 | 43 | ``` 44 | $ python3 geolocate.py -w 45 | ``` 46 | 47 | Example: 48 | ``` 49 | $ python3 geolocate.py -w myKey cover.crumble.hint 0.3km 50 | $ python3 geolocate.py -w cover.crumble.hint 0.3km 51 | ``` 52 | 53 | ## Search addresses with mapquest 54 | 55 | To use the mapquest api you will need an api key provided with a free account which will grant you 15000 requests per month (which are about 20 per hour if you use the service 24 hours per day) 56 | 57 | You can read more here: https://developer.mapquest.com/documentation/ 58 | 59 | Once you have an api key you can use it to search for places. 60 | 61 | ``` 62 | $ python3 gelocate.py -q 63 | ``` 64 | 65 | The program will then ask you to select one of the top 5 search results. If only one result is found it will be automatically selected. 66 | 67 | Example: 68 | ``` 69 | $ python3 geolocate.py -q myKey "Platz der Republik 1, 11011 Berlin" 0.3km 70 | Search Results: 71 | 72 | (1) Käfer Dachgarten-Restaurant im Deutschen Bundestag, 1, Platz der Republik, Tiergarten, Mitte, Berlin, 11011, Deutschland 73 | (2) Deutscher Bundestag, 1, Platz der Republik, Tiergarten, Mitte, Berlin, 11011, Deutschland 74 | 75 | Please select an option or type "exit" to exit 76 | > 77 | 78 | ``` 79 | 80 | If no key is provided in the console the script will look for a file `mapkey.txt` in the same folder and read the first line of that file as a key. If no file is found it will throw an error. 81 | 82 | ``` 83 | $ python3 geolocate.py -q 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | This document explains the separate modules of the application. 4 | 5 | ## geolocate 6 | 7 | The `geolocate.py` functions as the main application file. In computes the console arguments and contains the `main()` function. 8 | 9 | ## mapquest 10 | 11 | The `mapquest.py` file interfaces with the mapquest api. It downloads and processes the map data. 12 | 13 | ### get_json() 14 | 15 | **Arguments:** string key, string querry 16 | 17 | **Output:** dict | string result, int status_code 18 | 19 | This will querry the mapquest-api with a location (querry) and return the entire json as a dictionary if successfull, or as a string if not successfull along with the http-status-code. 20 | 21 | **Example:** 22 | ```python 23 | result, status = mapquest.get_json(myKey, querry) 24 | ``` 25 | 26 | ### choose_option() 27 | 28 | **Arguments:** dict full_json 29 | 30 | **Output:** float latitude, float longitude 31 | 32 | Takes the dictionary from the mapquest-api and generates a user interface to select the desired location. If only one option is available it will be autoselected. 33 | The function then extracts the coordinates from the selected location. 34 | 35 | **Example:** 36 | ```python 37 | result, status = mapquest.get_json(myKey, querry) 38 | if status == 200: 39 | lat, long = mapquest.choose_option(result) 40 | ``` 41 | 42 | ## what_three_words 43 | 44 | The "what_three_words.py" is a wrapper for the "What3Words" api. When provided an api key and a three-word-address the api can produce latitude and longitude coordinates. 45 | 46 | ### get_json() 47 | 48 | **Arguments:** string key, string querry 49 | 50 | **Output:** dict | string result, int status_code 51 | 52 | This will querry the "What3Words" api to convert a three-word-address to latitude and longitude coordinates and returns the entire json response as a dictionary if successfull, or a string ig not, along with the http status-code. 53 | 54 | **Example:** 55 | ```python 56 | result, status = what_three_words.get_json(myKey, querry) 57 | ``` 58 | 59 | ## twitter 60 | 61 | The `twitter.py` generates the twitter search querry and opens the standard browser. 62 | 63 | ### get_url() 64 | 65 | **Arguments:** latitude, longitude, radius 66 | 67 | **Output:** string url 68 | 69 | Formats the twitter search-url with latitude, longitude and radius 70 | 71 | **Example:** 72 | ```python 73 | lat, long = 52.5186202, 13.3761872 74 | radius = "0.2km" 75 | 76 | url = twitter.get_url(lat, long, radius) 77 | ``` 78 | 79 | ### open_standard_browser() 80 | 81 | **Arguments:** string url 82 | 83 | Opens the standard webbrowser of your operating system. 84 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | json 3 | webbrowser 4 | -------------------------------------------------------------------------------- /src/geolocate.py: -------------------------------------------------------------------------------- 1 | from sys import argv as args 2 | import twitter 3 | 4 | help_str = """ 5 | Twitter Geolocate let's you search twitter for tweets in a certain area. 6 | 7 | <> Required 8 | [] Optional 9 | 10 | To search for coordinates: 11 | $ python3 -c 12 | 13 | To search for a location name using mapquest-api: 14 | $ python3 -q 15 | 16 | To search using a 3-word address: 17 | $ python3 -w 18 | """ 19 | 20 | def main(): 21 | 22 | if len(args) == 5 and args[1] == "-c": 23 | lat = args[2] 24 | lon = args[3] 25 | rad = args[4] 26 | 27 | url = twitter.get_url(lat, lon, rad) 28 | twitter.open_standard_browser(url) 29 | 30 | 31 | elif 4 <= len(args) <= 5 and args[1] == "-q": 32 | import mapquest 33 | 34 | i = 2 35 | 36 | if len(args) == 5: 37 | key = args[i] 38 | i += 1 39 | elif len(args) == 4: 40 | from os.path import exists 41 | 42 | if exists("./mapkey.txt"): 43 | 44 | with open("./mapkey.txt", "r") as f: 45 | key = f.read().split("\n")[0] 46 | 47 | else: 48 | print("Could not find key.txt. Please provide a key in the command or create the file \"mapkey.txt\" in the current working directory.\nUse \"python3 geolocation.py\" for help.") 49 | exit(2) 50 | 51 | querry = args[i] 52 | i += 1 53 | 54 | radius = args[i] 55 | 56 | full_response, code = mapquest.get_json(key, querry) 57 | 58 | if code != 200: 59 | print("An error has occured!\n{msg}".format(msg=full_response)) 60 | exit(3) 61 | 62 | lat, lon = mapquest.choose_option(full_response) 63 | 64 | url = twitter.get_url(lat, lon, radius) 65 | twitter.open_standard_browser(url) 66 | 67 | elif 4 <= len(args) <= 5 and args[1] == "-w": 68 | import what_three_words as words 69 | 70 | i = 2 71 | 72 | if len(args) == 5: 73 | key = args[i] 74 | i += 1 75 | elif len(args) == 4: 76 | from os.path import exists 77 | 78 | if exists("./wordkey.txt"): 79 | 80 | with open("./wordkey.txt", "r") as f: 81 | key = f.read().split("\n")[0] 82 | 83 | else: 84 | print("Could not find key.txt. Please provide a key in the command or create the file \"wordkey.txt\" in the current working directory.\nUse \"python3 geolocation.py\" for help.") 85 | exit(2) 86 | 87 | 88 | querry = words.clean_querry(args[i]) 89 | i += 1 90 | 91 | radius = args[i] 92 | 93 | full_response, code = words.get_json(key, querry) 94 | 95 | if code != 200: 96 | print("An error has occured!\n{msg}".format(msg=full_response)) 97 | exit(3) 98 | 99 | lat = full_response["square"]["southwest"]["lat"] 100 | lon = full_response["square"]["southwest"]["lng"] 101 | 102 | url = twitter.get_url(lat, lon, radius) 103 | twitter.open_standard_browser(url) 104 | 105 | else: 106 | print(help_str) 107 | 108 | if __name__ == '__main__': 109 | main() 110 | -------------------------------------------------------------------------------- /src/mapquest.py: -------------------------------------------------------------------------------- 1 | import json, requests 2 | 3 | def get_json(key, querry): 4 | 5 | querry = querry.replace(" ", "+") 6 | limit = 5 7 | url = "http://open.mapquestapi.com/nominatim/v1/search.php?key={key}&format=json&q={querry}&addressdetails=0&limit={limit}" 8 | 9 | r = requests.get(url.format(key=key, querry=querry, limit = limit)) 10 | if r.status_code == 200: 11 | result = json.loads(r.text) 12 | else: 13 | result = r.text 14 | return result, r.status_code 15 | 16 | def choose_option(full_json): 17 | """ 18 | Takes full json-api-response and returns latitude and longitude as strings 19 | """ 20 | 21 | print("Search Results:\n") 22 | 23 | i = 1 24 | 25 | for result in full_json: 26 | print(f"({i}) {result['display_name']}") 27 | i += 1 28 | 29 | if len(full_json) == 1: 30 | selection = 0 31 | else: 32 | success = False 33 | print("\nPlease select an option or type \"exit\" to exit") 34 | 35 | while not success: 36 | c = input(">") 37 | 38 | if c == "exit": 39 | exit(5) 40 | 41 | try: 42 | selection = int(c) 43 | success = True 44 | except: 45 | print(f"Could not find result No. {c}\n") 46 | 47 | return full_json[selection-1]["lat"], full_json[selection-1]["lon"] 48 | -------------------------------------------------------------------------------- /src/twitter.py: -------------------------------------------------------------------------------- 1 | import webbrowser as browser 2 | 3 | def get_url(lat, lon, radius): 4 | url = "https://twitter.com/search?q=geocode:{lat},{lon},{radius}" 5 | return url.format(lat=lat, lon=lon, radius=radius) 6 | 7 | def open_standard_browser(url): 8 | browser.open(url) 9 | -------------------------------------------------------------------------------- /src/what_three_words.py: -------------------------------------------------------------------------------- 1 | import json, requests 2 | 3 | def get_json(key: str, querry: str): 4 | 5 | querry = querry.replace(" ", "+") 6 | limit = 5 7 | url = "https://api.what3words.com/v3/convert-to-coordinates?words={querry}&key={key}" 8 | 9 | r = requests.get(url.format(key=key, querry=querry, limit = limit)) 10 | if r.status_code == 200: 11 | result = json.loads(r.text) 12 | else: 13 | result = r.text 14 | return result, r.status_code 15 | 16 | def clean_querry(querry: str): 17 | """ 18 | Will remove the 3 slashes from the beginning of a querry 19 | """ 20 | 21 | if len(querry) > 3 and querry[:3] == "///": 22 | return querry[3:] 23 | else: 24 | return querry 25 | --------------------------------------------------------------------------------