def __init__(self, width=-1, height=-1):
            ### starting display, and 3d part###
            self.display = pi3d.Display.create(x=0, y=0, w=width, h=height,
                                               frames_per_second=20,
                                               background=(0, 0, 0, 255))
            pi3d.Light((0, 0, 10))

            self.camera = pi3d.Camera(is_3d=False)

            self.camera.was_moved = False

            self.flat_shader = pi3d.Shader("uv_flat")

            #starting input listeners#
            self.inputs = pi3d.InputEvents()
            self.inputs.get_mouse_movement()

            self.width = self.display.width
            self.height = self.display.height

            self.last_scroll = datetime.now()    # variable to limit zooming speed

            self.window = self.display

            # these are the point where the screen focuses
            self.view_longitude = -48.519688
            self.view_latitude = -27.606899

            #additional points showing on the map
            self.points = [(self.view_longitude, self.view_latitude),
                           (0.0, 0.0),
                           (180, 36.8),
                           (-47.886829, -15.793751)]

            # this will show as an arrow on the map
            self.tracked_object_position = [0, 0, 0]
            self.tracking = True
            self.following_tracked = True  # camera should follow tracked object?

            #current zoom level
            self.zoom = 10
            #current button state
            self.button = 0

            # mouse coordinates on screen
            self.pointer_x = 0
            self.pointer_y = 0

            # position variations on a "tick"
            self.dx = 0
            self.dy = 0

            #currently loaded tiles
            self.tiles = []
            self.tiles_set = set()
            self.current_center_tile = (0, 0)

            #draw gui only, the avoid redrawing the tiles
            self.draw_gui_only_flag = False

            #should reload the tiles? set on moving the map
            self.updated = True
            self.tile_list_updated = True

            #the tile loader gives the tiles around the current position
            self.tile_loader = TileLoader(self)

            #waypoint drawing widget
            self.waypoints = WaypointsWidget(self.display, self.camera)
            self.waypoints.set_points(self.points)

            #shows details on the screen as text
            self.text = TextWidget(self.display, self.camera)
            self.text.set_update_rate(3)  # updates every 3 seconds

            #shows the crosshair in the middle of the screen
            self.crosshair = Crosshair(self.display, self.camera)
            #shows the tracking arrow
            self.tracked = TrackedObject(self.display, self.camera)
            #shows the navball
            self.horizon = Horizon(self.camera, self.display)
            #shows the mouse pointer
            self.pointer = Pointer(self)
            #reads telemetry from the serial port
            self.telemetry_reader = TelemetryReader(self)
            #assorted telemetry received data
            self.data = {}

            #starts
            self.main_loop()
