├── .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 |
--------------------------------------------------------------------------------