def __init__(self, osm_object, size):
        """
        
        @type osm_object: L{geo.osm_import.OSM_objects}
        @param osm_object: The OSM data representation
        @type size: (int, int)
        @param size: window size in pixels as a tuple C{(width, height)}
        """
        width, height = size
        self.__window_width = width     #: window width in pixels
        self.__window_height = height   #: window height in pixels
        self.__show_tiles = False       #: stores if the background tiles are shown
        self.__show_poi = False         #: stores if the points of interest are shown
        self.__show_partitions = False  #: stores if the partitions are shown
        
        self.__osm_object = osm_object  #: OSM data representation
        self.__osm_box = osm_object.street_tree.get_bounds() #: bounding box of all street objects
        self.__zoom_object = ZoomObject(size) #: stores a reference to the L{geo.zoom.ZoomObject} 
        self.__zoom_object.find_zoom_level(self.__osm_box)
        
        self.__viewport_coordinates = self.__zoom_object.viewport_coordinates() #: Geographic coordinates of the edges of the visible area
                
        self.__draw_map()

        self.__show_generalized = 0     #: stores the tolerance value of the generalization currently shown
class OSMMapRendering(object):
    """ Objects of the class C{OSMMapRendering} stores the display parameters of the map.
    
    The instance methods of the class are used to set the display parameters,
    to draw the map and move/zoom the map.
    """

    def __init__(self, osm_object, size):
        """
        
        @type osm_object: L{geo.osm_import.OSM_objects}
        @param osm_object: The OSM data representation
        @type size: (int, int)
        @param size: window size in pixels as a tuple C{(width, height)}
        """
        width, height = size
        self.__window_width = width     #: window width in pixels
        self.__window_height = height   #: window height in pixels
        self.__show_tiles = False       #: stores if the background tiles are shown
        self.__show_poi = False         #: stores if the points of interest are shown
        self.__show_partitions = False  #: stores if the partitions are shown
        
        self.__osm_object = osm_object  #: OSM data representation
        self.__osm_box = osm_object.street_tree.get_bounds() #: bounding box of all street objects
        self.__zoom_object = ZoomObject(size) #: stores a reference to the L{geo.zoom.ZoomObject} 
        self.__zoom_object.find_zoom_level(self.__osm_box)
        
        self.__viewport_coordinates = self.__zoom_object.viewport_coordinates() #: Geographic coordinates of the edges of the visible area
                
        self.__draw_map()

        self.__show_generalized = 0     #: stores the tolerance value of the generalization currently shown

    def __draw_map(self):
        """ The method initialized the drawing of the map
        """
        
        # calculate the pixel dimensions
        tile_box = self.__zoom_object.tile_box()
        self.__min_lon, self.__min_lat, self.__max_lon, self.__max_lat = tile_box
        self.__projection = self.__osm_object.get_osm_projection()
        self.__min_x, self.__min_y = self.__projection(self.__min_lon, self.__min_lat)
        self.__max_x, self.__max_y = self.__projection(self.__max_lon, self.__max_lat)
        self.__width = self.__max_x - self.__min_x
        self.__height = self.__max_y - self.__min_y
        self.__pixel_width, self.__pixel_height = self.__zoom_object.tile_box_pixel()

        # calculate the adjustment of the map
        position = self.__zoom_object.get_position_in_tile(self.__osm_box[3], self.__osm_box[0])
        self.__vadj = gtk.Adjustment(position[1], lower=0,
                                     upper=self.__pixel_height,
                                     step_incr=10,
                                     page_incr=100,
                                     page_size=self.__window_height)    #: U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the vertical alignment of the viewport
        self.__hadj = gtk.Adjustment(position[0],
                                     lower=0,
                                     upper=self.__pixel_width,
                                     step_incr=10,
                                     page_incr=100,
                                     page_size=self.__window_width)     #: U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the horizontal alignment of the viewport
        
        # create the drawing area
        self.__area = gtk.DrawingArea() #: U{GTK drawing area<http://developer.gnome.org/pygtk/stable/class-gtkdrawingarea.html>}
        self.__area.set_size_request(self.__pixel_width, self.__pixel_height)
        
        self.__viewport_coordinates = self.__zoom_object.viewport_coordinates()
        self.__street_box = self.increase_box(self.__viewport_coordinates)
        
        # start the drawing itself
        self.__area.connect("expose-event", self.__draw_ways)
        
        # needed for display within the gui
        # add the drawing area to a viewport
        # the viewport will later on be added to the main window
        self.__vp = gtk.Viewport() #: U{GTK viewport object<http://developer.gnome.org/pygtk/stable/class-gtkviewport.html>}
        #self.__vp = gtk.ScrolledWindow()
        self.__vp.set_size_request(self.__window_width, self.__window_height)
        self.__vp.add(self.__area)
        #self.__vp.add_with_viewport(self.__area)
        
    
    
    def getArea(self):
        """
        Returns the U{GTK viewport object<http://developer.gnome.org/pygtk/stable/class-gtkviewport.html>} that contains the drawing area

        @returns: the U{GTK viewport object<http://developer.gnome.org/pygtk/stable/class-gtkviewport.html>} that contains the drawing area
        @rtype: C{U{gtk.Viewport<http://developer.gnome.org/pygtk/stable/class-gtkviewport.html>}}
        """
        return self.__vp

    def get_vadjustment(self):
        """ Returns the U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the vertical alignment of the viewport.
        
        @returns: the U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the vertical alignment of the viewport.
        @rtype: C{U{gtk.Adjustment<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>}}
        """
        return self.__vadj
    vadj = property(get_vadjustment, None, None, 'read-only property for the U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the vertical alignment of the viewport.')

    def get_hadjustment(self):
        """ Returns the U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the horizontal alignment of the viewport.
        
        @returns: the U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the horizontal alignment of the viewport.
        @rtype: C{U{gtk.Adjustment<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>}}
        """
        return self.__hadj
    hadj = property(get_hadjustment, None, None, 'read-only property for the U{GTK Adjustment object<http://developer.gnome.org/pygtk/stable/class-gtkadjustment.html>} for the horizontal alignment of the viewport.')
    
    def __draw_ways(self, area, event):
        """ Callback function for the "expose-event signal"
        
        The method is executed everytime the drawing area receives a redraw signal.
        
        @param area: the GTK widget that received the signal
        @param event: the event that triggered the signal
        """
        self.style = self.__area.get_style()
        self.gc = self.style.fg_gc[gtk.STATE_NORMAL]


        # TODO: remove debug code
