[GRASS-SVN] r40799 - grass-addons/gui/wxpython/data_catalog

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Feb 3 14:10:51 EST 2010


Author: rashadkm
Date: 2010-02-03 14:10:50 -0500 (Wed, 03 Feb 2010)
New Revision: 40799

Added:
   grass-addons/gui/wxpython/data_catalog/mapdisp_window.py
Log:
fixed mapdisp_window

Added: grass-addons/gui/wxpython/data_catalog/mapdisp_window.py
===================================================================
--- grass-addons/gui/wxpython/data_catalog/mapdisp_window.py	                        (rev 0)
+++ grass-addons/gui/wxpython/data_catalog/mapdisp_window.py	2010-02-03 19:10:50 UTC (rev 40799)
@@ -0,0 +1,2760 @@
+"""!
+ at package mapdisp_window.py
+
+ at brief GIS map display canvas, buffered window.
+
+Classes:
+ - MapWindow
+ - BufferedWindow
+
+(C) 2006-2009 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS
+for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import time
+import math
+import sys
+import tempfile
+
+import wx
+
+import grass.script as grass
+
+import dbm
+import dbm_dialogs
+import gdialogs
+import gcmd
+import utils
+import globalvar
+import gselect
+from debug import Debug
+from preferences import globalSettings as UserSettings
+from units import ConvertValue as UnitsConvertValue
+from vdigit import GV_LINES as VDigit_Lines_Type
+from vdigit import VDigitCategoryDialog
+from vdigit import VDigitZBulkDialog
+from vdigit import VDigitDuplicatesDialog
+from vdigit import PseudoDC as VDigitPseudoDC
+
+class MapWindow(object):
+    """!
+    Abstract map window class
+
+    Parent for BufferedWindow class (2D display mode) and
+    GLWindow (3D display mode)
+    """
+    def __init__(self, parent, id=wx.ID_ANY,
+                 pos=wx.DefaultPosition,
+                 size=wx.DefaultSize,
+                 style=wx.NO_FULL_REPAINT_ON_RESIZE,
+                 Map=None, tree=None, lmgr=None):
+        self.parent = parent # MapFrame
+ 
+        #
+        # mouse attributes -- position on the screen, begin and end of
+        # dragging, and type of drawing
+        #
+        self.mouse = {
+            'begin': [0, 0], # screen coordinates
+            'end'  : [0, 0],
+            'use'  : "pointer",
+            'box'  : "point"
+            }
+        
+    def EraseMap(self):
+        """!
+        Erase the canvas (virtual method)
+        """
+        pass
+
+    def UpdateMap(self):
+        """!
+        Updates the canvas anytime there is a change to the
+        underlaying images or to the geometry of the canvas.
+        """
+        pass
+
+    def OnLeftDown(self, event):
+        pass
+
+    def OnLeftUp(self, event):
+        pass
+
+    def OnMouseMotion(self, event):
+        pass
+
+    def OnZoomToMap(self, event):
+        pass
+
+    def OnZoomToRaster(self, event):
+        pass
+    
+    def GetSelectedLayer(self, type = 'layer', multi = False):
+        """!
+        Get selected layer from layer tree
+
+        @param type 'item' / 'layer' / 'nviz'
+        @param multi return first selected layer or all
+        
+        @return layer / map layer properties / nviz properties
+        @return None / [] on failure
+        """
+        ret = []
+        if not self.tree or \
+                not self.tree.GetSelection():
+            if multi:
+                return []
+            else:
+                return None
+        
+        if multi and \
+                type == 'item':
+            return self.tree.GetSelections()
+        
+        for item in self.tree.GetSelections():
+            if not item.IsChecked():
+                if multi:
+                    continue
+                else:
+                    return None
+
+            if type == 'item': # -> multi = False
+                return item
+        
+            try:
+                if type == 'nviz':
+                    layer = self.tree.GetPyData(item)[0]['nviz']
+                else:
+                    layer = self.tree.GetPyData(item)[0]['maplayer']
+            except:
+                layer = None
+
+            if multi:
+                ret.append(layer)
+            else:
+                return layer
+            
+        return ret
+    
+class BufferedWindow(MapWindow, wx.Window):
+    """!
+    A Buffered window class.
+
+    When the drawing needs to change, you app needs to call the
+    UpdateMap() method. Since the drawing is stored in a bitmap, you
+    can also save the drawing to file by calling the
+    SaveToFile(self,file_name,file_type) method.
+    """
+
+    def __init__(self, parent, id,
+                 pos = wx.DefaultPosition,
+                 size = wx.DefaultSize,
+                 style=wx.NO_FULL_REPAINT_ON_RESIZE,
+                 Map=None, tree=None, lmgr=None):
+
+        MapWindow.__init__(self, parent, id, pos, size, style,
+                           Map, tree, lmgr)
+        wx.Window.__init__(self, parent, id, pos, size, style)
+
+        self.Map = Map
+        self.tree = tree
+        self.lmgr = lmgr    # Layer Manager
+        
+        #
+        # Flags
+        #
+        self.resize = False # indicates whether or not a resize event has taken place
+        self.dragimg = None # initialize variable for map panning
+
+        #
+        # Variable for drawing on DC
+        #
+        self.pen = None      # pen for drawing zoom boxes, etc.
+        self.polypen = None  # pen for drawing polylines (measurements, profiles, etc)
+        # List of wx.Point tuples defining a polyline (geographical coordinates)
+        self.polycoords = []
+        # ID of rubber band line
+        self.lineid = None
+        # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
+        self.plineid = None
+        
+        #
+        # Event bindings
+        #
+        self.Bind(wx.EVT_PAINT,        self.OnPaint)
+        self.Bind(wx.EVT_SIZE,         self.OnSize)
+        self.Bind(wx.EVT_IDLE,         self.OnIdle)
+        self.Bind(wx.EVT_MOTION,       self.MouseActions)
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
+        self.processMouse = True
+        
+        #
+        # Render output objects
+        #
+        self.mapfile = None   # image file to be rendered
+        self.img = ""         # wx.Image object (self.mapfile)
+        # used in digitization tool (do not redraw vector map)
+        self.imgVectorMap = None
+        # decoration overlays
+        self.overlays = {}
+        # images and their PseudoDC ID's for painting and dragging
+        self.imagedict = {}   
+        self.select = {}      # selecting/unselecting decorations for dragging
+        self.textdict = {}    # text, font, and color indexed by id
+        self.currtxtid = None # PseudoDC id for currently selected text
+
+        #
+        # Zoom objects
+        #
+        self.zoomhistory = [] # list of past zoom extents
+        self.currzoom = 0 # current set of extents in zoom history being used
+
+        self.zoomtype = 1   # 1 zoom in, 0 no zoom, -1 zoom out
+        self.hitradius = 10 # distance for selecting map decorations
+        self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
+
+        # OnSize called to make sure the buffer is initialized.
+        # This might result in OnSize getting called twice on some
+        # platforms at initialization, but little harm done.
+        ### self.OnSize(None)
+
+        self.DefinePseudoDC()
+        # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
+        self.redrawAll = True
+
+        # will store an off screen empty bitmap for saving to file
+        self._buffer = ''
+
+        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
+
+        # vars for handling mouse clicks
+        self.dragid   = -1
+        self.lastpos  = (0, 0)
+
+    def DefinePseudoDC(self, vdigit = False):
+        """!Define PseudoDC class to use
+
+        @vdigit True to use PseudoDC from vdigit
+        """
+        # create PseudoDC used for background map, map decorations like scales and legends
+        self.pdc = self.PseudoDC(vdigit)
+        # used for digitization tool
+        self.pdcVector = None
+        # decorations (region box, etc.)
+        self.pdcDec = self.PseudoDC(vdigit)
+        # pseudoDC for temporal objects (select box, measurement tool, etc.)
+        self.pdcTmp = self.PseudoDC(vdigit)
+        
+    def PseudoDC(self, vdigit = False):
+        """!Create PseudoDC instance"""
+        if vdigit:
+            PseudoDC = VDigitPseudoDC
+        else:
+            PseudoDC = wx.PseudoDC
+        
+        return PseudoDC()
+    
+    def CheckPseudoDC(self):
+        """!Try to draw background
+        
+        @return True on success
+        @return False on failure
+        """
+        try:
+            self.pdc.BeginDrawing()
+            self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+            self.pdc.BeginDrawing()
+        except:
+            return False
+        
+        return True
+    
+    def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0, 0, 0, 0]):
+        """!
+        Draws map and overlay decorations
+        """
+        if drawid == None:
+            if pdctype == 'image' and img:
+                drawid = self.imagedict[img]
+            elif pdctype == 'clear':
+                drawid == None
+            else:
+                drawid = wx.NewId()
+        
+        if img and pdctype == 'image':
+            # self.imagedict[img]['coords'] = coords
+            self.select[self.imagedict[img]['id']] = False # ?
+
+        pdc.BeginDrawing()
+
+        if drawid != 99:
+            bg = wx.TRANSPARENT_BRUSH
+        else:
+            bg = wx.Brush(self.GetBackgroundColour())
+
+        pdc.SetBackground(bg)
+        
+        ### pdc.Clear()
+
+        Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % \
+                       (drawid, pdctype, coords))
+
+        # set PseudoDC id
+        if drawid is not None:
+            pdc.SetId(drawid)
+        
+        if pdctype == 'clear': # erase the display
+            bg = wx.WHITE_BRUSH
+            # bg = wx.Brush(self.GetBackgroundColour())
+            pdc.SetBackground(bg)
+            pdc.RemoveAll()
+            pdc.Clear()
+            pdc.EndDrawing()
+            
+            self.Refresh()
+            return
+
+        if pdctype == 'image': # draw selected image
+            bitmap = wx.BitmapFromImage(img)
+            w,h = bitmap.GetSize()
+            pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
+            pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
+
+        elif pdctype == 'box': # draw a box on top of the map
+            if self.pen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.pen)
+                x2 = max(coords[0],coords[2])
+                x1 = min(coords[0],coords[2])
+                y2 = max(coords[1],coords[3])
+                y1 = min(coords[1],coords[3])
+                rwidth = x2-x1
+                rheight = y2-y1
+                rect = wx.Rect(x1, y1, rwidth, rheight)
+                pdc.DrawRectangleRect(rect)
+                pdc.SetIdBounds(drawid, rect)
+                
+        elif pdctype == 'line': # draw a line on top of the map
+            if self.pen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.pen)
+                pdc.DrawLine(wx.Point(coords[0], coords[1]),
+                             wx.Point(coords[2], coords[3]))
+                pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
+                # self.ovlcoords[drawid] = coords
+
+        elif pdctype == 'polyline': # draw a polyline on top of the map
+            if self.polypen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.polypen)
+                ### pdc.DrawLines(coords)
+                if (len(coords) < 2):
+                    return
+                i = 1
+                while i < len(coords):
+                    pdc.DrawLinePoint(wx.Point(coords[i-1][0], coords[i-1][1]),
+                                      wx.Point(coords[i][0], coords[i][1]))
+                    i += 1
+
+                # get bounding rectangle for polyline
+                xlist = []
+                ylist = []
+                if len(coords) > 0:
+                    for point in coords:
+                        x,y = point
+                        xlist.append(x)
+                        ylist.append(y)
+                    x1=min(xlist)
+                    x2=max(xlist)
+                    y1=min(ylist)
+                    y2=max(ylist)
+                    pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
+                    # self.ovlcoords[drawid] = [x1,y1,x2,y2]
+
+        elif pdctype == 'point': # draw point
+            if self.pen:
+                pdc.SetPen(self.pen)
+                pdc.DrawPoint(coords[0], coords[1])
+                coordsBound = (coords[0] - 5,
+                               coords[1] - 5,
+                               coords[0] + 5,
+                               coords[1] + 5)
+                pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
+                # self.ovlcoords[drawid] = coords
+
+        elif pdctype == 'text': # draw text on top of map
+            if not img['active']:
+                return #only draw active text
+            if img.has_key('rotation'):
+                rotation = float(img['rotation'])
+            else:
+                rotation = 0.0
+            w, h = self.GetFullTextExtent(img['text'])[0:2]
+            pdc.SetFont(img['font'])
+            pdc.SetTextForeground(img['color'])
+            coords, w, h = self.TextBounds(img)
+            if rotation == 0:
+                pdc.DrawText(img['text'], coords[0], coords[1])
+            else:
+                pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
+            pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], w, h))
+            
+        pdc.EndDrawing()
+        
+        self.Refresh()
+        
+        return drawid
+
+    def TextBounds(self, textinfo):
+        """!
+        Return text boundary data
+
+        @param textinfo text metadata (text, font, color, rotation)
+        @param coords reference point
+        """
+        if textinfo.has_key('rotation'):
+            rotation = float(textinfo['rotation'])
+        else:
+            rotation = 0.0
+        
+        coords = textinfo['coords']
+        
+        Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
+                   (textinfo['text'], rotation))
+
+        self.Update()
+        ### self.Refresh()
+
+        self.SetFont(textinfo['font'])
+
+        w, h = self.GetTextExtent(textinfo['text'])
+
+        if rotation == 0:
+            coords[2], coords[3] = coords[0] + w, coords[1] + h
+            return coords, w, h
+
+        boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
+        boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
+        coords[2] = coords[0] + boxw
+        coords[3] = coords[1] + boxh
+        
+        return coords, boxw, boxh
+
+    def OnPaint(self, event):
+        """!
+        Draw PseudoDC's to buffered paint DC
+
+        self.pdc for background and decorations
+        self.pdcVector for vector map which is edited
+        self.pdcTmp for temporaly drawn objects (self.polycoords)
+
+        If self.redrawAll is False on self.pdcTmp content is re-drawn
+        """
+        Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
+
+        dc = wx.BufferedPaintDC(self, self.buffer)
+        
+        ### dc.SetBackground(wx.Brush("White"))
+        dc.Clear()
+
+        # use PrepareDC to set position correctly
+        self.PrepareDC(dc)
+
+        # create a clipping rect from our position and size
+        # and update region
+        rgn = self.GetUpdateRegion().GetBox()
+        dc.SetClippingRect(rgn)
+
+        switchDraw = False
+        if self.redrawAll is None:
+            self.redrawAll = True
+            switchDraw = True
+        
+        if self.redrawAll: # redraw pdc and pdcVector
+            # draw to the dc using the calculated clipping rect
+            self.pdc.DrawToDCClipped(dc, rgn)
+
+            # draw vector map layer
+            if self.pdcVector:
+                # decorate with GDDC (transparency)
+                try:
+                    gcdc = wx.GCDC(dc)
+                    self.pdcVector.DrawToDCClipped(gcdc, rgn)
+                except NotImplementedError, e:
+                    print >> sys.stderr, e
+                    self.pdcVector.DrawToDCClipped(dc, rgn)
+
+            self.bufferLast = None
+        else: # do not redraw pdc and pdcVector
+            if self.bufferLast is None:
+                # draw to the dc
+                self.pdc.DrawToDC(dc)
+
+                if self.pdcVector:
+                    # decorate with GDDC (transparency)
+                    try:
+                        gcdc = wx.GCDC(dc)
+                        self.pdcVector.DrawToDC(gcdc)
+                    except NotImplementedError, e:
+                        print >> sys.stderr, e
+                        self.pdcVector.DrawToDC(dc)
+                        
+                # store buffered image
+                # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
+                self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
+
+            pdcLast = self.PseudoDC(vdigit = False)
+            pdcLast.DrawBitmap(self.bufferLast, 0, 0, False)
+            pdcLast.DrawToDC(dc)
+
+        # draw decorations (e.g. region box)
+        try:
+            gcdc = wx.GCDC(dc)
+            self.pdcDec.DrawToDC(gcdc)
+        except NotImplementedError, e:
+            print >> sys.stderr, e
+            self.pdcDec.DrawToDC(dc)
+
+        # draw temporary object on the foreground
+        ### self.pdcTmp.DrawToDCClipped(dc, rgn)
+        self.pdcTmp.DrawToDC(dc)
+
+        if switchDraw:
+            self.redrawAll = False
+                
+    def OnSize(self, event):
+        """!
+        Scale map image so that it is
+        the same size as the Window
+        """
+        Debug.msg(3, "BufferedWindow.OnSize():")
+
+        # set size of the input image
+        self.Map.ChangeMapSize(self.GetClientSize())
+        # align extent based on center point and display resolution
+        # this causes that image is not resized when display windows is resized
+        # self.Map.AlignExtentFromDisplay()
+
+        # Make new off screen bitmap: this bitmap will always have the
+        # current drawing in it, so it can be used to save the image to
+        # a file, or whatever.
+        self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
+
+        # get the image to be rendered
+        self.img = self.GetImage()
+        
+        # update map display
+        if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
+            self.img = self.img.Scale(self.Map.width, self.Map.height)
+            if len(self.Map.GetListOfLayers()) > 0:
+                self.UpdateMap()
+
+        # re-render image on idle
+        self.resize = True
+
+        # reposition checkbox in statusbar
+        self.parent.StatusbarReposition()
+
+        # update statusbar
+        self.parent.StatusbarUpdate()
+
+    def OnIdle(self, event):
+        """!
+        Only re-render a composite map image from GRASS during
+        idle time instead of multiple times during resizing.
+        """
+        if self.resize:
+            self.UpdateMap(render=True)
+
+        event.Skip()
+
+    def SaveToFile(self, FileName, FileType):
+        """!
+        This draws the psuedo DC to a buffer that
+        can be saved to a file.
+        """
+        dc = wx.BufferedPaintDC(self, self.buffer)
+        self.pdc.DrawToDC(dc)
+        if self.pdcVector:
+            self.pdcVector.DrawToDC(dc)
+        self.buffer.SaveFile(FileName, FileType)
+
+    def GetOverlay(self):
+        """!
+        Converts rendered overlay files to wx.Image
+
+        Updates self.imagedict
+
+        @return list of images
+        """
+        imgs = []
+        for overlay in self.Map.GetListOfLayers(l_type="overlay", l_active=True):
+            if os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
+                img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
+                self.imagedict[img] = { 'id' : overlay.id,
+                                        'layer' : overlay }
+                imgs.append(img)
+
+        return imgs
+
+    def GetImage(self):
+        """!
+        Converts redered map files to wx.Image
+
+        Updates self.imagedict (id=99)
+
+        @return wx.Image instance (map composition)
+        """
+        imgId = 99
+        if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
+                os.path.getsize(self.Map.mapfile):
+            img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
+        else:
+            img = None
+
+        self.imagedict[img] = { 'id': imgId }
+
+        return img
+
+    def UpdateMap(self, render=True, renderVector=True):
+        """!
+        Updates the canvas anytime there is a change to the
+        underlaying images or to the geometry of the canvas.
+
+        @param render re-render map composition
+        @param renderVector re-render vector map layer enabled for editing (used for digitizer)
+        """
+        start = time.clock()
+        
+        self.resize = False
+
+        # if len(self.Map.GetListOfLayers()) == 0:
+        #    return False
+        
+        if self.img is None:
+            render = True
+
+        #
+        # initialize process bar (only on 'render')
+        #
+        if render is True or renderVector is True:
+            self.parent.statusbarWin['progress'].Show()
+            if self.parent.statusbarWin['progress'].GetRange() > 0:
+                self.parent.statusbarWin['progress'].SetValue(1)
+        
+        #
+        # render background image if needed
+        #
+        
+        # update layer dictionary if there has been a change in layers
+        if self.tree and self.tree.reorder == True:
+            self.tree.ReorderLayers()
+        
+        # reset flag for auto-rendering
+        if self.tree:
+            self.tree.rerender = False
+        
+        if render:
+            # update display size
+            self.Map.ChangeMapSize(self.GetClientSize())
+            if self.parent.statusbarWin['resolution'].IsChecked():
+                # use computation region resolution for rendering
+                windres = True
+            else:
+                windres = False
+            self.mapfile = self.Map.Render(force=True, mapWindow=self.parent,
+                                           windres=windres)
+        else:
+            self.mapfile = self.Map.Render(force=False, mapWindow=self.parent)
+        
+        self.img = self.GetImage() # id=99
+            
+        #
+        # clear pseudoDcs
+        #
+        for pdc in (self.pdc,
+                    self.pdcDec,
+                    self.pdcTmp):
+            pdc.Clear()
+            pdc.RemoveAll()
+        
+        #
+        # draw background map image to PseudoDC
+        #
+        if not self.img:
+            self.Draw(self.pdc, pdctype='clear')
+        else:
+            try:
+                id = self.imagedict[self.img]['id']
+            except:
+                return False
+
+            self.Draw(self.pdc, self.img, drawid=id)
+        
+        #
+        # render vector map layer
+        #
+        digitToolbar = self.parent.toolbars['vdigit']
+        if renderVector and digitToolbar and \
+                digitToolbar.GetLayer():
+            # set region
+            self.parent.digit.driver.UpdateRegion()
+            # re-calculate threshold for digitization tool
+            self.parent.digit.driver.GetThreshold()
+            # draw map
+            if self.pdcVector:
+                self.pdcVector.Clear()
+                self.pdcVector.RemoveAll()
+            try:
+                item = self.tree.FindItemByData('maplayer', digitToolbar.GetLayer())
+            except TypeError:
+                item = None
+            
+            if item and self.tree.IsItemChecked(item):
+                self.parent.digit.driver.DrawMap()
+
+            # translate tmp objects (pointer position)
+            if digitToolbar.GetAction() == 'moveLine':
+                if  hasattr(self, "vdigitMove") and \
+                        self.vdigitMove.has_key('beginDiff'):
+                    # move line
+                    for id in self.vdigitMove['id']:
+                        self.pdcTmp.TranslateId(id,
+                                                self.vdigitMove['beginDiff'][0],
+                                                self.vdigitMove['beginDiff'][1])
+                    del self.vdigitMove['beginDiff']
+        
+        #
+        # render overlays
+        #
+        for img in self.GetOverlay():
+            # draw any active and defined overlays
+            if self.imagedict[img]['layer'].IsActive():
+                id = self.imagedict[img]['id']
+                self.Draw(self.pdc, img=img, drawid=id,
+                          pdctype=self.overlays[id]['pdcType'], coords=self.overlays[id]['coords'])
+
+        for id in self.textdict.keys():
+            self.Draw(self.pdc, img=self.textdict[id], drawid=id,
+                      pdctype='text', coords=[10, 10, 10, 10])
+        
+        # optionally draw computational extent box
+        self.DrawCompRegionExtent()
+        
+        #
+        # redraw pdcTmp if needed
+        #
+        if len(self.polycoords) > 0:
+            self.DrawLines(self.pdcTmp)
+        
+        if not self.parent.IsStandalone() and \
+                self.parent.GetLayerManager().georectifying:
+            # -> georectifier (redraw GCPs)
+            if self.parent.toolbars['georect']:
+                coordtype = 'gcpcoord'
+            else:
+                coordtype = 'mapcoord'
+            self.parent.GetLayerManager().georectifying.DrawGCP(coordtype)
+            
+        # 
+        # clear measurement
+        #
+        if self.mouse["use"] == "measure":
+            self.ClearLines(pdc=self.pdcTmp)
+            self.polycoords = []
+            self.mouse['use'] = 'pointer'
+            self.mouse['box'] = 'point'
+            self.mouse['end'] = [0, 0]
+            self.SetCursor(self.parent.cursors["default"])
+            
+        stop = time.clock()
+        
+        #
+        # hide process bar
+        #
+        self.parent.statusbarWin['progress'].Hide()
+
+        #
+        # update statusbar 
+        #
+        ### self.Map.SetRegion()
+        self.parent.StatusbarUpdate()
+        if grass.find_file(name = 'MASK', element = 'cell')['name']:
+            # mask found
+            self.parent.statusbarWin['mask'].SetLabel(_('MASK'))
+        else:
+            self.parent.statusbarWin['mask'].SetLabel('')
+        
+        Debug.msg (2, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
+                   (render, renderVector, (stop-start)))
+        
+        return True
+
+    def DrawCompRegionExtent(self):
+        """!
+        Draw computational region extent in the display
+        
+        Display region is drawn as a blue box inside the computational region,
+        computational region inside a display region as a red box).
+        """
+        if hasattr(self, "regionCoords"):
+            compReg = self.Map.GetRegion()
+            dispReg = self.Map.GetCurrentRegion()
+            reg = None
+            if self.IsInRegion(dispReg, compReg):
+                self.polypen = wx.Pen(colour=wx.Colour(0, 0, 255, 128), width=3, style=wx.SOLID)
+                reg = dispReg
+            else:
+                self.polypen = wx.Pen(colour=wx.Colour(255, 0, 0, 128),
+                                      width=3, style=wx.SOLID)
+                reg = compReg
+            
+            self.regionCoords = []
+            self.regionCoords.append((reg['w'], reg['n']))
+            self.regionCoords.append((reg['e'], reg['n']))
+            self.regionCoords.append((reg['e'], reg['s']))
+            self.regionCoords.append((reg['w'], reg['s']))
+            self.regionCoords.append((reg['w'], reg['n']))
+            # draw region extent
+            self.DrawLines(pdc=self.pdcDec, polycoords=self.regionCoords)
+
+    def IsInRegion(self, region, refRegion):
+        """!
+        Test if 'region' is inside of 'refRegion'
+
+        @param region input region
+        @param refRegion reference region (e.g. computational region)
+
+        @return True if region is inside of refRegion
+        @return False 
+        """
+        if region['s'] >= refRegion['s'] and \
+                region['n'] <= refRegion['n'] and \
+                region['w'] >= refRegion['w'] and \
+                region['e'] <= refRegion['e']:
+            return True
+
+        return False
+
+    def EraseMap(self):
+        """!
+        Erase the canvas
+        """
+        self.Draw(self.pdc, pdctype='clear')
+                  
+        if self.pdcVector:
+            self.Draw(self.pdcVector, pdctype='clear')
+        
+        self.Draw(self.pdcDec, pdctype='clear')
+        self.Draw(self.pdcTmp, pdctype='clear')
+        
+    def DragMap(self, moveto):
+        """!
+        Drag the entire map image for panning.
+        """
+
+        dc = wx.BufferedDC(wx.ClientDC(self))
+        dc.SetBackground(wx.Brush("White"))
+        dc.Clear()
+
+        self.dragimg = wx.DragImage(self.buffer)
+        self.dragimg.BeginDrag((0, 0), self)
+        self.dragimg.GetImageRect(moveto)
+        self.dragimg.Move(moveto)
+
+        self.dragimg.DoDrawImage(dc, moveto)
+        self.dragimg.EndDrag()
+
+        return True
+
+    def DragItem(self, id, event):
+        """!
+        Drag an overlay decoration item
+        """
+        if id == 99 or id == '' or id == None: return
+        Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
+        x, y = self.lastpos
+        dx = event.GetX() - x
+        dy = event.GetY() - y
+        self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+        r = self.pdc.GetIdBounds(id)
+        ### FIXME in vdigit/pseudodc.i
+        if type(r) is list:
+            r = wx.Rect(r[0], r[1], r[2], r[3])
+        if id > 100: # text dragging
+            rtop = (r[0],r[1]-r[3],r[2],r[3])
+            r = r.Union(rtop)
+            rleft = (r[0]-r[2],r[1],r[2],r[3])
+            r = r.Union(rleft)
+        self.pdc.TranslateId(id, dx, dy)
+
+        r2 = self.pdc.GetIdBounds(id)
+        if type(r2) is list:
+            r2 = wx.Rect(r[0], r[1], r[2], r[3])
+        if id > 100: # text
+            self.textdict[id]['coords'] = r2
+        r = r.Union(r2)
+        r.Inflate(4,4)
+        self.RefreshRect(r, False)
+        self.lastpos = (event.GetX(), event.GetY())
+                
+    def MouseDraw(self, pdc=None, begin=None, end=None):
+        """!
+        Mouse box or line from 'begin' to 'end'
+
+        If not given from self.mouse['begin'] to self.mouse['end'].
+
+        """
+        self.redrawAll = False
+        
+        if not pdc:
+            return
+
+        if begin is None:
+            begin = self.mouse['begin']
+        if end is None:
+            end   = self.mouse['end']
+
+        Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
+                       (self.mouse['use'], self.mouse['box'],
+                        begin[0], begin[1], end[0], end[1]))
+
+        if self.mouse['box'] == "box":
+            boxid = wx.ID_NEW
+            mousecoords = [begin[0], begin[1],
+                           end[0], end[1]]
+            r = pdc.GetIdBounds(boxid)
+            if type(r) is list:
+                r = wx.Rect(r[0], r[1], r[2], r[3])
+            r.Inflate(4, 4)
+            try:
+                pdc.ClearId(boxid)
+            except:
+                pass
+            self.RefreshRect(r, False)
+            pdc.SetId(boxid)
+            self.Draw(pdc, drawid=boxid, pdctype='box', coords=mousecoords)
+        elif self.mouse['box'] == "line" or self.mouse['box'] == 'point':
+            self.lineid = wx.ID_NEW
+            mousecoords = [begin[0], begin[1], \
+                           end[0], end[1]]
+            x1=min(begin[0],end[0])
+            x2=max(begin[0],end[0])
+            y1=min(begin[1],end[1])
+            y2=max(begin[1],end[1])
+            r = wx.Rect(x1,y1,x2-x1,y2-y1)
+            r.Inflate(4,4)
+            try:
+                pdc.ClearId(self.lineid)
+            except:
+                pass
+            self.RefreshRect(r, False)
+            pdc.SetId(self.lineid)
+            self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=mousecoords)
+
+    def DrawLines(self, pdc=None, polycoords=None):
+        """!
+        Draw polyline in PseudoDC
+
+        Set self.pline to wx.NEW_ID + 1
+
+        polycoords - list of polyline vertices, geographical coordinates
+        (if not given, self.polycoords is used)
+
+        """
+        if not pdc:
+            pdc = self.pdcTmp
+
+        if not polycoords:
+            polycoords = self.polycoords
+        
+        if len(polycoords) > 0:
+            self.plineid = wx.ID_NEW + 1
+            # convert from EN to XY
+            coords = []
+            for p in polycoords:
+                coords.append(self.Cell2Pixel(p))
+
+            self.Draw(pdc, drawid=self.plineid, pdctype='polyline', coords=coords)
+            
+            Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
+                           (coords, self.plineid))
+
+            return self.plineid
+
+        return -1
+
+    def DrawCross(self, pdc, coords, size, rotation=0,
+                  text=None, textAlign='lr', textOffset=(5, 5)):
+        """!Draw cross in PseudoDC
+
+        @todo implement rotation
+
+        @param pdc PseudoDC
+        @param coord center coordinates
+        @param rotation rotate symbol
+        @param text draw also text (text, font, color, rotation)
+        @param textAlign alignment (default 'lower-right')
+        @textOffset offset for text (from center point)
+        """
+        Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
+                  (pdc, coords, size))
+        coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
+                       (coords[0], coords[1] - size, coords[0], coords[1] + size))
+
+        self.lineid = wx.NewId()
+        for lineCoords in coordsCross:
+            self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=lineCoords)
+
+        if not text:
+            return self.lineid
+
+        if textAlign == 'ul':
+            coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
+        elif textAlign == 'ur':
+            coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
+        elif textAlign == 'lr':
+            coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
+        else:
+            coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
+        
+        self.Draw(pdc, img=text,
+                  pdctype='text', coords=coord)
+
+        return self.lineid
+
+    def MouseActions(self, event):
+        """!
+        Mouse motion and button click notifier
+        """
+        if not self.processMouse:
+            return
+        
+        ### if self.redrawAll is False:
+        ###    self.redrawAll = True
+        
+        # zoom with mouse wheel
+        if event.GetWheelRotation() != 0:
+            self.OnMouseWheel(event)
+            
+        # left mouse button pressed
+        elif event.LeftDown():
+            self.OnLeftDown(event)
+
+        # left mouse button released
+        elif event.LeftUp():
+            self.OnLeftUp(event)
+
+        # dragging
+        elif event.Dragging():
+            self.OnDragging(event)
+
+        # double click
+        elif event.ButtonDClick():
+            self.OnButtonDClick(event)
+
+        # middle mouse button pressed
+        elif event.MiddleDown():
+            self.OnMiddleDown(event)
+
+        # right mouse button pressed
+        elif event.RightDown():
+            self.OnRightDown(event)
+
+        # right mouse button released
+        elif event.RightUp():
+            self.OnRightUp(event)
+
+        elif event.Moving():
+            self.OnMouseMoving(event)
+
+#        event.Skip()
+        
+    def OnMouseWheel(self, event):
+        """!
+        Mouse wheel moved
+        """
+        self.processMouse = False
+        current  = event.GetPositionTuple()[:]
+        wheel = event.GetWheelRotation()
+        Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
+        # zoom 1/2 of the screen, centered to current mouse position (TODO: settings)
+        begin = (current[0] - self.Map.width / 4,
+                 current[1] - self.Map.height / 4)
+        end   = (current[0] + self.Map.width / 4,
+                 current[1] + self.Map.height / 4)
+
+        if wheel > 0:
+            zoomtype = 1
+        else:
+            zoomtype = -1
+
+        # zoom
+        self.Zoom(begin, end, zoomtype)
+
+        # redraw map
+        self.UpdateMap()
+
+        ### self.OnPaint(None)
+
+        # update statusbar
+        self.parent.StatusbarUpdate()
+
+        self.Refresh()
+        self.processMouse = True
+#        event.Skip()
+
+    def OnDragging(self, event):
+        """!
+        Mouse dragging with left button down
+        """
+        Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
+        current  = event.GetPositionTuple()[:]
+        previous = self.mouse['begin']
+        move = (current[0] - previous[0],
+                current[1] - previous[1])
+
+        digitToolbar = self.parent.toolbars['vdigit']
+
+        # dragging or drawing box with left button
+        if self.mouse['use'] == 'pan':
+            self.DragMap(move)
+
+        # dragging decoration overlay item
+        elif (self.mouse['use'] == 'pointer' and 
+                not digitToolbar and 
+                self.dragid != None):
+            self.DragItem(self.dragid, event)
+
+        # dragging anything else - rubber band box or line
+        else:
+            if (self.mouse['use'] == 'pointer' and 
+                not digitToolbar): return
+            self.mouse['end'] = event.GetPositionTuple()[:]
+            digitClass = self.parent.digit
+            if (event.LeftIsDown() and 
+                not (digitToolbar and 
+                    digitToolbar.GetAction() in ("moveLine",) and 
+                    digitClass.driver.GetSelected() > 0)):
+                # draw box only when left mouse button is pressed
+                self.MouseDraw(pdc=self.pdcTmp)
+      
+#        event.Skip()
+
+    def OnLeftDownVDigitAddLine(self, event):
+        """!
+        Left mouse button down - vector digitizer add new line
+        action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        try:
+            mapLayer = digitToolbar.GetLayer().GetName()
+        except:
+            return
+        
+        if digitToolbar.GetAction('type') in ["point", "centroid"]:
+            # add new point
+            if digitToolbar.GetAction('type') == 'point':
+                point = True
+            else:
+                point = False
+
+            east, north = self.Pixel2Cell(self.mouse['begin'])
+            fid = digitClass.AddPoint(mapLayer, point, east, north)
+            if fid < 0:
+                return
+
+            self.UpdateMap(render=False) # redraw map
+            
+            # add new record into atribute table
+            if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled')  is True:
+                # select attributes based on layer and category
+                cats = { fid : {
+                        UserSettings.Get(group='vdigit', key="layer", subkey='value') :
+                            (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
+                        }}
+                
+                posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                                 self.mouse['end'][1] + self.dialogOffset))
+                
+                addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent=self, map=mapLayer,
+                                                                   cats=cats,
+                                                                   pos=posWindow,
+                                                                   action="add")
+
+                if not point:
+                    self.__geomAttrb(fid, addRecordDlg, 'area', digitClass,
+                                     digitToolbar.GetLayer())
+                    self.__geomAttrb(fid, addRecordDlg, 'perimeter', digitClass,
+                                     digitToolbar.GetLayer())
+
+                if addRecordDlg.mapDBInfo and \
+                        addRecordDlg.ShowModal() == wx.ID_OK:
+                    sqlfile = tempfile.NamedTemporaryFile(mode="w")
+                    for sql in addRecordDlg.GetSQLString():
+                        sqlfile.file.write(sql + ";\n")
+                    sqlfile.file.flush()
+                    
+                    gcmd.RunCommand('db.execute',
+                                    parent = self,
+                                    quiet = True, 
+                                    input = sqlfile.name)
+                
+                if addRecordDlg.mapDBInfo:
+                    self.__updateATM()
+        
+        elif digitToolbar.GetAction('type') in ["line", "boundary"]:
+            # add new point to the line
+            self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
+            self.DrawLines(pdc=self.pdcTmp)
+    
+    def __geomAttrb(self, fid, dialog, attrb, digit, mapLayer):
+        """!Trac geometry attributes?"""
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        vdigit = self.tree.GetPyData(item)[0]['vdigit']
+        if vdigit and \
+                vdigit.has_key('geomAttr') and \
+                vdigit['geomAttr'].has_key(attrb):
+            val = -1
+            if attrb == 'length':
+                val = digit.GetLineLength(fid)
+                type = attrb
+            elif attrb == 'area':
+                val = digit.GetAreaSize(fid)
+                type = attrb
+            elif attrb == 'perimeter':
+                val = digit.GetAreaPerimeter(fid)
+                type = 'length'
+            
+            if val > 0:
+                layer = int(UserSettings.Get(group='vdigit', key="layer", subkey='value'))
+                column = vdigit['geomAttr'][attrb]['column']
+                val = UnitsConvertValue(val, type, vdigit['geomAttr'][attrb]['units'])
+                dialog.SetColumnValue(layer, column, val)
+                dialog.OnReset()
+        
+    def __geomAttrbUpdate(self, fids):
+        """!Update geometry atrributes of currently selected features
+
+        @param fid list feature id
+        """
+        mapLayer = self.parent.toolbars['vdigit'].GetLayer()
+        vectorName =  mapLayer.GetName()
+        digit = self.parent.digit
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        vdigit = self.tree.GetPyData(item)[0]['vdigit']
+        
+        if vdigit is None or not vdigit.has_key('geomAttr'):
+            return
+        
+        dbInfo = gselect.VectorDBInfo(vectorName)
+        sqlfile = tempfile.NamedTemporaryFile(mode="w")
+        for fid in fids:
+            for layer, cats in digit.GetLineCats(fid).iteritems():
+                table = dbInfo.GetTable(layer)
+                for attrb, item in vdigit['geomAttr'].iteritems():
+                    val = -1
+                    if attrb == 'length':
+                        val = digit.GetLineLength(fid)
+                        type = attrb
+                    elif attrb == 'area':
+                        val = digit.GetAreaSize(fid)
+                        type = attrb
+                    elif attrb == 'perimeter':
+                        val = digit.GetAreaPerimeter(fid)
+                        type = 'length'
+
+                    if val < 0:
+                        continue
+                    val = UnitsConvertValue(val, type, item['units'])
+                    
+                    for cat in cats:
+                        sqlfile.write('UPDATE %s SET %s = %f WHERE %s = %d;\n' % \
+                                          (table, item['column'], val,
+                                           dbInfo.GetKeyColumn(layer), cat))
+            sqlfile.file.flush()
+            gcmd.RunCommand('db.execute',
+                            parent = True,
+                            quiet = True,
+                            input = sqlfile.name)
+            
+    def __updateATM(self):
+        """!Update open Attribute Table Manager
+
+        @todo: use AddDataRow() instead
+        """
+        # update ATM
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitVector = digitToolbar.GetLayer().GetName()
+                            
+        for atm in self.lmgr.dialogs['atm']:
+            atmVector = atm.GetVectorName()
+            if atmVector == digitVector:
+                layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
+                # TODO: use AddDataRow instead
+                atm.LoadData(layer)
+        
+    def OnLeftDownVDigitEditLine(self, event):
+        """!
+        Left mouse button down - vector digitizer edit linear feature
+        - add new vertex.
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
+        self.vdigitMove['id'].append(wx.NewId())
+        self.DrawLines(pdc=self.pdcTmp)
+
+    def OnLeftDownVDigitMoveLine(self, event):
+        """!
+        Left mouse button down - vector digitizer move feature/vertex,
+        edit linear feature
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        self.vdigitMove = {}
+        # geographic coordinates of initial position (left-down)
+        self.vdigitMove['begin'] = None
+        # list of ids to modify    
+        self.vdigitMove['id'] = []
+        # ids geographic coordinates
+        self.vdigitMove['coord'] = {}
+                
+        if digitToolbar.GetAction() in ["moveVertex", "editLine"]:
+            # set pen
+            pcolor = UserSettings.Get(group='vdigit', key="symbol",
+                                      subkey=["highlight", "color"])
+            self.pen = self.polypen = wx.Pen(colour=pcolor,
+                                             width=2, style=wx.SHORT_DASH)
+            self.pdcTmp.SetPen(self.polypen)
+
+    def OnLeftDownVDigitDisplayCA(self, event):
+        """!
+        Left mouse button down - vector digitizer display categories
+        or attributes action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        try:
+            mapLayer = digitToolbar.GetLayer().GetName()
+        except:
+            return
+        
+        coords = self.Pixel2Cell(self.mouse['begin'])
+        
+        # unselect
+        digitClass.driver.SetSelected([])
+        
+        # select feature by point
+        cats = {}
+        if digitClass.driver.SelectLineByPoint(coords,
+                                               digitClass.GetSelectType()) is None:
+            return
+
+        if UserSettings.Get(group='vdigit', key='checkForDupl',
+                            subkey='enabled'):
+            lines = digitClass.driver.GetSelected()
+        else:
+            lines = (digitClass.driver.GetSelected()[0],) # only first found
+                        
+        for line in lines:
+            cats[line] = digitClass.GetLineCats(line)
+                   
+        posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                         self.mouse['end'][1] + self.dialogOffset))
+    
+        if digitToolbar.GetAction() == "displayAttrs":
+            # select attributes based on coordinates (all layers)
+            if self.parent.dialogs['attributes'] is None:
+                self.parent.dialogs['attributes'] = \
+                    dbm_dialogs.DisplayAttributesDialog(parent=self, map=mapLayer,
+                                                        cats=cats,
+                                                        action="update")
+            else:
+                # upgrade dialog
+                self.parent.dialogs['attributes'].UpdateDialog(cats=cats)
+
+            if self.parent.dialogs['attributes']:
+                if len(cats.keys()) > 0:
+                    # highlight feature & re-draw map
+                    if not self.parent.dialogs['attributes'].IsShown():
+                        self.parent.dialogs['attributes'].Show()
+                else:
+                    if self.parent.dialogs['attributes'] and \
+                            self.parent.dialogs['attributes'].IsShown():
+                        self.parent.dialogs['attributes'].Hide()
+
+        else: # displayCats
+            if self.parent.dialogs['category'] is None:
+                # open new dialog
+                dlg = VDigitCategoryDialog(parent=self,
+                                           map=mapLayer,
+                                           cats=cats,
+                                           pos=posWindow,
+                                           title=_("Update categories"))
+                self.parent.dialogs['category'] = dlg
+            else:
+                # update currently open dialog
+                self.parent.dialogs['category'].UpdateDialog(cats=cats)
+                            
+            if self.parent.dialogs['category']:
+                if len(cats.keys()) > 0:
+                    # highlight feature & re-draw map
+                    if not self.parent.dialogs['category'].IsShown():
+                        self.parent.dialogs['category'].Show()
+                else:
+                    if self.parent.dialogs['category'].IsShown():
+                        self.parent.dialogs['category'].Hide()
+                
+        self.UpdateMap(render=False)
+ 
+    def OnLeftDownVDigitCopyCA(self, event):
+        """!
+        Left mouse button down - vector digitizer copy categories
+        or attributes action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if not hasattr(self, "copyCatsList"):
+            self.copyCatsList = []
+        else:
+            self.copyCatsIds = []
+            self.mouse['box'] = 'box'
+        
+    def OnLeftDownVDigitCopyLine(self, event):
+        """!
+        Left mouse button down - vector digitizer copy lines action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if not hasattr(self, "copyIds"):
+            self.copyIds = []
+            self.layerTmp = None
+        
+    def OnLeftDownVDigitBulkLine(self, event):
+        """!
+        Left mouse button down - vector digitizer label 3d vector
+        lines
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if len(self.polycoords) > 1: # start new line
+            self.polycoords = []
+            self.ClearLines(pdc=self.pdcTmp)
+        self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
+        if len(self.polycoords) == 1:
+            begin = self.Pixel2Cell(self.polycoords[-1])
+            end   = self.Pixel2Cell(self.mouse['end'])
+        else:
+            end   = self.Pixel2Cell(self.polycoords[-1])
+            begin = self.Pixel2Cell(self.mouse['begin'])
+            
+            self.DrawLines(self.pdcTmp, polycoords = (begin, end))
+        
+    def OnLeftDown(self, event):
+        """!
+        Left mouse button pressed
+        """
+        Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
+                   self.mouse["use"])
+
+        self.mouse['begin'] = event.GetPositionTuple()[:]
+
+        if self.mouse["use"] in ["measure", "profile"]:
+            # measure or profile
+            if len(self.polycoords) == 0:
+                self.mouse['end'] = self.mouse['begin']
+                self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
+                self.ClearLines(pdc=self.pdcTmp)
+            else:
+                self.mouse['begin'] = self.mouse['end']
+        elif self.mouse['use'] == 'zoom':
+            pass
+
+        #
+        # vector digizer
+        #
+        elif self.mouse["use"] == "pointer" and \
+                self.parent.toolbars['vdigit']:
+            digitToolbar = self.parent.toolbars['vdigit']
+            digitClass   = self.parent.digit
+            
+            try:
+                mapLayer = digitToolbar.GetLayer().GetName()
+            except:
+                wx.MessageBox(parent=self,
+                              message=_("No vector map selected for editing."),
+                              caption=_("Vector digitizer"),
+                              style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+                event.Skip()
+                return
+            
+            if digitToolbar.GetAction() not in ("moveVertex",
+                                                "addVertex",
+                                                "removeVertex",
+                                                "editLine"):
+                # set pen
+                self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+                self.polypen = wx.Pen(colour='dark green', width=2, style=wx.SOLID)
+
+            if digitToolbar.GetAction() in ("addVertex",
+                                            "removeVertex",
+                                            "splitLines"):
+                # unselect
+                digitClass.driver.SetSelected([])
+
+            if digitToolbar.GetAction() == "addLine":
+                self.OnLeftDownVDigitAddLine(event)
+            
+            elif digitToolbar.GetAction() == "editLine" and \
+                    hasattr(self, "vdigitMove"):
+                self.OnLeftDownVDigitEditLine(event)
+
+            elif digitToolbar.GetAction() in ("moveLine", 
+                                              "moveVertex",
+                                              "editLine") and \
+                    not hasattr(self, "vdigitMove"):
+                self.OnLeftDownVDigitMoveLine(event)
+
+            elif digitToolbar.GetAction() in ("displayAttrs"
+                                              "displayCats"):
+                self.OnLeftDownVDigitDisplayCA(event)
+            
+            elif digitToolbar.GetAction() in ("copyCats",
+                                              "copyAttrs"):
+                self.OnLeftDownVDigitCopyCA(event)
+            
+            elif digitToolbar.GetAction() == "copyLine":
+                self.OnLeftDownVDigitCopyLine(event)
+            
+            elif digitToolbar.GetAction() == "zbulkLine":
+                self.OnLeftDownVDigitBulkLine(event)
+            
+        elif self.mouse['use'] == 'pointer':
+            # get decoration or text id
+            self.idlist = []
+            self.dragid = ''
+            self.lastpos = self.mouse['begin']
+            idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
+                                          self.hitradius)
+            if 99 in idlist:
+                idlist.remove(99)                             
+            if idlist != []:
+                self.dragid = idlist[0] #drag whatever is on top
+        else:
+            pass
+
+        event.Skip()
+
+    def OnLeftUpVDigitVarious(self, event):
+        """!
+        Left mouse button up - vector digitizer various actions
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        pos2 = self.Pixel2Cell(self.mouse['end'])
+        
+        nselected = 0
+        # -> delete line || move line || move vertex
+        if digitToolbar.GetAction() in ("moveVertex",
+                                        "editLine"):
+            if len(digitClass.driver.GetSelected()) == 0:
+                nselected = digitClass.driver.SelectLineByPoint(pos1, type=VDigit_Lines_Type)
+                
+                if digitToolbar.GetAction() == "editLine":
+                    try:
+                        selVertex = digitClass.driver.GetSelectedVertex(pos1)[0]
+                    except IndexError:
+                        selVertex = None
+                        
+                    if selVertex:
+                        # self.UpdateMap(render=False)
+                        ids = digitClass.driver.GetSelected(grassId=False)
+                        # move this line to tmp layer
+                        self.polycoords = []
+                        for id in ids:
+                            if id % 2: # register only vertices
+                                e, n = self.Pixel2Cell(self.pdcVector.GetIdBounds(id)[0:2])
+                                self.polycoords.append((e, n))
+                        digitClass.driver.DrawSelected(False) 
+                                
+                        if selVertex < ids[-1] / 2:
+                            # choose first or last node of line
+                            self.vdigitMove['id'].reverse()
+                            self.polycoords.reverse()
+                    else:
+                        # unselect
+                        digitClass.driver.SetSelected([])
+                        del self.vdigitMove
+                
+                    self.UpdateMap(render=False)
+            
+        elif digitToolbar.GetAction() in ("copyCats",
+                                          "copyAttrs"):
+            if not hasattr(self, "copyCatsIds"):
+                # 'from' -> select by point
+                nselected = digitClass.driver.SelectLineByPoint(pos1, digitClass.GetSelectType())
+                if nselected:
+                    self.copyCatsList = digitClass.driver.GetSelected()
+            else:
+                # -> 'to' -> select by bbox
+                digitClass.driver.SetSelected([])
+                # return number of selected features (by box/point)
+                nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                               digitClass.GetSelectType())
+                if nselected == 0:
+                    if digitClass.driver.SelectLineByPoint(pos1,
+                                                           digitClass.GetSelectType()) is not None:
+                        nselected = 1
+                        
+                if nselected > 0:
+                    self.copyCatsIds = digitClass.driver.GetSelected()
+
+        elif digitToolbar.GetAction() == "queryLine":
+            selected = digitClass.SelectLinesByQuery(pos1, pos2)
+            nselected = len(selected)
+            if nselected > 0:
+                digitClass.driver.SetSelected(selected)
+
+        else:
+            # -> moveLine || deleteLine, etc. (select by point/box)
+            if digitToolbar.GetAction() == 'moveLine' and \
+                    len(digitClass.driver.GetSelected()) > 0:
+                nselected = 0
+            else:
+                if digitToolbar.GetAction() == 'moveLine':
+                    drawSeg = True
+                else:
+                    drawSeg = False
+
+                nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                               digitClass.GetSelectType(),
+                                                               drawSeg)
+                    
+                if nselected == 0:
+                    if digitClass.driver.SelectLineByPoint(pos1,
+                                                           digitClass.GetSelectType()) is not None:
+                        nselected = 1
+        
+        if nselected > 0:
+            if digitToolbar.GetAction() in ("moveLine",
+                                            "moveVertex"):
+                # get pseudoDC id of objects which should be redrawn
+                if digitToolbar.GetAction() == "moveLine":
+                    # -> move line
+                    self.vdigitMove['id'] = digitClass.driver.GetSelected(grassId=False)
+                    self.vdigitMove['coord'] = digitClass.driver.GetSelectedCoord()
+                else: # moveVertex
+                    self.vdigitMove['id'] = digitClass.driver.GetSelectedVertex(pos1)
+                    if len(self.vdigitMove['id']) == 0: # no vertex found
+                        digitClass.driver.SetSelected([])
+                
+            #
+            # check for duplicates
+            #
+            if UserSettings.Get(group='vdigit', key='checkForDupl', subkey='enabled') is True:
+                dupl = digitClass.driver.GetDuplicates()
+                self.UpdateMap(render=False)
+                    
+                if dupl:
+                    posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                                     self.mouse['end'][1] + self.dialogOffset))
+                    
+                    dlg = VDigitDuplicatesDialog(parent=self, data=dupl, pos=posWindow)
+                    
+                    if dlg.ShowModal() == wx.ID_OK:
+                        digitClass.driver.UnSelect(dlg.GetUnSelected())
+                        # update selected
+                        self.UpdateMap(render=False)
+                
+            if digitToolbar.GetAction() != "editLine":
+                # -> move line || move vertex
+                self.UpdateMap(render=False)
+        
+        else: # no vector object found
+            if not (digitToolbar.GetAction() in ("moveLine",
+                                                 "moveVertex") and \
+                        len(self.vdigitMove['id']) > 0):
+                # avoid left-click when features are already selected
+                self.UpdateMap(render=False, renderVector=False)
+        
+    def OnLeftUpVDigitModifyLine(self, event):
+        """!
+        Left mouse button up - vector digitizer split line, add/remove
+        vertex action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        
+        pointOnLine = digitClass.driver.SelectLineByPoint(pos1,
+                                                          type=VDigit_Lines_Type)
+
+        if not pointOnLine:
+            return
+
+        if digitToolbar.GetAction() in ["splitLine", "addVertex"]:
+            self.UpdateMap(render=False) # highlight object
+            self.DrawCross(pdc=self.pdcTmp, coords=self.Cell2Pixel(pointOnLine),
+                           size=5)
+        else: # removeVertex
+            # get only id of vertex
+            try:
+                id = digitClass.driver.GetSelectedVertex(pos1)[0]
+            except IndexError:
+                id = None
+
+            if id:
+                x, y = self.pdcVector.GetIdBounds(id)[0:2]
+                self.pdcVector.RemoveId(id)
+                self.UpdateMap(render=False) # highlight object
+                self.DrawCross(pdc=self.pdcTmp, coords=(x, y),
+                               size=5)
+            else:
+                # unselect
+                digitClass.driver.SetSelected([])
+                self.UpdateMap(render=False)
+
+    def OnLeftUpVDigitCopyLine(self, event):
+        """!
+        Left mouse button up - vector digitizer copy feature action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        pos2 = self.Pixel2Cell(self.mouse['end'])
+        
+        if UserSettings.Get(group='vdigit', key='bgmap',
+                            subkey='value', internal=True) == '':
+            # no background map -> copy from current vector map layer
+            nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                           digitClass.GetSelectType())
+
+            if nselected > 0:
+                # highlight selected features
+                self.UpdateMap(render=False)
+            else:
+                self.UpdateMap(render=False, renderVector=False)
+        else:
+            # copy features from background map
+            self.copyIds += digitClass.SelectLinesFromBackgroundMap(pos1, pos2)
+            if len(self.copyIds) > 0:
+                color = UserSettings.Get(group='vdigit', key='symbol',
+                                         subkey=['highlight', 'color'])
+                colorStr = str(color[0]) + ":" + \
+                    str(color[1]) + ":" + \
+                    str(color[2])
+                dVectTmp = ['d.vect',
+                            'map=%s' % UserSettings.Get(group='vdigit', key='bgmap',
+                                                        subkey='value', internal=True),
+                            'cats=%s' % utils.ListOfCatsToRange(self.copyIds),
+                            '-i',
+                            'color=%s' % colorStr,
+                            'fcolor=%s' % colorStr,
+                            'type=point,line,boundary,centroid',
+                            'width=2']
+                        
+                if not self.layerTmp:
+                    self.layerTmp = self.Map.AddLayer(type='vector',
+                                                      name=globalvar.QUERYLAYER,
+                                                      command=dVectTmp)
+                else:
+                    self.layerTmp.SetCmd(dVectTmp)
+                
+                self.UpdateMap(render=True, renderVector=False)
+            else:
+                self.UpdateMap(render=False, renderVector=False)
+            
+            self.redrawAll = None
+            
+    def OnLeftUpVDigitBulkLine(self, event):
+        """!
+        Left mouse button up - vector digitizer z-bulk line action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        # select lines to be labeled
+        pos1 = self.polycoords[0]
+        pos2 = self.polycoords[1]
+        nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
+                                                       digitClass.GetSelectType())
+
+        if nselected > 0:
+            # highlight selected features
+            self.UpdateMap(render=False)
+            self.DrawLines(pdc=self.pdcTmp) # redraw temp line
+        else:
+            self.UpdateMap(render=False, renderVector=False)
+
+    def OnLeftUpVDigitConnectLine(self, event):
+        """!
+        Left mouse button up - vector digitizer connect line action
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        digitClass   = self.parent.digit
+        
+        if len(digitClass.driver.GetSelected()) > 0:
+            self.UpdateMap(render=False)
+        
+    def OnLeftUp(self, event):
+        """!
+        Left mouse button released
+        """
+        Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
+                   self.mouse["use"])
+
+        self.mouse['end'] = event.GetPositionTuple()[:]
+
+        if self.mouse['use'] in ["zoom", "pan"]:
+            # set region in zoom or pan
+            begin = self.mouse['begin']
+            end = self.mouse['end']
+            if self.mouse['use'] == 'zoom':
+                # set region for click (zero-width box)
+                if begin[0] - end[0] == 0 or \
+                        begin[1] - end[1] == 0:
+                    # zoom 1/2 of the screen (TODO: settings)
+                    begin = (end[0] - self.Map.width / 4,
+                             end[1] - self.Map.height / 4)
+                    end   = (end[0] + self.Map.width / 4,
+                             end[1] + self.Map.height / 4)
+
+            self.Zoom(begin, end, self.zoomtype)
+
+            # redraw map
+            self.UpdateMap(render=True)
+
+            # update statusbar
+            self.parent.StatusbarUpdate()
+
+        elif self.mouse["use"] == "query":
+            # querying
+            self.parent.QueryMap(self.mouse['begin'][0],self.mouse['begin'][1])
+
+        elif self.mouse["use"] == "queryVector":
+            # editable mode for vector map layers
+            self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
+
+            # clear temp canvas
+            self.UpdateMap(render=False, renderVector=False)
+            
+        elif self.mouse["use"] in ["measure", "profile"]:
+            # measure or profile
+            if self.mouse["use"] == "measure":
+                self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
+
+            self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
+            self.ClearLines(pdc=self.pdcTmp)
+            self.DrawLines(pdc=self.pdcTmp)
+        
+        elif self.mouse["use"] == "pointer" and \
+                self.parent.GetLayerManager().georectifying:
+            # -> georectifying
+            coord = self.Pixel2Cell(self.mouse['end'])
+            if self.parent.toolbars['georect']:
+                coordtype = 'gcpcoord'
+            else:
+                coordtype = 'mapcoord'
+
+            self.parent.GetLayerManager().georectifying.SetGCPData(coordtype, coord, self)
+            self.UpdateMap(render=False, renderVector=False)
+
+        elif self.mouse["use"] == "pointer" and self.parent.toolbars['vdigit']:
+            # digitization tool active
+            digitToolbar = self.parent.toolbars['vdigit']
+            digitClass   = self.parent.digit
+            
+            if hasattr(self, "vdigitMove"):
+                if len(digitClass.driver.GetSelected()) == 0:
+                    self.vdigitMove['begin'] = self.Pixel2Cell(self.mouse['begin']) # left down
+                
+                # eliminate initial mouse moving efect
+                self.mouse['begin'] = self.mouse['end'] 
+
+            if digitToolbar.GetAction() in ("deleteLine",
+                                            "moveLine",
+                                            "moveVertex",
+                                            "copyCats",
+                                            "copyAttrs",
+                                            "editLine",
+                                            "flipLine",
+                                            "mergeLine",
+                                            "snapLine",
+                                            "queryLine",
+                                            "breakLine",
+                                            "typeConv",
+                                            "connectLine"):
+                self.OnLeftUpVDigitVarious(event)
+
+            elif digitToolbar.GetAction() in ("splitLine",
+                                              "addVertex",
+                                              "removeVertex"):
+                self.OnLeftUpVDigitModifyLine(event)
+
+            elif digitToolbar.GetAction() == "copyLine":
+                self.OnLeftUpVDigitCopyLine(event)
+            
+            elif digitToolbar.GetAction() == "zbulkLine" and \
+                    len(self.polycoords) == 2:
+                self.OnLeftUpVDigitBulkLine(event)
+            
+            elif digitToolbar.GetAction() == "connectLine":
+                self.OnLeftUpConnectLine(event)
+            
+            if len(digitClass.driver.GetSelected()) > 0:
+                self.redrawAll = None
+            
+        elif (self.mouse['use'] == 'pointer' and 
+                self.dragid >= 0):
+            # end drag of overlay decoration
+            
+            if self.dragid < 99 and self.overlays.has_key(self.dragid):
+                self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
+            elif self.dragid > 100 and self.textdict.has_key(self.dragid):
+                self.textdict[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
+            else:
+                pass
+            self.dragid = None
+            self.currtxtid = None
+        
+    def OnButtonDClick(self, event):
+        """!
+        Mouse button double click
+        """
+        Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
+                   self.mouse["use"])
+
+        if self.mouse["use"] == "measure":
+            # measure
+            self.ClearLines(pdc=self.pdcTmp)
+            self.polycoords = []
+            self.mouse['use'] = 'pointer'
+            self.mouse['box'] = 'point'
+            self.mouse['end'] = [0, 0]
+            self.Refresh()
+            self.SetCursor(self.parent.cursors["default"])
+
+        elif self.mouse["use"] == "profile":
+            pass
+
+        elif self.mouse['use'] == 'pointer' and \
+                self.parent.toolbars['vdigit']:
+            # vector digitizer
+            pass
+
+        else:
+            # select overlay decoration options dialog
+            clickposition = event.GetPositionTuple()[:]
+            idlist  = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
+            if idlist == []:
+                return
+            self.dragid = idlist[0]
+
+            # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
+            if self.dragid > 100:
+                self.currtxtid = self.dragid
+                self.parent.OnAddText(None)
+            elif self.dragid == 0:
+                self.parent.OnAddBarscale(None)
+            elif self.dragid == 1:
+                self.parent.OnAddLegend(None)
+        
+    def OnRightDown(self, event):
+        """!
+        Right mouse button pressed
+        """
+        Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
+                   self.mouse["use"])
+                   
+        digitToolbar = self.parent.toolbars['vdigit']
+        if digitToolbar:
+            digitClass = self.parent.digit
+            # digitization tool (confirm action)
+            if digitToolbar.GetAction() in ("moveLine",
+                                            "moveVertex") and \
+                    hasattr(self, "vdigitMove"):
+
+                pFrom = self.vdigitMove['begin']
+                pTo = self.Pixel2Cell(event.GetPositionTuple())
+                
+                move = (pTo[0] - pFrom[0],
+                        pTo[1] - pFrom[1])
+                
+                if digitToolbar.GetAction() == "moveLine":
+                    # move line
+                    if digitClass.MoveSelectedLines(move) < 0:
+                        return
+                elif digitToolbar.GetAction() == "moveVertex":
+                    # move vertex
+                    fid = digitClass.MoveSelectedVertex(pFrom, move)
+                    if fid < 0:
+                        return
+
+                    self.__geomAttrbUpdate([fid,])
+                
+                del self.vdigitMove
+                
+        event.Skip()
+
+    def OnRightUp(self, event):
+        """!
+        Right mouse button released
+        """
+        Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
+                   self.mouse["use"])
+
+        digitToolbar = self.parent.toolbars['vdigit']
+        if digitToolbar:
+            digitClass = self.parent.digit
+            # digitization tool (confirm action)
+            if digitToolbar.GetAction() == "addLine" and \
+                    digitToolbar.GetAction('type') in ["line", "boundary"]:
+                # -> add new line / boundary
+                try:
+                    map = digitToolbar.GetLayer().GetName()
+                except:
+                    map = None
+                    wx.MessageBox(parent=self,
+                                  message=_("No vector map selected for editing."),
+                                  caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                    
+                if map:
+                    # mapcoords = []
+                    # xy -> EN
+                    # for coord in self.polycoords:
+                    #    mapcoords.append(self.Pixel2Cell(coord))
+                    if digitToolbar.GetAction('type') == 'line':
+                        line = True
+                    else:
+                        line = False
+
+                    if len(self.polycoords) < 2: # ignore 'one-point' lines
+                        return
+                    
+                    fid = digitClass.AddLine(map, line, self.polycoords)
+                    if fid < 0:
+                        return
+                    
+                    position = self.Cell2Pixel(self.polycoords[-1])
+                    self.polycoords = []
+                    self.UpdateMap(render=False)
+                    self.redrawAll = True
+                    self.Refresh()
+                    
+                    # add new record into atribute table
+                    if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled') and \
+                            (line is True or \
+                                 (not line and fid > 0)):
+                        posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
+                                                         position[1] + self.dialogOffset))
+
+                        # select attributes based on layer and category
+                        cats = { fid : {
+                                UserSettings.Get(group='vdigit', key="layer", subkey='value') :
+                                    (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
+                                }}
+                        
+                        addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent=self, map=map,
+                                                                           cats=cats,
+                                                                           pos=posWindow,
+                                                                           action="add")
+
+                        self.__geomAttrb(fid, addRecordDlg, 'length', digitClass,
+                                         digitToolbar.GetLayer())
+                        # auto-placing centroid
+                        self.__geomAttrb(fid, addRecordDlg, 'area', digitClass,
+                                         digitToolbar.GetLayer())
+                        self.__geomAttrb(fid, addRecordDlg, 'perimeter', digitClass,
+                                         digitToolbar.GetLayer())
+
+                        if addRecordDlg.mapDBInfo and \
+                               addRecordDlg.ShowModal() == wx.ID_OK:
+                            sqlfile = tempfile.NamedTemporaryFile(mode="w")
+                            for sql in addRecordDlg.GetSQLString():
+                                sqlfile.file.write(sql + ";\n")
+                            sqlfile.file.flush()
+                            gcmd.RunCommand('db.execute',
+                                            parent = True,
+                                            quiet = True,
+                                            input = sqlfile.name)
+                        
+                        if addRecordDlg.mapDBInfo:
+                            self.__updateATM()
+            
+            elif digitToolbar.GetAction() == "deleteLine":
+                # -> delete selected vector features
+                if digitClass.DeleteSelectedLines() < 0:
+                    return
+                self.__updateATM()
+            elif digitToolbar.GetAction() == "splitLine":
+                # split line
+                if digitClass.SplitLine(self.Pixel2Cell(self.mouse['begin'])) < 0:
+                    return
+            elif digitToolbar.GetAction() == "addVertex":
+                # add vertex
+                fid = digitClass.AddVertex(self.Pixel2Cell(self.mouse['begin']))
+                if fid < 0:
+                    return
+            elif digitToolbar.GetAction() == "removeVertex":
+                # remove vertex
+                fid = digitClass.RemoveVertex(self.Pixel2Cell(self.mouse['begin']))
+                if fid < 0:
+                    return
+                self.__geomAttrbUpdate([fid,])
+            elif digitToolbar.GetAction() in ("copyCats", "copyAttrs"):
+                try:
+                    if digitToolbar.GetAction() == 'copyCats':
+                        if digitClass.CopyCats(self.copyCatsList,
+                                               self.copyCatsIds, copyAttrb=False) < 0:
+                            return
+                    else:
+                        if digitClass.CopyCats(self.copyCatsList,
+                                               self.copyCatsIds, copyAttrb=True) < 0:
+                            return
+                    
+                    del self.copyCatsList
+                    del self.copyCatsIds
+                except AttributeError:
+                    pass
+                
+                self.__updateATM()
+                
+            elif digitToolbar.GetAction() == "editLine" and \
+                    hasattr(self, "vdigitMove"):
+                line = digitClass.driver.GetSelected()
+                if digitClass.EditLine(line, self.polycoords) < 0:
+                    return
+                
+                del self.vdigitMove
+                
+            elif digitToolbar.GetAction() == "flipLine":
+                if digitClass.FlipLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "mergeLine":
+                if digitClass.MergeLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "breakLine":
+                if digitClass.BreakLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "snapLine":
+                if digitClass.SnapLine() < 0:
+                    return
+            elif digitToolbar.GetAction() == "connectLine":
+                if len(digitClass.driver.GetSelected()) > 1:
+                    if digitClass.ConnectLine() < 0:
+                        return
+            elif digitToolbar.GetAction() == "copyLine":
+                if digitClass.CopyLine(self.copyIds) < 0:
+                    return
+                del self.copyIds
+                if self.layerTmp:
+                    self.Map.DeleteLayer(self.layerTmp)
+                    self.UpdateMap(render=True, renderVector=False)
+                del self.layerTmp
+
+            elif digitToolbar.GetAction() == "zbulkLine" and len(self.polycoords) == 2:
+                pos1 = self.polycoords[0]
+                pos2 = self.polycoords[1]
+
+                selected = digitClass.driver.GetSelected()
+                dlg = VDigitZBulkDialog(parent=self, title=_("Z bulk-labeling dialog"),
+                                        nselected=len(selected))
+                if dlg.ShowModal() == wx.ID_OK:
+                    if digitClass.ZBulkLines(pos1, pos2, dlg.value.GetValue(),
+                                             dlg.step.GetValue()) < 0:
+                        return
+                self.UpdateMap(render=False, renderVector=True)
+            elif digitToolbar.GetAction() == "typeConv":
+                # -> feature type conversion
+                # - point <-> centroid
+                # - line <-> boundary
+                if digitClass.TypeConvForSelectedLines() < 0:
+                    return
+
+            if digitToolbar.GetAction() != "addLine":
+                # unselect and re-render
+                digitClass.driver.SetSelected([])
+                self.polycoords = []
+                self.UpdateMap(render=False)
+
+            self.redrawAll = True
+            self.Refresh()
+            
+        event.Skip()
+
+    def OnMiddleDown(self, event):
+        """!
+        Middle mouse button pressed
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        # digitization tool
+        if self.mouse["use"] == "pointer" and digitToolbar:
+            digitClass = self.parent.digit
+            if (digitToolbar.GetAction() == "addLine" and \
+                    digitToolbar.GetAction('type') in ["line", "boundary"]) or \
+                    digitToolbar.GetAction() == "editLine":
+                # add line or boundary -> remove last point from the line
+                try:
+                    removed = self.polycoords.pop()
+                    Debug.msg(4, "BufferedWindow.OnMiddleDown(): polycoords_poped=%s" % \
+                                  [removed,])
+
+                    self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
+                except:
+                    pass
+
+                if digitToolbar.GetAction() == "editLine":
+                    # remove last vertex & line
+                    if len(self.vdigitMove['id']) > 1:
+                        self.vdigitMove['id'].pop()
+
+                self.UpdateMap(render=False, renderVector=False)
+
+            elif digitToolbar.GetAction() in ["deleteLine", "moveLine", "splitLine",
+                                              "addVertex", "removeVertex", "moveVertex",
+                                              "copyCats", "flipLine", "mergeLine",
+                                              "snapLine", "connectLine", "copyLine",
+                                              "queryLine", "breakLine", "typeConv"]:
+                # varios tools -> unselected selected features
+                digitClass.driver.SetSelected([])
+                if digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] and \
+                        hasattr(self, "vdigitMove"):
+
+                    del self.vdigitMove
+                    
+                elif digitToolbar.GetAction() == "copyCats":
+                    try:
+                        del self.copyCatsList
+                        del self.copyCatsIds
+                    except AttributeError:
+                        pass
+                
+                elif digitToolbar.GetAction() == "copyLine":
+                    del self.copyIds
+                    if self.layerTmp:
+                        self.Map.DeleteLayer(self.layerTmp)
+                        self.UpdateMap(render=True, renderVector=False)
+                    del self.layerTmp
+
+                self.polycoords = []
+                self.UpdateMap(render=False) # render vector
+
+            elif digitToolbar.GetAction() == "zbulkLine":
+                # reset polyline
+                self.polycoords = []
+                digitClass.driver.SetSelected([])
+                self.UpdateMap(render=False)
+            
+            self.redrawAll = True
+            
+    def OnMouseMoving(self, event):
+        """!
+        Motion event and no mouse buttons were pressed
+        """
+        digitToolbar = self.parent.toolbars['vdigit']
+        if self.mouse["use"] == "pointer" and digitToolbar:
+            digitClass = self.parent.digit
+            self.mouse['end'] = event.GetPositionTuple()[:]
+            Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
+                           (self.mouse['end'][0], self.mouse['end'][1]))
+            if digitToolbar.GetAction() == "addLine" and digitToolbar.GetAction('type') in ["line", "boundary"]:
+                if len(self.polycoords) > 0:
+                    self.MouseDraw(pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1]))
+            elif digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] \
+                    and hasattr(self, "vdigitMove"):
+                dx = self.mouse['end'][0] - self.mouse['begin'][0]
+                dy = self.mouse['end'][1] - self.mouse['begin'][1]
+                
+                if len(self.vdigitMove['id']) > 0:
+                    # draw lines on new position
+                    if digitToolbar.GetAction() == "moveLine":
+                        # move line
+                        for id in self.vdigitMove['id']:
+                            self.pdcTmp.TranslateId(id, dx, dy)
+                    elif digitToolbar.GetAction() in ["moveVertex", "editLine"]:
+                        # move vertex ->
+                        # (vertex, left vertex, left line,
+                        # right vertex, right line)
+
+                        # do not draw static lines
+                        if digitToolbar.GetAction() == "moveVertex":
+                            self.polycoords = []
+                            ### self.pdcTmp.TranslateId(self.vdigitMove['id'][0], dx, dy)
+                            self.pdcTmp.RemoveId(self.vdigitMove['id'][0])
+                            if self.vdigitMove['id'][1] > 0: # previous vertex
+                                x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][1])[0:2])
+                                self.pdcTmp.RemoveId(self.vdigitMove['id'][1]+1)
+                                self.polycoords.append((x, y))
+                            ### x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][0])[0:2])
+                            self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
+                            if self.vdigitMove['id'][2] > 0: # next vertex
+                                x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][2])[0:2])
+                                self.pdcTmp.RemoveId(self.vdigitMove['id'][2]-1)
+                                self.polycoords.append((x, y))
+                            
+                            self.ClearLines(pdc=self.pdcTmp)
+                            self.DrawLines(pdc=self.pdcTmp)
+
+                        else: # edit line
+                            try:
+                                if self.vdigitMove['id'][-1] > 0: # previous vertex
+                                    self.MouseDraw(pdc=self.pdcTmp,
+                                                   begin=self.Cell2Pixel(self.polycoords[-1]))
+                            except: # no line
+                                self.vdigitMove['id'] = []
+                                self.polycoords = []
+
+                self.Refresh() # TODO: use RefreshRect()
+                self.mouse['begin'] = self.mouse['end']
+
+            elif digitToolbar.GetAction() == "zbulkLine":
+                if len(self.polycoords) == 1:
+                    # draw mouse moving
+                    self.MouseDraw(self.pdcTmp)
+
+        event.Skip()
+
+    def ClearLines(self, pdc=None):
+        """!
+        Clears temporary drawn lines from PseudoDC
+        """
+        if not pdc:
+            pdc=self.pdcTmp
+        try:
+            pdc.ClearId(self.lineid)
+            pdc.RemoveId(self.lineid)
+        except:
+            pass
+
+        try:
+            pdc.ClearId(self.plineid)
+            pdc.RemoveId(self.plineid)
+        except:
+            pass
+
+        Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
+                  (self.lineid, self.plineid))
+
+        ### self.Refresh()
+
+        return True
+
+    def Pixel2Cell(self, (x, y)):
+        """!
+        Convert image coordinates to real word coordinates
+
+        Input : int x, int y
+        Output: float x, float y
+        """
+
+        try:
+            x = int(x)
+            y = int(y)
+        except:
+            return None
+
+        if self.Map.region["ewres"] > self.Map.region["nsres"]:
+            res = self.Map.region["ewres"]
+        else:
+            res = self.Map.region["nsres"]
+
+        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+
+        east  = w + x * res
+        north = n - y * res
+
+        # extent does not correspond with whole map canvas area...
+        # east  = self.Map.region['w'] + x * self.Map.region["ewres"]
+        # north = self.Map.region['n'] - y * self.Map.region["nsres"]
+
+        return (east, north)
+
+    def Cell2Pixel(self, (east, north)):
+        """!
+        Convert real word coordinates to image coordinates
+        """
+
+        try:
+            east  = float(east)
+            north = float(north)
+        except:
+            return None
+
+        if self.Map.region["ewres"] > self.Map.region["nsres"]:
+            res = self.Map.region["ewres"]
+        else:
+            res = self.Map.region["nsres"]
+
+        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+
+        # x = int((east  - w) / res)
+        # y = int((n - north) / res)
+
+        x = (east  - w) / res
+        y = (n - north) / res
+
+        return (x, y)
+
+    def Zoom(self, begin, end, zoomtype):
+        """!
+        Calculates new region while (un)zoom/pan-ing
+        """
+        x1, y1 = begin
+        x2, y2 = end
+        newreg = {}
+
+        # threshold - too small squares do not make sense
+        # can only zoom to windows of > 5x5 screen pixels
+        if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
+
+            if x1 > x2:
+                x1, x2 = x2, x1
+            if y1 > y2:
+                y1, y2 = y2, y1
+
+            # zoom in
+            if zoomtype > 0:
+                newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
+                newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
+
+            # zoom out
+            elif zoomtype < 0:
+                newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
+                newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + 2 * \
+                                                                (self.Map.width  - x2),
+                                                            self.Map.height + 2 * \
+                                                                (self.Map.height - y2)))
+        # pan
+        elif zoomtype == 0:
+            dx = x1 - x2
+            dy = y1 - y2
+            newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
+            newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + dx,
+                                                        self.Map.height + dy))
+
+        # if new region has been calculated, set the values
+        if newreg != {}:
+            # LL locations
+            if self.parent.Map.projinfo['proj'] == 'll':
+                if newreg['n'] > 90.0:
+                    newreg['n'] = 90.0
+                if newreg['s'] < -90.0:
+                    newreg['s'] = -90.0
+            
+            ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
+            cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
+            
+            if hasattr(self, "vdigitMove"):
+                # xo = self.Cell2Pixel((self.Map.region['center_easting'], self.Map.region['center_northing']))
+                # xn = self.Cell2Pixel(ce, cn))
+                tmp = self.Pixel2Cell(self.mouse['end'])
+            
+            # calculate new center point and display resolution
+            self.Map.region['center_easting'] = ce
+            self.Map.region['center_northing'] = cn
+            self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width
+            self.Map.region["nsres"] = (newreg['n'] - newreg['s']) / self.Map.height
+            self.Map.AlignExtentFromDisplay()
+
+            if hasattr(self, "vdigitMove"):
+                tmp1 = self.mouse['end']
+                tmp2 = self.Cell2Pixel(self.vdigitMove['begin'])
+                dx = tmp1[0] - tmp2[0]
+                dy = tmp1[1] - tmp2[1]
+                self.vdigitMove['beginDiff'] = (dx, dy)
+                for id in self.vdigitMove['id']:
+                    self.pdcTmp.RemoveId(id)
+            
+            self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                             self.Map.region['e'], self.Map.region['w'])
+
+        if self.redrawAll is False:
+            self.redrawAll = True
+
+    def ZoomBack(self):
+        """!
+        Zoom to previous extents in zoomhistory list
+        """
+        zoom = list()
+        
+        if len(self.zoomhistory) > 1:
+            self.zoomhistory.pop()
+            zoom = self.zoomhistory[-1]
+        
+        if len(self.zoomhistory) < 2: # disable tool
+            self.parent.toolbars['map'].Enable('zoomback', enable = False)
+        
+        # zoom to selected region
+        self.Map.GetRegion(n = zoom[0], s = zoom[1],
+                           e = zoom[2], w = zoom[3],
+                           update = True)
+        # update map
+        self.UpdateMap()
+        
+        # update statusbar
+        self.parent.StatusbarUpdate()
+
+    def ZoomHistory(self, n, s, e, w):
+        """!
+        Manages a list of last 10 zoom extents
+
+        Return removed history item if exists
+        """
+        removed = None
+        self.zoomhistory.append((n,s,e,w))
+
+        if len(self.zoomhistory) > 10:
+            removed = self.zoomhistory.pop(0)
+
+        if removed:
+            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
+                      (self.zoomhistory, removed))
+        else:
+            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
+                      (self.zoomhistory))
+        
+        if len(self.zoomhistory) > 1:
+            self.parent.toolbars['map'].Enable('zoomback')
+        
+        return removed
+
+    def OnZoomToMap(self, event):
+        """!
+        Set display extents to match selected raster (including NULLs)
+        or vector map.
+        """
+        self.ZoomToMap()
+
+    def OnZoomToRaster(self, event):
+        """!
+        Set display extents to match selected raster map (ignore NULLs)
+        """
+        self.ZoomToMap(ignoreNulls = True)
+        
+    def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
+        """!
+        Set display extents to match selected raster
+        or vector map(s).
+
+        @param layer list of layers to be zoom to
+        @param ignoreNulls True to ignore null-values
+        @param render True to re-render display
+        """
+        zoomreg = {}
+
+        if not layers:
+            layers = self.GetSelectedLayer(multi = True)
+        
+        if not layers:
+            return
+        
+        rast = []
+        vect = []
+        updated = False
+        for l in layers:
+            # only raster/vector layers are currently supported
+            if l.type == 'raster':
+                rast.append(l.name)
+            elif l.type == 'vector':
+                digitToolbar = self.parent.toolbars['vdigit']
+                if digitToolbar and digitToolbar.GetLayer() == l:
+                    w, s, b, e, n, t = self.parent.digit.driver.GetMapBoundingBox()
+                    self.Map.GetRegion(n=n, s=s, w=w, e=e,
+                                       update=True)
+                    updated = True
+                else:
+                    vect.append(l.name)
+        
+        if not updated:
+            self.Map.GetRegion(rast = rast,
+                               vect = vect,
+                               update = True)
+        
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+        
+        if render:
+            self.UpdateMap()
+
+        self.parent.StatusbarUpdate()
+        
+    def ZoomToWind(self, event):
+        """!
+        Set display geometry to match computational
+        region settings (set with g.region)
+        """
+        self.Map.region = self.Map.GetRegion()
+        ### self.Map.SetRegion(windres=True)
+
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+
+        self.UpdateMap()
+
+        self.parent.StatusbarUpdate()
+
+    def ZoomToDefault(self, event):
+        """!
+        Set display geometry to match default region settings
+        """
+        self.Map.region = self.Map.GetRegion(default=True)
+        self.Map.AdjustRegion() # aling region extent to the display
+
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+
+        self.UpdateMap()
+
+        self.parent.StatusbarUpdate()
+        
+    def DisplayToWind(self, event):
+        """!
+        Set computational region (WIND file) to
+        match display extents
+        """
+        tmpreg = os.getenv("GRASS_REGION")
+        if tmpreg:
+            del os.environ["GRASS_REGION"]
+
+        # We ONLY want to set extents here. Don't mess with resolution. Leave that
+        # for user to set explicitly with g.region
+        new = self.Map.AlignResolution()
+        gcmd.RunCommand('g.region',
+                        parent = self,
+                        overwrite = True,
+                        n = new['n'],
+                        s = new['s'],
+                        e = new['e'],
+                        w = new['w'],
+                        rows = int(new['rows']),
+                        cols = int(new['cols']))
+        
+        if tmpreg:
+            os.environ["GRASS_REGION"] = tmpreg
+
+    def ZoomToSaved(self, event):
+        """!Set display geometry to match extents in
+        saved region file
+        """
+        dlg = gdialogs.SavedRegion(parent = self,
+                                   title = _("Zoom to saved region extents"),
+                                   loadsave='load')
+        
+        if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
+            dlg.Destroy()
+            return
+        
+        if not grass.find_file(name = dlg.wind, element = 'windows')['name']:
+            wx.MessageBox(parent = self,
+                          message = _("Region <%s> not found. Operation canceled.") % dlg.wind,
+                          caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
+            dlg.Destroy()
+            return
+        
+        self.Map.GetRegion(regionName = dlg.wind,
+                           update = True)
+        
+        dlg.Destroy()
+        
+        self.ZoomHistory(self.Map.region['n'],
+                         self.Map.region['s'],
+                         self.Map.region['e'],
+                         self.Map.region['w'])
+        
+        self.UpdateMap()
+                
+    def SaveDisplayRegion(self, event):
+        """!
+        Save display extents to named region file.
+        """
+
+        dlg = gdialogs.SavedRegion(parent = self,
+                                   title = _("Save display extents to region file"),
+                                   loadsave='save')
+        
+        if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
+            dlg.Destroy()
+            return
+
+        # test to see if it already exists and ask permission to overwrite
+        if grass.find_file(name = dlg.wind, element = 'windows')['name']:
+            overwrite = wx.MessageBox(parent = self,
+                                      message = _("Region file <%s> already exists. "
+                                                  "Do you want to overwrite it?") % (dlg.wind),
+                                      caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
+            if (overwrite == wx.YES):
+                self.SaveRegion(dlg.wind)
+        else:
+            self.SaveRegion(dlg.wind)
+        
+        dlg.Destroy()
+
+    def SaveRegion(self, wind):
+        """!Save region settings
+
+        @param wind region name
+        """
+        new = self.Map.GetCurrentRegion()
+
+        tmpreg = os.getenv("GRASS_REGION")
+        if tmpreg:
+            del os.environ["GRASS_REGION"]
+        
+        gcmd.RunCommand('g.region',
+                        overwrite = True,
+                        parent = self,
+                        flags = 'u',
+                        n = new['n'],
+                        s = new['s'],
+                        e = new['e'],
+                        w = new['w'],
+                        rows = int(new['rows']),
+                        cols = int(new['cols']),
+                        save = wind)
+        
+        if tmpreg:
+            os.environ["GRASS_REGION"] = tmpreg
+
+    def Distance(self, beginpt, endpt, screen=True):
+        """!Calculete distance
+
+        LL-locations not supported
+
+        @todo Use m.distance
+
+        @param beginpt first point
+        @param endpt second point
+        @param screen True for screen coordinates otherwise EN
+        """
+        x1, y1 = beginpt
+        x2, y2 = endpt
+        if screen:
+            dEast  = (x2 - x1) * self.Map.region["ewres"]
+            dNorth = (y2 - y1) * self.Map.region["nsres"]
+        else:
+            dEast  = (x2 - x1)
+            dNorth = (y2 - y1)
+        
+        return (math.sqrt(math.pow((dEast),2) + math.pow((dNorth),2)), (dEast, dNorth))



More information about the grass-commit mailing list