-
-
Notifications
You must be signed in to change notification settings - Fork 54
Expand file tree
/
Copy pathgetpos.py
More file actions
executable file
·144 lines (121 loc) · 4.01 KB
/
Copy pathgetpos.py
File metadata and controls
executable file
·144 lines (121 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env python3
"""
Get the current position (latitude, longitude, accuracy/precision) via GeoClue2.
Requirements:
sudo apt install gir1.2-geoclue-2.0 python3-gi
GeoClue2 daemon (package "geoclue-2.0") must be installed and running.
"""
import argparse
import json
import sys
import gi
gi.require_version("Geoclue", "2.0")
from gi.repository import GLib, Geoclue
def get_current_position(app_id="my-python-app",
accuracy_level=Geoclue.AccuracyLevel.EXACT,
timeout_seconds=15,
max_accuracy_meters=None,
on_update=None):
loop = GLib.MainLoop()
result = {}
error_holder = {}
simple_holder = {}
def meets_threshold(location):
if max_accuracy_meters is None:
return True
accuracy = location.get_property("accuracy")
return accuracy is not None and accuracy <= max_accuracy_meters
def capture(location):
result["latitude"] = location.get_property("latitude")
result["longitude"] = location.get_property("longitude")
result["accuracy"] = location.get_property("accuracy")
if on_update is not None:
on_update(dict(result))
def on_location_notify(simple, param):
location = simple.get_location()
if location is None:
return
capture(location)
if meets_threshold(location):
loop.quit()
def on_client_ready(source, res, user_data=None):
try:
simple = Geoclue.Simple.new_finish(res)
except GLib.Error as e:
error_holder["error"] = e
loop.quit()
return
simple_holder["simple"] = simple
location = simple.get_location()
if location is not None:
capture(location)
if meets_threshold(location):
loop.quit()
return
simple.connect("notify::location", on_location_notify)
def on_timeout():
if "latitude" not in result:
error_holder["error"] = TimeoutError(
f"No location fix received within {timeout_seconds}s"
)
loop.quit()
return False
GLib.timeout_add_seconds(timeout_seconds, on_timeout)
Geoclue.Simple.new(app_id, accuracy_level, None, on_client_ready)
loop.run()
if "error" in error_holder:
raise error_holder["error"]
return result
def parse_args():
parser = argparse.ArgumentParser(
description="Get the current position via GeoClue2."
)
parser.add_argument(
"-t", "--timeout",
type=float,
default=15,
metavar="SECONDS",
help="max time to wait for a location fix (default: 15)",
)
parser.add_argument(
"-a", "--accuracy",
type=float,
default=600,
metavar="METERS",
help=("keep waiting for fixes until accuracy is reached"),
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="print every position fix as it's acquired, not just the final one",
)
parser.add_argument(
"-vo", "--verbose-stdout",
action="store_true",
help=("print intermediate positions to stdout"),
)
return parser.parse_args()
def main():
args = parse_args()
if args.verbose_stdout:
args.verbose = True
def print_update(fix):
stream = sys.stdout if args.verbose_stdout else sys.stderr
print(json.dumps(fix), file=stream)
try:
loc = get_current_position(
accuracy_level=Geoclue.AccuracyLevel.EXACT,
timeout_seconds=args.timeout,
max_accuracy_meters=args.accuracy,
on_update=print_update if args.verbose else None,
)
except Exception as e:
print(f"Failed to get location: {e}", file=sys.stderr)
sys.exit(1)
print(json.dumps({
"lat": loc["latitude"],
"lon": loc["longitude"],
"accuracy": loc["accuracy"],
}))
if __name__ == "__main__":
main()