#        colors = ['#f00', '#0f0', '#00f', '#f0f', '#0ff', '#f0f']
#        color_index = 0
#        boxes_to_draw = [self.__osm_box, self.__street_box, self.__viewport_coordinates]
#        for box_to_draw in boxes_to_draw:
#            self.gc.set_rgb_fg_color(gtk.gdk.Color(colors[color_index]))
#            color_index += 1
#            w, s, e, n = box_to_draw
#            sw_x, sw_y = self.__projection(w, s)
#            nw_x, nw_y = self.__projection(w, n)
#            ne_x, ne_y = self.__projection(e, n)
#            se_x, se_y = self.__projection(e, s)
#            self.__area.window.draw_line(self.gc,
#                                         self.__pixel_x(sw_x), self.__pixel_y(sw_y),
#                                         self.__pixel_x(nw_x), self.__pixel_y(nw_y))
#            self.__area.window.draw_line(self.gc,
#                                         self.__pixel_x(sw_x), self.__pixel_y(sw_y),
#                                         self.__pixel_x(ne_x), self.__pixel_y(ne_y))
#            self.__area.window.draw_line(self.gc,
#                                         self.__pixel_x(sw_x), self.__pixel_y(sw_y),
#                                         self.__pixel_x(se_x), self.__pixel_y(se_y))
#            self.__area.window.draw_line(self.gc,
#                                         self.__pixel_x(ne_x), self.__pixel_y(ne_y),
#                                         self.__pixel_x(nw_x), self.__pixel_y(nw_y))
#            self.__area.window.draw_line(self.gc,
#                                         self.__pixel_x(se_x), self.__pixel_y(se_y),
#                                         self.__pixel_x(nw_x), self.__pixel_y(nw_y))
#            self.__area.window.draw_line(self.gc,
#                                         self.__pixel_x(se_x), self.__pixel_y(se_y),
#                                         self.__pixel_x(ne_x), self.__pixel_y(ne_y))
#            print self.__pixel_x(sw_x), self.__pixel_y(sw_y)
#            print self.__pixel_x(nw_x), self.__pixel_y(nw_y)
#            print self.__pixel_x(ne_x), self.__pixel_y(ne_y)
#            print self.__pixel_x(se_x), self.__pixel_y(se_y)
#        self.gc.set_rgb_fg_color(gtk.gdk.Color(FOREGROUND_COLOR))



        # create the background image if tiles are activated 
        if self.__show_tiles:
            background_image = background_from_tiles(self.__zoom_object.get_tiles(), self.__zoom_object.zoom_level)
            pixbuf = pil_image_to_pixbuf(background_image)
            self.__area.window.draw_pixbuf(self.gc, pixbuf, 0, 0, 0, 0)

        #ways = [self.__osm_object.getWayByID(index) for index in self.__osm_object.way_tree.intersection(self.__osm_object.box, "raw")]
        #way_colors = ['#f00','#0f0', '#00f', '#f0f', '#0ff', '#f0f']
        way_colors = ['#f00','#0f0', '#00f']
        
        # draw the streets
        ways = [self.__osm_object.getWayByID(index) for index in self.__osm_object.street_tree.intersection(self.__street_box, "raw")]
        for way in ways:
            
            # calculate color and thickness of the streets,
            # depending on what is displayed
            self.gc.line_width = 1
            if self.__show_tiles:
                self.gc.line_width += 1
            if self.__show_partitions:
                self.__osm_object.get_partitions()
                if self.__osm_object.get_partitions().recalculate:
                    self.__osm_object.recalculate_partitions()
                partition = way.partition_id
                if self.__osm_object.get_partitions().get_largest_partition() == partition:
                    self.gc.set_rgb_fg_color(gtk.gdk.Color(FOREGROUND_COLOR))
                elif partition == -1:
                    self.gc.line_width += 3
                    self.gc.set_rgb_fg_color(gtk.gdk.Color('#f00'))
                else:
                    self.gc.line_width += 2
                    self.gc.set_rgb_fg_color(gtk.gdk.Color(way_colors[partition % len(way_colors)]))
            
            # find the nodes for drawing the lines
            if 'highway' in way.getTags():
                points = []
                if self.__show_generalized == 0:
                    nodes = way.nodes
                else:
                    # if necessary use the generalized way
                    nodes = way.generalized.get(self.__show_generalized)
                for node in nodes:
                    node_x, node_y = node.get_xy()
                    points.append((self.__pixel_x(node_x), self.__pixel_y(node_y)))
                self.__area.window.draw_lines(self.gc, points)
        
        if self.__show_partitions:
            self.gc.set_rgb_fg_color(gtk.gdk.Color(FOREGROUND_COLOR))
        
        # draw the POI
        if self.__show_poi:
            for node in self.__osm_object.get_poi():
                poi_state = node.get_poi()

                # different colors for the different states of a POI
                if poi_state == POI_CONNECTED:
                    self.gc.set_rgb_fg_color(gtk.gdk.Color('#0f0'))
                elif poi_state == POI_SELECTED:  
                    self.gc.set_rgb_fg_color(gtk.gdk.Color('#00f'))
                elif poi_state == POI_NOT_CONNECTED:  
                    self.gc.set_rgb_fg_color(gtk.gdk.Color('#f00'))
                node_x, node_y = node.get_xy()
                self.__area.window.draw_arc(self.gc, True,
                                            self.__pixel_x(node_x)-POI_SIZE/2,
                                            self.__pixel_y(node_y)-POI_SIZE/2,
                                            POI_SIZE, POI_SIZE, 0, 360*64)
            self.gc.set_rgb_fg_color(gtk.gdk.Color(FOREGROUND_COLOR))
        
    def __pixel_x(self, x):
        """ Calculates for a given geodetic x coordinate the pixel coordinate based on the dimensions of the map
        
        @type x: C{float}
        @param x: geodetic x coordinate
        @returns: pixel coordinate in x direction
        @rtype: C{int}
        """
        return int((x - self.__min_x) * self.__pixel_width / self.__width) + 1

    def __pixel_y(self, y):
        """ Calculates for a given geodetic y coordinate the pixel coordinate based on the dimensions of the map
        
        @type y: C{float}
        @param y: geodetic y coordinate
        @returns: pixel coordinate in y direction
        @rtype: C{int}
        """
        return int((self.__max_y - y) * self.__pixel_height / self.__height) + 1

    def zoom_in(self):
        """ Increases the OSM zoom level by 1 and initializes the recalculation of the map dimensions     
        """
        if self.__zoom_object.zoom_level < 18:
            #self.__street_box = self.__osm_box[:]
            self.__osm_box = self.decrease_box(self.__osm_box)
            self.__zoom_object.zoom_in()
            self.__zoom_object.find_zoom_level(self.__osm_box)
            self.__draw_map()
    
    def zoom_out(self):
        """ Decreases the OSM zoom level by 1 and initializes the recalculation of the map dimensions     
        """
        if self.__zoom_object.zoom_level > 10:
            #self.__osm_box = self.__street_box[:]
            #self.__street_box = self.increase_box(self.__street_box)
            self.__osm_box = self.increase_box(self.__osm_box)
            self.__zoom_object.find_zoom_level(self.__osm_box)
            self.__draw_map()

    def move(self, keyname):
        """ Moves the map and initializes the recalculation of the map dimensions
        
        @param keyname: one of 'Left', 'Right', 'Up' or 'Down'
        """
        d_x = self.__osm_box[2] - self.__osm_box[0]
        d_y = self.__osm_box[3] - self.__osm_box[1]
        if keyname == 'Left':
            self.__osm_box[0] += d_x/2
            self.__osm_box[2] += d_x/2
            #self.__street_box[0] += d_x/2
            #self.__street_box[2] += d_x/2
        if keyname == 'Right':
            self.__osm_box[0] -= d_x/2
            self.__osm_box[2] -= d_x/2
            #self.__street_box[0] -= d_x/2
            #self.__street_box[2] -= d_x/2
        if keyname == 'Up':
            self.__osm_box[1] -= d_y/2
            self.__osm_box[3] -= d_y/2
            #self.__street_box[1] -= d_y/2
            #self.__street_box[3] -= d_y/2
        if keyname == 'Down':
            self.__osm_box[1] += d_y/2
            self.__osm_box[3] += d_y/2
            #self.__street_box[1] += d_y/2
            #self.__street_box[3] += d_y/2
        self.__zoom_object.find_zoom_level(self.__osm_box)
        self.__draw_map()
        
    def increase_box(self, box):
        """ Doubles the size of an OSM bounding box
        
        @type box: C{[min_lon, min_lat, max_lon, max_lat]}
        @param box: OSM bounding box
        @returns: the increased bounding box
        @rtype: C{[min_lon, min_lat, max_lon, max_lat]}
        """
        d_x = box[2] - box[0]
        d_y = box[3] - box[1]
        increased_box = [box[0] - d_x/2,
                         box[1] - d_y/2,
                         box[2] + d_x/2,
                         box[3] + d_y/2]
        return increased_box
 
    def decrease_box(self, box):
        """ Halves the size of an OSM bounding box
        
        @type box: C{[min_lon, min_lat, max_lon, max_lat]}
        @param box: OSM bounding box
        @returns: the decreased bounding box
        @rtype: C{[min_lon, min_lat, max_lon, max_lat]}
        """
        d_x = box[2] - box[0]
        d_y = box[3] - box[1]
        decreased_box = [box[0] + d_x/4,
                         box[1] + d_y/4,
                         box[2] - d_x/4,
                         box[3] - d_y/4]
        return decreased_box   

    def set_show_tiles(self, state):
        """ Sets the state if the OSM tiles are shown
        
        @type state: C{bool}
        @param state: True if the tiles are shown
        """
        self.__show_tiles = state
    show_tiles = property(None, set_show_tiles, None, 'write-only property for the "show tile" state')

    def set_show_poi(self, state):
        """ Sets the state if the points of interest are shown
        
        @type state: C{bool}
        @param state: True if the POI are shown
        """
        self.__show_poi = state
    show_poi = property(None, set_show_poi, None, 'write-only property for the "show POI" state')        

    def set_show_partitions(self, state):
        """ Sets the state if the partitions are shown
        
        @type state: C{bool}
        @param state: True if the partitions are shown
        """
        self.__show_partitions = state
    show_partitions = property(None, set_show_partitions, None, 'write-only property for the "show partitions" state') 

    def set_show_generalized(self, tolerance):
        """ Sets the tolerance value of the generalization that will be used for the map display
        
        @param tolerance: tolerance value of the generalization that will be used for the map display
        """
        self.__show_generalized = tolerance
    show_generalized = property(None, set_show_generalized, None, 'write-only property for the generalization that will be displayed')