from plyer import gps from kivy.app import App from kivy.clock import Clock from kivy.properties import DictProperty, NumericProperty from kivy.clock import mainthread from kivy.utils import platform from kivy_garden.mapview import MapView from kivy.uix.screenmanager import ScreenManager, Screen, RiseInTransition from kivy.vector import Vector from kivy.animation import Animation from math import atan2, sin, cos, degrees, floor class MapScreen(Screen): pass class CompassScreen(Screen): pass class FlockompassApp(App): gps_data = DictProperty() session_data = DictProperty() needle_angle = NumericProperty(0) fbearing = NumericProperty(0) def dump(self, dt): print(dt, self.gps_data, self.session_data) def set_destination(self): self.compass_enable() self.session_data['dest_lat'] = self.ms.ids.centermark.lat self.session_data['dest_lon'] = self.ms.ids.centermark.lon self.ms.ids.mapview.center_on(self.session_data['dest_lat'], self.session_data['dest_lon']) def request_android_permissions(self): from android.permissions import request_permissions, Permission def callback(permissions, results): if all([res for res in results]): print("aguas: callback. All permissions granted.") else: print("aguas: callback. Some permissions refused.", results) request_permissions([Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION], callback) def gps_start(self, minTime, minDistance): gps.start(minTime, minDistance) def gps_stop(self): gps.stop() @mainthread def on_location(self, **kwargs): self.gps_data = kwargs if ('dest_lat' not in self.session_data and 'dest_lon' not in self.session_data): self.session_data['dest_lat'] = self.gps_data['lat'] self.session_data['dest_lon'] = self.gps_data['lon'] self.ms.ids.mapview.center_on(self.session_data['dest_lat'], self.session_data['dest_lon']) else: lat1 = self.gps_data['lat'] lon1 = self.gps_data['lon'] lat2 = self.session_data['dest_lat'] lon2 = self.session_data['dest_lon'] self.fbearing = atan2(sin(lon2 - lon1) * cos(lat2), cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2-lon1)) self.fbearing = self.needle_angle + degrees(self.fbearing) self.fbearing = (self.fbearing + 360) % 360 def get_field(self, dt): needle_angle = 7 if self.cs.facade.field != (None, None, None): self.x_calib, self.y_calib, self.z_calib = self.cs.facade.field x, y, z = self.cs.facade.field needle_angle = Vector(x, y).angle((0, 1)) + 90. # fix animation transition around the unit circle if (self.needle_angle % 360) - needle_angle > 180: needle_angle += 360 elif (self.needle_angle % 360) - needle_angle < -180: needle_angle -= 360 # add the number of revolutions to the result needle_angle += 360 * floor(self.needle_angle / 360.) # animate the needle if self.cs._anim: self.cs._anim.stop(self) self.cs._anim = Animation(needle_angle=needle_angle, d=0.2, t='out_quad') self.cs._anim.start(self) def compass_enable(self): self.cs.facade.enable() Clock.schedule_interval(self.get_field, 1 / 5.0) def compass_disable(self): self.cs.facade.disable() Clock.unschedule(self.get_field) def on_pause(self): self.compass_disable() self.gps_stop() return True def on_resume(self): self.compass_enable() self.gps_start(1000, 0) def build(self): # start GPS try: gps.configure(on_location=self.on_location) except NotImplementedError: import traceback traceback.print_exc() print('GPS is not implemented for your platform') if platform == "android": print("gps.py: Android detected. Requesting permissions") self.request_android_permissions() self.gps_start(1000, 0) # setup app screens screen_manager = ScreenManager(transition=RiseInTransition()) self.ms = MapScreen(name='map') screen_manager.add_widget(self.ms) self.cs = CompassScreen(name='compass') self.cs._anim = None self.cs.p = None screen_manager.add_widget(self.cs) Clock.schedule_interval(self.dump, 2.0) return screen_manager if __name__ == '__main__': app = FlockompassApp() app.run()