class GroundStation(object):

        def __init__(self, width=-1, height=-1):
            ### starting display, and 3d part###
            self.display = pi3d.Display.create(x=0, y=0, w=width, h=height,
                                               frames_per_second=20,
                                               background=(0, 0, 0, 255))
            pi3d.Light((0, 0, 10))

            self.camera = pi3d.Camera(is_3d=False)

            self.camera.was_moved = False

            self.flat_shader = pi3d.Shader("uv_flat")

            #starting input listeners#
            self.inputs = pi3d.InputEvents()
            self.inputs.get_mouse_movement()

            self.width = self.display.width
            self.height = self.display.height

            self.last_scroll = datetime.now()    # variable to limit zooming speed

            self.window = self.display

            # these are the point where the screen focuses
            self.view_longitude = -48.519688
            self.view_latitude = -27.606899

            #additional points showing on the map
            self.points = [(self.view_longitude, self.view_latitude),
                           (0.0, 0.0),
                           (180, 36.8),
                           (-47.886829, -15.793751)]

            # this will show as an arrow on the map
            self.tracked_object_position = [0, 0, 0]
            self.tracking = True
            self.following_tracked = True  # camera should follow tracked object?

            #current zoom level
            self.zoom = 10
            #current button state
            self.button = 0

            # mouse coordinates on screen
            self.pointer_x = 0
            self.pointer_y = 0

            # position variations on a "tick"
            self.dx = 0
            self.dy = 0

            #currently loaded tiles
            self.tiles = []
            self.tiles_set = set()
            self.current_center_tile = (0, 0)

            #draw gui only, the avoid redrawing the tiles
            self.draw_gui_only_flag = False

            #should reload the tiles? set on moving the map
            self.updated = True
            self.tile_list_updated = True

            #the tile loader gives the tiles around the current position
            self.tile_loader = TileLoader(self)

            #waypoint drawing widget
            self.waypoints = WaypointsWidget(self.display, self.camera)
            self.waypoints.set_points(self.points)

            #shows details on the screen as text
            self.text = TextWidget(self.display, self.camera)
            self.text.set_update_rate(3)  # updates every 3 seconds

            #shows the crosshair in the middle of the screen
            self.crosshair = Crosshair(self.display, self.camera)
            #shows the tracking arrow
            self.tracked = TrackedObject(self.display, self.camera)
            #shows the navball
            self.horizon = Horizon(self.camera, self.display)
            #shows the mouse pointer
            self.pointer = Pointer(self)
            #reads telemetry from the serial port
            self.telemetry_reader = TelemetryReader(self)
            #assorted telemetry received data
            self.data = {}

            #starts
            self.main_loop()

        def zoom_in(self):
            self.zoom += 1
            if self.zoom > 20:
                self.zoom = 20

        def zoom_out(self):
            self.zoom -= 1
            if self.zoom < 2:
                self.zoom = 2

        def on_scroll(self, zoom_in):
            if (datetime.now() - self.last_scroll).microseconds > 100000:
                if zoom_in > 0:
                    self.zoom_in()
                elif zoom_in < 0:
                    self.zoom_out()
                self.queue_draw(tile=True)
                self.last_scroll = datetime.now()
                print(self.zoom)

        def set_zoom(self, zoom):
            self.zoom = zoom

        def set_focus(self, longitude, latitude):
            self.view_longitude = longitude
            self.view_latitude = latitude
            self.queue_draw(tile=True)

        #@timeit
        def draw_info(self):
            """
            draws text data on the screen, currently, gps data and pending tiles to draw

            usually takes 1~5ms
            250ms when updating!
            """
            string = " lat:{0}\n long:{1}".format(self.view_latitude, self.view_longitude)
            self.text.set_text(string)
            self.text.update()

        def set_tracked_position(self, longitude, latitude, yaw):
            """
            updates tracked object position, called by telemetry reader
            """
            if self.tracked_object_position[0] != longitude or self.tracked_object_position[1] != latitude:
                self.tracked_object_position = (longitude, latitude, yaw)
                #asks to update the gui alone
                self.queue_draw(gui_only=True)

            if self.following_tracked and "gps_sats" in self.data:
                if self.data["gps_sats"] >= 3:
                    self.set_focus(longitude, latitude)
                    self.queue_draw(tile=True)

        def set_attitude(self, roll, pitch, yaw=0):
            """
            updates the object attitude, called from telemetry reader, written directly on self.horizon.
            should probably be changed
            """
            if self.horizon.tilt != pitch or self.horizon.roll != roll or self.horizon.yaw != yaw:
                self.horizon.set_attitude(roll, pitch, yaw)
                self.queue_draw(gui_only=True)

        def draw_tiles(self):
            """
            Core map-drawing function

            checks if the map position has been updated, if not, prints the old tiles.
            if so, reloads the tiles as a bidimensional list, and prints them accordingly
            """
            span_x = self.width
            span_y = self.height
            tiles_x = int(ceil(span_x/256.0))
            tiles_y = int(ceil(span_y/256.0))
            if self.updated or self.tile_list_updated:
                # checks if the centered tile changed, so the tile set has to be reloaded
                new_center_tile = self.tile_loader.coord_to_gmap_tile_int(self.view_longitude,
                                                                          self.view_latitude,
                                                                          self.zoom)

                if new_center_tile != self.current_center_tile or self.tile_list_updated:
                    new_tiles = self.tile_loader.load_area(self.view_longitude,
                                                           self.view_latitude,
                                                           self.zoom,
                                                           tiles_x,
                                                           tiles_y)
                    ## using sets to detect sprites to be loaded and unloaded
                    new_set = set()
                    for line in new_tiles:
                        new_set |= set(line)

                    removing = self.tiles_set-new_set
                    self.display.remove_sprites(*removing)

                    adding = new_set - self.tiles_set
                    self.display.add_sprites(*adding)
                    self.tiles = new_tiles
                    self.tiles_set = new_set     # updated current tiles

                self.current_center_tile = new_center_tile

                # tiles have been properly loaded, now we have to place them on the proper positions

                tile_number = 0
                line_number = 0
                x_center = 0
                y_center = 0
                ## offset to keep the current coordinate centered on the screen
                offset_x, offset_y = self.tile_loader.gmap_tile_xy_from_coord(self.view_longitude,
                                                                              self.view_latitude,
                                                                              self.zoom)

                #size of lines and columns
                x_tiles = len(self.tiles[0])
                y_tiles = len(self.tiles)

                #draw it all, fixing the position
                for line in self.tiles:
                    for tile in line:
                        x = (tile_number - int(x_tiles/2)) * 256 + x_center
                        y = (line_number - int(y_tiles/2)) * 256 + y_center
                        final_x = x - offset_x + 128
                        final_y = y - offset_y + 128
                        tile.position(final_x+self.dx, -(final_y+self.dy), 100.0)
                        #tile.draw()
                        tile_number += 1
                    tile_number = 0
                    line_number += 1

                #self.draw_points()  # must move out of here!

            else:
                pass

        def draw(self):
            """
            high level draw function, calls the lower ones
            """
            self.draw_tiles()
            self.draw_gui()
            self.updated = False

        #@timeit
        def draw_gui(self):
            if self.draw_gui_only_flag:
                self.tracked.draw(self.tracking,
                                  self.tracked_object_position,
                                  self.view_longitude,
                                  self.view_latitude,
                                  self.zoom)

                self.draw_instruments()     # 0ms
                self.waypoints.draw_points(self.updated,
                                           self.view_longitude,
                                           self.view_latitude,
                                           self.zoom)
                self.pointer.update()
                self.draw_info()            # 5ms

        #@timeit
        def draw_instruments(self):
            """
            draws navball
            takes ~0ms
            """
            self.horizon.update()

        def queue_draw(self, gui_only=False, tile=False):
            """
            schedules a draw on next tick, may be complete, or gui-only
            """
            self.updated = not gui_only
            self.draw_gui_only_flag = True
            self.tile_list_updated = tile

        def update_mouse(self):
            """
            process mouse state
            """
            self.inputs.do_input_events()
            imx, imy, zoom, imh, imd = self.inputs.get_mouse_movement()
            self.pointer.on_move(self.pointer_x+imx, self.pointer_y+imy)

            if self.button == 1:
                delta_longitude, delta_latitude = dpix_to_dcoord(imx, self.view_latitude, imy, self.zoom)
                self.view_longitude -= delta_longitude
                self.view_latitude -= delta_latitude
                self.queue_draw()

               #check map boundaries
                if self.view_longitude > 180:
                    self.view_longitude = 180.0
                if self.view_longitude < -180:
                    self.view_longitude = -180.0
                if self.view_latitude > 85.0:
                    self.view_latitude = 85.0
                if self.view_latitude < -85.0:
                    self.view_latitude = -85.0
                #asks for update
                self.queue_draw()

            if self.inputs.key_state("BTN_LEFT"):
                self.button = 1
            else:
                self.button = 0
            if zoom:
                self.on_scroll(zoom)

        def set_data(self, string, value):
            self.data[string] = value

        def main_loop(self):
            """
            This is the Main Loop of the Game
            checks for mouse and keyboard inputs,
            draws the screen,
            and quits, if esc is pressed
            """
            time = 0
            start_time = datetime.now()
            while self.display.loop_running():
                self.update_mouse()
                if self.inputs.key_state(27):
                    self.display.destroy()
                    self.tile_loader.run = False
                    self.telemetry_reader.run = False
                self.draw()
                new_time = datetime.now()
                time = ((new_time-start_time).microseconds/1000000.0) * 0.1 + time*0.9
                #print "frame took {0:.2f} avg: {1:.2f} , fps:{2:.2f}".format(((new_time-start_time).microseconds/1000000.0), time, 1.0/time)
                start_time = new_time