GRASS Programmer's Manual  6.4.2(2012)
mapdisp_window.py
Go to the documentation of this file.
00001 """!
00002 @package mapdisp_window.py
00003 
00004 @brief Map display canvas - buffered window.
00005 
00006 Classes:
00007  - MapWindow
00008  - BufferedWindow
00009 
00010 (C) 2006-2011 by the GRASS Development Team
00011 This program is free software under the GNU General Public
00012 License (>=v2). Read the file COPYING that comes with GRASS
00013 for details.
00014 
00015 @author Martin Landa <landa.martin gmail.com>
00016 @author Michael Barton
00017 @author Jachym Cepicky
00018 """
00019 
00020 import os
00021 import time
00022 import math
00023 import sys
00024 import tempfile
00025 import traceback
00026 
00027 import wx
00028 
00029 import grass.script as grass
00030 
00031 import dbm
00032 import gdialogs
00033 import gcmd
00034 import utils
00035 import globalvar
00036 import gselect
00037 from debug import Debug
00038 from preferences import globalSettings as UserSettings
00039 from units import ConvertValue as UnitsConvertValue
00040 
00041 try:
00042     import grass.lib.gis as gislib
00043     haveCtypes = True
00044 except ImportError:
00045     haveCtypes = False
00046 
00047 class MapWindow(object):
00048     """!Abstract map display window class
00049     
00050     Superclass for BufferedWindow class (2D display mode), and GLWindow
00051     (3D display mode).
00052     """
00053     def __init__(self, parent, id = wx.ID_ANY,
00054                  Map = None, tree = None, lmgr = None, **kwargs):
00055         self.parent = parent # MapFrame
00056         self.Map    = Map
00057         self.tree   = tree
00058         self.lmgr   = lmgr
00059         
00060         # mouse attributes -- position on the screen, begin and end of
00061         # dragging, and type of drawing
00062         self.mouse = {
00063             'begin': [0, 0], # screen coordinates
00064             'end'  : [0, 0],
00065             'use'  : "pointer",
00066             'box'  : "point"
00067             }
00068 
00069     def OnMotion(self, event):
00070         """!Track mouse motion and update statusbar
00071         """
00072         if self.parent.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
00073             precision = int(UserSettings.Get(group = 'projection', key = 'format',
00074                                              subkey = 'precision'))
00075             format = UserSettings.Get(group = 'projection', key = 'format',
00076                                       subkey = 'll')
00077             try:
00078                 e, n = self.Pixel2Cell(event.GetPositionTuple())
00079             except (TypeError, ValueError):
00080                 self.parent.statusbar.SetStatusText("", 0)
00081                 return
00082             
00083             updated = False
00084             if hasattr(self, "digit"):
00085                 updated = self._onMotion((e, n), precision)
00086 
00087             if not updated:
00088                 if self.parent.statusbarWin['projection'].IsChecked():
00089                     if not UserSettings.Get(group = 'projection', key = 'statusbar', subkey = 'proj4'):
00090                         self.parent.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
00091                     else:
00092                         proj, coord  = utils.ReprojectCoordinates(coord = (e, n),
00093                                                                   projOut = UserSettings.Get(group = 'projection',
00094                                                                                              key = 'statusbar',
00095                                                                                              subkey = 'proj4'),
00096                                                                   flags = 'd')
00097                     
00098                         if coord:
00099                             e, n = coord
00100                             if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
00101                                 self.parent.statusbar.SetStatusText(utils.Deg2DMS(e, n, precision = precision),
00102                                                                     0)
00103                             else:
00104                                 self.parent.statusbar.SetStatusText("%.*f; %.*f" % \
00105                                                                         (precision, e, precision, n), 0)
00106                         else:
00107                             self.parent.statusbar.SetStatusText(_("Error in projection (check the settings)"), 0)
00108                 else:
00109                     if self.parent.Map.projinfo['proj'] == 'll' and format == 'DMS':
00110                         self.parent.statusbar.SetStatusText(utils.Deg2DMS(e, n, precision = precision),
00111                                                             0)
00112                     else:
00113                         self.parent.statusbar.SetStatusText("%.*f; %.*f" % \
00114                                                                 (precision, e, precision, n), 0)
00115         
00116         event.Skip()
00117 
00118     def GetLayerByName(self, name, mapType, dataType = 'layer'):
00119         """!Get layer from layer tree by nam
00120         
00121         @param name layer name
00122         @param type 'item' / 'layer' / 'nviz'
00123 
00124         @return layer / map layer properties / nviz properties
00125         @return None
00126         """
00127         if not self.tree:
00128             return None
00129         
00130         try:
00131             mapLayer = self.Map.GetListOfLayers(l_type = mapType, l_name = name)[0]
00132         except IndexError:
00133             return None
00134         
00135         if dataType == 'layer':
00136             return mapLayer
00137         item = self.tree.FindItemByData('maplayer', mapLayer)
00138         if not item:
00139             return None
00140         if dataType == 'nviz':
00141             return self.tree.GetPyData(item)[0]['nviz']
00142         
00143         return item
00144     
00145     def GetSelectedLayer(self, type = 'layer', multi = False):
00146         """!Get selected layer from layer tree
00147         
00148         @param type 'item' / 'layer' / 'nviz'
00149         @param multi return first selected layer or all
00150         
00151         @return layer / map layer properties / nviz properties
00152         @return None / [] on failure
00153         """
00154         ret = []
00155         if not self.tree or \
00156                 not self.tree.GetSelection():
00157             if multi:
00158                 return []
00159             else:
00160                 return None
00161         
00162         if multi and \
00163                 type == 'item':
00164             return self.tree.GetSelections()
00165         
00166         for item in self.tree.GetSelections():
00167             if not item.IsChecked():
00168                 if multi:
00169                     continue
00170                 else:
00171                     return None
00172 
00173             if type == 'item': # -> multi = False
00174                 return item
00175         
00176             try:
00177                 if type == 'nviz':
00178                     layer = self.tree.GetPyData(item)[0]['nviz']
00179                 else:
00180                     layer = self.tree.GetPyData(item)[0]['maplayer']
00181             except:
00182                 layer = None
00183 
00184             if multi:
00185                 ret.append(layer)
00186             else:
00187                 return layer
00188             
00189         return ret
00190     
00191 class BufferedWindow(MapWindow, wx.Window):
00192     """!A Buffered window class (2D view mode)
00193 
00194     Superclass for VDigitWindow (vector digitizer).
00195     
00196     When the drawing needs to change, you app needs to call the
00197     UpdateMap() method. Since the drawing is stored in a bitmap, you
00198     can also save the drawing to file by calling the
00199     SaveToFile() method.
00200     """
00201     def __init__(self, parent, id = wx.ID_ANY,
00202                  Map = None, tree = None, lmgr = None,
00203                  style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
00204         MapWindow.__init__(self, parent, id, Map, tree, lmgr, **kwargs)
00205         wx.Window.__init__(self, parent, id, style = style, **kwargs)
00206         
00207         # flags
00208         self.resize = False # indicates whether or not a resize event has taken place
00209         self.dragimg = None # initialize variable for map panning
00210         
00211         # variables for drawing on DC
00212         self.pen = None      # pen for drawing zoom boxes, etc.
00213         self.polypen = None  # pen for drawing polylines (measurements, profiles, etc)
00214         # List of wx.Point tuples defining a polyline (geographical coordinates)
00215         self.polycoords = []
00216         # ID of rubber band line
00217         self.lineid = None
00218         # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
00219         self.plineid = None
00220         
00221         # event bindings
00222         self.Bind(wx.EVT_PAINT,        self.OnPaint)
00223         self.Bind(wx.EVT_SIZE,         self.OnSize)
00224         self.Bind(wx.EVT_IDLE,         self.OnIdle)
00225         self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
00226         self.Bind(wx.EVT_MOTION,       self.OnMotion)
00227         
00228         self.processMouse = True
00229         
00230         # render output objects
00231         self.mapfile = None   # image file to be rendered
00232         self.img     = None   # wx.Image object (self.mapfile)
00233         # decoration overlays
00234         self.overlays = {}
00235         # images and their PseudoDC ID's for painting and dragging
00236         self.imagedict = {}   
00237         self.select = {}      # selecting/unselecting decorations for dragging
00238         self.textdict = {}    # text, font, and color indexed by id
00239         self.currtxtid = None # PseudoDC id for currently selected text
00240         
00241         # zoom objects
00242         self.zoomhistory  = [] # list of past zoom extents
00243         self.currzoom     = 0  # current set of extents in zoom history being used
00244         self.zoomtype     = 1  # 1 zoom in, 0 no zoom, -1 zoom out
00245         self.hitradius    = 10 # distance for selecting map decorations
00246         self.dialogOffset = 5  # offset for dialog (e.g. DisplayAttributesDialog)
00247         
00248         # OnSize called to make sure the buffer is initialized.
00249         # This might result in OnSize getting called twice on some
00250         # platforms at initialization, but little harm done.
00251         ### self.OnSize(None)
00252         
00253         self._definePseudoDC()
00254         # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
00255         self.redrawAll = True
00256         
00257         # will store an off screen empty bitmap for saving to file
00258         self._buffer = None
00259         
00260         self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
00261         
00262         # vars for handling mouse clicks
00263         self.dragid   = -1
00264         self.lastpos  = (0, 0)
00265         
00266     def _definePseudoDC(self):
00267         """!Define PseudoDC objects to use
00268         """
00269         # create PseudoDC used for background map, map decorations like scales and legends
00270         self.pdc = wx.PseudoDC()
00271         # used for digitization tool
00272         self.pdcVector = None
00273         # decorations (region box, etc.)
00274         self.pdcDec = wx.PseudoDC()
00275         # pseudoDC for temporal objects (select box, measurement tool, etc.)
00276         self.pdcTmp = wx.PseudoDC()
00277         
00278     def Draw(self, pdc, img = None, drawid = None, pdctype = 'image', coords = [0, 0, 0, 0]):
00279         """!Draws map and overlay decorations
00280         """
00281         if drawid == None:
00282             if pdctype == 'image' and img:
00283                 drawid = self.imagedict[img]
00284             elif pdctype == 'clear':
00285                 drawid == None
00286             else:
00287                 drawid = wx.NewId()
00288         
00289         if img and pdctype == 'image':
00290             # self.imagedict[img]['coords'] = coords
00291             self.select[self.imagedict[img]['id']] = False # ?
00292         
00293         pdc.BeginDrawing()
00294         
00295         if drawid != 99:
00296             bg = wx.TRANSPARENT_BRUSH
00297         else:
00298             bg = wx.Brush(self.GetBackgroundColour())
00299         
00300         pdc.SetBackground(bg)
00301         
00302         Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype = %s, coord=%s" % \
00303                        (drawid, pdctype, coords))
00304         
00305         # set PseudoDC id
00306         if drawid is not None:
00307             pdc.SetId(drawid)
00308             
00309         if pdctype == 'clear': # erase the display
00310             bg = wx.WHITE_BRUSH
00311             # bg = wx.Brush(self.GetBackgroundColour())
00312             pdc.SetBackground(bg)
00313             pdc.RemoveAll()
00314             pdc.Clear()
00315             pdc.EndDrawing()
00316             
00317             self.Refresh()
00318             return
00319         
00320         if pdctype == 'image': # draw selected image
00321             bitmap = wx.BitmapFromImage(img)
00322             w,h = bitmap.GetSize()
00323             pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
00324             pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
00325         
00326         elif pdctype == 'box': # draw a box on top of the map
00327             if self.pen:
00328                 pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
00329                 pdc.SetPen(self.pen)
00330                 x2 = max(coords[0],coords[2])
00331                 x1 = min(coords[0],coords[2])
00332                 y2 = max(coords[1],coords[3])
00333                 y1 = min(coords[1],coords[3])
00334                 rwidth = x2-x1
00335                 rheight = y2-y1
00336                 rect = wx.Rect(x1, y1, rwidth, rheight)
00337                 pdc.DrawRectangleRect(rect)
00338                 pdc.SetIdBounds(drawid, rect)
00339                 
00340         elif pdctype == 'line': # draw a line on top of the map
00341             if self.pen:
00342                 pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
00343                 pdc.SetPen(self.pen)
00344                 pdc.DrawLinePoint(wx.Point(coords[0], coords[1]),wx.Point(coords[2], coords[3]))
00345                 pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
00346         
00347         elif pdctype == 'polyline': # draw a polyline on top of the map
00348             if self.polypen:
00349                 pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
00350                 pdc.SetPen(self.polypen)
00351                 if (len(coords) < 2):
00352                     return
00353                 i = 1
00354                 while i < len(coords):
00355                     pdc.DrawLinePoint(wx.Point(coords[i-1][0], coords[i-1][1]),
00356                                       wx.Point(coords[i][0], coords[i][1]))
00357                     i += 1
00358                 
00359                 # get bounding rectangle for polyline
00360                 xlist = []
00361                 ylist = []
00362                 if len(coords) > 0:
00363                     for point in coords:
00364                         x,y = point
00365                         xlist.append(x)
00366                         ylist.append(y)
00367                     x1 = min(xlist)
00368                     x2 = max(xlist)
00369                     y1 = min(ylist)
00370                     y2 = max(ylist)
00371                     pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
00372                     # self.ovlcoords[drawid] = [x1,y1,x2,y2]
00373         
00374         elif pdctype == 'point': # draw point
00375             if self.pen:
00376                 pdc.SetPen(self.pen)
00377                 pdc.DrawPoint(coords[0], coords[1])
00378                 coordsBound = (coords[0] - 5,
00379                                coords[1] - 5,
00380                                coords[0] + 5,
00381                                coords[1] + 5)
00382                 pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
00383         
00384         elif pdctype == 'text': # draw text on top of map
00385             if not img['active']:
00386                 return # only draw active text
00387             if 'rotation' in img:
00388                 rotation = float(img['rotation'])
00389             else:
00390                 rotation = 0.0
00391             w, h = self.GetFullTextExtent(img['text'])[0:2]
00392             pdc.SetFont(img['font'])
00393             pdc.SetTextForeground(img['color'])
00394             coords, w, h = self.TextBounds(img)
00395             if rotation == 0:
00396                 pdc.DrawText(img['text'], coords[0], coords[1])
00397             else:
00398                 pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
00399             pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], w, h))
00400         
00401         pdc.EndDrawing()
00402         
00403         self.Refresh()
00404         
00405         return drawid
00406     
00407     def TextBounds(self, textinfo):
00408         """!Return text boundary data
00409         
00410         @param textinfo text metadata (text, font, color, rotation)
00411         @param coords reference point
00412         """
00413         if 'rotation' in textinfo:
00414             rotation = float(textinfo['rotation'])
00415         else:
00416             rotation = 0.0
00417         
00418         coords = textinfo['coords']
00419         
00420         Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
00421                    (textinfo['text'], rotation))
00422         
00423         self.Update()
00424         
00425         self.SetFont(textinfo['font'])
00426         
00427         w, h = self.GetTextExtent(textinfo['text'])
00428         
00429         if rotation == 0:
00430             coords[2], coords[3] = coords[0] + w, coords[1] + h
00431             return coords, w, h
00432         
00433         boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
00434         boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
00435         coords[2] = coords[0] + boxw
00436         coords[3] = coords[1] + boxh
00437         
00438         return coords, boxw, boxh
00439 
00440     def OnPaint(self, event):
00441         """!Draw PseudoDC's to buffered paint DC
00442         
00443         If self.redrawAll is False on self.pdcTmp content is re-drawn
00444         """
00445         Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
00446         
00447         dc = wx.BufferedPaintDC(self, self.buffer)
00448         dc.Clear()
00449         
00450         # use PrepareDC to set position correctly
00451         self.PrepareDC(dc)
00452         
00453         # create a clipping rect from our position and size
00454         # and update region
00455         rgn = self.GetUpdateRegion().GetBox()
00456         dc.SetClippingRect(rgn)
00457         
00458         switchDraw = False
00459         if self.redrawAll is None:
00460             self.redrawAll = True
00461             switchDraw = True
00462         
00463         if self.redrawAll: # redraw pdc and pdcVector
00464             # draw to the dc using the calculated clipping rect
00465             self.pdc.DrawToDCClipped(dc, rgn)
00466             
00467             # draw vector map layer
00468             if hasattr(self, "digit"):
00469                 # decorate with GDDC (transparency)
00470                 try:
00471                     gcdc = wx.GCDC(dc)
00472                     self.pdcVector.DrawToDCClipped(gcdc, rgn)
00473                 except NotImplementedError, e:
00474                     print >> sys.stderr, e
00475                     self.pdcVector.DrawToDCClipped(dc, rgn)
00476             
00477             self.bufferLast = None
00478         else: # do not redraw pdc and pdcVector
00479             if self.bufferLast is None:
00480                 # draw to the dc
00481                 self.pdc.DrawToDC(dc)
00482                 
00483                 if hasattr(self, "digit"):
00484                     # decorate with GDDC (transparency)
00485                     try:
00486                         gcdc = wx.GCDC(dc)
00487                         self.pdcVector.DrawToDC(gcdc)
00488                     except NotImplementedError, e:
00489                         print >> sys.stderr, e
00490                         self.pdcVector.DrawToDC(dc)
00491                 
00492                 # store buffered image
00493                 # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
00494                 self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
00495             
00496             self.pdc.DrawBitmap(self.bufferLast, 0, 0, False)
00497             self.pdc.DrawToDC(dc)
00498         
00499         # draw decorations (e.g. region box)
00500         try:
00501             gcdc = wx.GCDC(dc)
00502             self.pdcDec.DrawToDC(gcdc)
00503         except NotImplementedError, e:
00504             print >> sys.stderr, e
00505             self.pdcDec.DrawToDC(dc)
00506         
00507         # draw temporary object on the foreground
00508         ### self.pdcTmp.DrawToDCClipped(dc, rgn)
00509         self.pdcTmp.DrawToDC(dc)
00510         
00511         if switchDraw:
00512             self.redrawAll = False
00513         
00514     def OnSize(self, event):
00515         """!Scale map image so that it is the same size as the Window
00516         """
00517         Debug.msg(3, "BufferedWindow.OnSize():")
00518         
00519         # set size of the input image
00520         self.Map.ChangeMapSize(self.GetClientSize())
00521         # align extent based on center point and display resolution
00522         # this causes that image is not resized when display windows is resized
00523         ### self.Map.AlignExtentFromDisplay()
00524         
00525         # Make new off screen bitmap: this bitmap will always have the
00526         # current drawing in it, so it can be used to save the image to
00527         # a file, or whatever.
00528         self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
00529         
00530         # get the image to be rendered
00531         self.img = self.GetImage()
00532         
00533         # update map display
00534         if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
00535             self.img = self.img.Scale(self.Map.width, self.Map.height)
00536             if len(self.Map.GetListOfLayers()) > 0:
00537                 self.UpdateMap()
00538         
00539         # re-render image on idle
00540         self.resize = True
00541 
00542         # reposition checkbox in statusbar
00543         self.parent.StatusbarReposition()
00544         
00545         # update statusbar
00546         self.parent.StatusbarUpdate()
00547         
00548     def OnIdle(self, event):
00549         """!Only re-render a composite map image from GRASS during
00550         idle time instead of multiple times during resizing.
00551         """
00552         if self.resize:
00553             self.UpdateMap(render = True)
00554         
00555         event.Skip()
00556 
00557     def SaveToFile(self, FileName, FileType, width, height):
00558         """!This draws the pseudo DC to a buffer that can be saved to
00559         a file.
00560         
00561         @param FileName file name
00562         @param FileType type of bitmap
00563         @param width image width
00564         @param height image height
00565         """
00566         busy = wx.BusyInfo(message = _("Please wait, exporting image..."),
00567                            parent = self)
00568         wx.Yield()
00569         
00570         self.Map.ChangeMapSize((width, height))
00571         ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
00572         self.Map.Render(force = True, windres = True)
00573         img = self.GetImage()
00574         self.Draw(self.pdc, img, drawid = 99)
00575         dc = wx.BufferedPaintDC(self, ibuffer)
00576         dc.Clear()
00577         self.PrepareDC(dc)
00578         self.pdc.DrawToDC(dc)
00579         if self.pdcVector:
00580             self.pdcVector.DrawToDC(dc)
00581         ibuffer.SaveFile(FileName, FileType)
00582         
00583         busy.Destroy()
00584         
00585         self.UpdateMap(render = True)
00586         self.Refresh()
00587         
00588     def GetOverlay(self):
00589         """!Converts rendered overlay files to wx.Image
00590         
00591         Updates self.imagedict
00592         
00593         @return list of images
00594         """
00595         imgs = []
00596         for overlay in self.Map.GetListOfLayers(l_type = "overlay", l_active = True):
00597             if os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
00598                 img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
00599                 self.imagedict[img] = { 'id' : overlay.id,
00600                                         'layer' : overlay }
00601                 imgs.append(img)
00602 
00603         return imgs
00604     
00605     def GetImage(self):
00606         """!Converts redered map files to wx.Image
00607         
00608         Updates self.imagedict (id=99)
00609         
00610         @return wx.Image instance (map composition)
00611         """
00612         imgId = 99
00613         if self.mapfile and self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
00614                 os.path.getsize(self.Map.mapfile):
00615             img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
00616         else:
00617             img = None
00618         
00619         self.imagedict[img] = { 'id': imgId }
00620         
00621         return img
00622 
00623     def UpdateMap(self, render = True, renderVector = True):
00624         """!Updates the canvas anytime there is a change to the
00625         underlaying images or to the geometry of the canvas.
00626         
00627         @param render re-render map composition
00628         @param renderVector re-render vector map layer enabled for editing (used for digitizer)
00629         """
00630         start = time.clock()
00631         
00632         self.resize = False
00633         
00634         if self.img is None:
00635             render = True
00636         
00637         #
00638         # initialize process bar (only on 'render')
00639         #
00640         if render or renderVector:
00641             self.parent.statusbarWin['progress'].Show()
00642             if self.parent.statusbarWin['progress'].GetRange() > 0:
00643                 self.parent.statusbarWin['progress'].SetValue(1)
00644         
00645         #
00646         # render background image if needed
00647         #
00648         # update layer dictionary if there has been a change in layers
00649         if self.tree and self.tree.reorder:
00650             self.tree.ReorderLayers()
00651         
00652         # reset flag for auto-rendering
00653         if self.tree:
00654             self.tree.rerender = False
00655         
00656         try:
00657             if render:
00658                 # update display size
00659                 self.Map.ChangeMapSize(self.GetClientSize())
00660                 if self.parent.statusbarWin['resolution'].IsChecked():
00661                     # use computation region resolution for rendering
00662                     windres = True
00663                 else:
00664                     windres = False
00665                 self.mapfile = self.Map.Render(force = True, mapWindow = self.parent,
00666                                                windres = windres)
00667             else:
00668                 self.mapfile = self.Map.Render(force = False, mapWindow = self.parent)
00669         except gcmd.GException, e:
00670             gcmd.GError(message = e.value)
00671             self.mapfile = None
00672         
00673         self.img = self.GetImage() # id=99
00674         
00675         #
00676         # clear pseudoDcs
00677         #
00678         for pdc in (self.pdc,
00679                     self.pdcDec,
00680                     self.pdcTmp):
00681             pdc.Clear()
00682             pdc.RemoveAll()
00683         
00684         #
00685         # draw background map image to PseudoDC
00686         #
00687         if not self.img:
00688             self.Draw(self.pdc, pdctype = 'clear')
00689         else:
00690             try:
00691                 id = self.imagedict[self.img]['id']
00692             except:
00693                 return False
00694             
00695             self.Draw(self.pdc, self.img, drawid = id)
00696         
00697         #
00698         # render vector map layer
00699         #
00700         if renderVector and hasattr(self, "digit"):
00701             self._updateMap()
00702         #
00703         # render overlays
00704         #
00705         for img in self.GetOverlay():
00706             # draw any active and defined overlays
00707             if self.imagedict[img]['layer'].IsActive():
00708                 id = self.imagedict[img]['id']
00709                 self.Draw(self.pdc, img = img, drawid = id,
00710                           pdctype = self.overlays[id]['pdcType'], coords = self.overlays[id]['coords'])
00711         
00712         for id in self.textdict.keys():
00713             self.Draw(self.pdc, img = self.textdict[id], drawid = id,
00714                       pdctype = 'text', coords = [10, 10, 10, 10])
00715         
00716         # optionally draw computational extent box
00717         self.DrawCompRegionExtent()
00718         
00719         #
00720         # redraw pdcTmp if needed
00721         #
00722         if len(self.polycoords) > 0:
00723             self.DrawLines(self.pdcTmp)
00724         
00725         if not self.parent.IsStandalone() and \
00726                 self.parent.GetLayerManager().georectifying:
00727             # -> georectifier (redraw GCPs)
00728             if self.parent.toolbars['georect']:
00729                 coordtype = 'gcpcoord'
00730             else:
00731                 coordtype = 'mapcoord'
00732             self.parent.GetLayerManager().georectifying.DrawGCP(coordtype)
00733             
00734         if not self.parent.IsStandalone() and \
00735                 self.parent.GetLayerManager().gcpmanagement:
00736             # -> georectifier (redraw GCPs)
00737             if self.parent.toolbars['gcpdisp']:
00738                 if self == self.parent.TgtMapWindow:
00739                     coordtype = 'target'
00740                 else:
00741                     coordtype = 'source'
00742 
00743                 self.parent.DrawGCP(coordtype)
00744 
00745         # 
00746         # clear measurement
00747         #
00748         if self.mouse["use"] == "measure":
00749             self.ClearLines(pdc = self.pdcTmp)
00750             self.polycoords = []
00751             self.mouse['use'] = 'pointer'
00752             self.mouse['box'] = 'point'
00753             self.mouse['end'] = [0, 0]
00754             self.SetCursor(self.parent.cursors["default"])
00755             
00756         stop = time.clock()
00757         
00758         #
00759         # hide process bar
00760         #
00761         self.parent.statusbarWin['progress'].Hide()
00762 
00763         #
00764         # update statusbar 
00765         #
00766         ### self.Map.SetRegion()
00767         self.parent.StatusbarUpdate()
00768         if grass.find_file(name = 'MASK', element = 'cell')['name']:
00769             # mask found
00770             self.parent.statusbarWin['mask'].SetLabel(_('MASK'))
00771         else:
00772             self.parent.statusbarWin['mask'].SetLabel('')
00773         
00774         Debug.msg (1, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
00775                    (render, renderVector, (stop-start)))
00776         
00777         return True
00778 
00779     def DrawCompRegionExtent(self):
00780         """!Draw computational region extent in the display
00781         
00782         Display region is drawn as a blue box inside the computational region,
00783         computational region inside a display region as a red box).
00784         """
00785         if hasattr(self, "regionCoords"):
00786             compReg = self.Map.GetRegion()
00787             dispReg = self.Map.GetCurrentRegion()
00788             reg = None
00789             if self.IsInRegion(dispReg, compReg):
00790                 self.polypen = wx.Pen(colour = wx.Colour(0, 0, 255, 128), width = 3, style = wx.SOLID)
00791                 reg = dispReg
00792             else:
00793                 self.polypen = wx.Pen(colour = wx.Colour(255, 0, 0, 128),
00794                                       width = 3, style = wx.SOLID)
00795                 reg = compReg
00796             
00797             self.regionCoords = []
00798             self.regionCoords.append((reg['w'], reg['n']))
00799             self.regionCoords.append((reg['e'], reg['n']))
00800             self.regionCoords.append((reg['e'], reg['s']))
00801             self.regionCoords.append((reg['w'], reg['s']))
00802             self.regionCoords.append((reg['w'], reg['n']))
00803             # draw region extent
00804             self.DrawLines(pdc = self.pdcDec, polycoords = self.regionCoords)
00805 
00806     def IsInRegion(self, region, refRegion):
00807         """!
00808         Test if 'region' is inside of 'refRegion'
00809 
00810         @param region input region
00811         @param refRegion reference region (e.g. computational region)
00812 
00813         @return True if region is inside of refRegion
00814         @return False 
00815         """
00816         if region['s'] >= refRegion['s'] and \
00817                 region['n'] <= refRegion['n'] and \
00818                 region['w'] >= refRegion['w'] and \
00819                 region['e'] <= refRegion['e']:
00820             return True
00821         
00822         return False
00823 
00824     def EraseMap(self):
00825         """!Erase map canvas
00826         """
00827         self.Draw(self.pdc, pdctype = 'clear')
00828         
00829         if hasattr(self, "digit"):
00830             self.Draw(self.pdcVector, pdctype = 'clear')
00831         
00832         self.Draw(self.pdcDec, pdctype = 'clear')
00833         self.Draw(self.pdcTmp, pdctype = 'clear')
00834         
00835     def DragMap(self, moveto):
00836         """!Drag the entire map image for panning.
00837         
00838         @param moveto dx,dy
00839         """
00840         dc = wx.BufferedDC(wx.ClientDC(self))
00841         dc.SetBackground(wx.Brush("White"))
00842         dc.Clear()
00843         
00844         self.dragimg = wx.DragImage(self.buffer)
00845         self.dragimg.BeginDrag((0, 0), self)
00846         self.dragimg.GetImageRect(moveto)
00847         self.dragimg.Move(moveto)
00848         
00849         self.dragimg.DoDrawImage(dc, moveto)
00850         self.dragimg.EndDrag()
00851         
00852     def DragItem(self, id, event):
00853         """!Drag an overlay decoration item
00854         """
00855         if id == 99 or id == '' or id == None: return
00856         Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
00857         x, y = self.lastpos
00858         dx = event.GetX() - x
00859         dy = event.GetY() - y
00860         self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
00861         r = self.pdc.GetIdBounds(id)
00862         if type(r) is list:
00863             r = wx.Rect(r[0], r[1], r[2], r[3])
00864         if id > 100: # text dragging
00865             rtop = (r[0],r[1]-r[3],r[2],r[3])
00866             r = r.Union(rtop)
00867             rleft = (r[0]-r[2],r[1],r[2],r[3])
00868             r = r.Union(rleft)
00869         self.pdc.TranslateId(id, dx, dy)
00870         
00871         r2 = self.pdc.GetIdBounds(id)
00872         if type(r2) is list:
00873             r2 = wx.Rect(r[0], r[1], r[2], r[3])
00874         if id > 100: # text
00875             self.textdict[id]['coords'] = r2
00876         r = r.Union(r2)
00877         r.Inflate(4,4)
00878         self.RefreshRect(r, False)
00879         self.lastpos = (event.GetX(), event.GetY())
00880                 
00881     def MouseDraw(self, pdc = None, begin = None, end = None):
00882         """!Mouse box or line from 'begin' to 'end'
00883         
00884         If not given from self.mouse['begin'] to self.mouse['end'].
00885         """
00886         if not pdc:
00887             return
00888         
00889         if begin is None:
00890             begin = self.mouse['begin']
00891         if end is None:
00892             end   = self.mouse['end']
00893         
00894         Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
00895                        (self.mouse['use'], self.mouse['box'],
00896                         begin[0], begin[1], end[0], end[1]))
00897         
00898         if self.mouse['box'] == "box":
00899             boxid = wx.ID_NEW
00900             mousecoords = [begin[0], begin[1],
00901                            end[0], end[1]]
00902             r = pdc.GetIdBounds(boxid)
00903             if type(r) is list:
00904                 r = wx.Rect(r[0], r[1], r[2], r[3])
00905             r.Inflate(4, 4)
00906             try:
00907                 pdc.ClearId(boxid)
00908             except:
00909                 pass
00910             self.RefreshRect(r, False)
00911             pdc.SetId(boxid)
00912             self.Draw(pdc, drawid = boxid, pdctype = 'box', coords = mousecoords)
00913         
00914         elif self.mouse['box'] == "line":
00915             self.lineid = wx.ID_NEW
00916             mousecoords = [begin[0], begin[1], \
00917                            end[0], end[1]]
00918             x1 = min(begin[0],end[0])
00919             x2 = max(begin[0],end[0])
00920             y1 = min(begin[1],end[1])
00921             y2 = max(begin[1],end[1])
00922             r = wx.Rect(x1,y1,x2-x1,y2-y1)
00923             r.Inflate(4,4)
00924             try:
00925                 pdc.ClearId(self.lineid)
00926             except:
00927                 pass
00928             self.RefreshRect(r, False)
00929             pdc.SetId(self.lineid)
00930             self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = mousecoords)
00931 
00932     def DrawLines(self, pdc = None, polycoords = None):
00933         """!Draw polyline in PseudoDC
00934         
00935         Set self.pline to wx.NEW_ID + 1
00936         
00937         polycoords - list of polyline vertices, geographical coordinates
00938         (if not given, self.polycoords is used)
00939         """
00940         if not pdc:
00941             pdc = self.pdcTmp
00942         
00943         if not polycoords:
00944             polycoords = self.polycoords
00945         
00946         if len(polycoords) > 0:
00947             self.plineid = wx.ID_NEW + 1
00948             # convert from EN to XY
00949             coords = []
00950             for p in polycoords:
00951                 coords.append(self.Cell2Pixel(p))
00952 
00953             self.Draw(pdc, drawid = self.plineid, pdctype = 'polyline', coords = coords)
00954             
00955             Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
00956                            (coords, self.plineid))
00957             
00958             return self.plineid
00959         
00960         return -1
00961 
00962     def DrawCross(self, pdc, coords, size, rotation = 0,
00963                   text = None, textAlign = 'lr', textOffset = (5, 5)):
00964         """!Draw cross in PseudoDC
00965 
00966         @todo implement rotation
00967 
00968         @param pdc PseudoDC
00969         @param coord center coordinates
00970         @param rotation rotate symbol
00971         @param text draw also text (text, font, color, rotation)
00972         @param textAlign alignment (default 'lower-right')
00973         @textOffset offset for text (from center point)
00974         """
00975         Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
00976                   (pdc, coords, size))
00977         coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
00978                        (coords[0], coords[1] - size, coords[0], coords[1] + size))
00979 
00980         self.lineid = wx.NewId()
00981         for lineCoords in coordsCross:
00982             self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = lineCoords)
00983         
00984         if not text:
00985             return self.lineid
00986         
00987         if textAlign == 'ul':
00988             coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
00989         elif textAlign == 'ur':
00990             coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
00991         elif textAlign == 'lr':
00992             coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
00993         else:
00994             coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
00995         
00996         self.Draw(pdc, img = text,
00997                   pdctype = 'text', coords = coord)
00998         
00999         return self.lineid
01000 
01001     def MouseActions(self, event):
01002         """!Mouse motion and button click notifier
01003         """
01004         if not self.processMouse:
01005             return
01006         
01007         # zoom with mouse wheel
01008         if event.GetWheelRotation() != 0:
01009             self.OnMouseWheel(event)
01010             
01011         # left mouse button pressed
01012         elif event.LeftDown():
01013             self.OnLeftDown(event)
01014         
01015         # left mouse button released
01016         elif event.LeftUp():
01017             self.OnLeftUp(event)
01018         
01019         # dragging
01020         elif event.Dragging():
01021             self.OnDragging(event)
01022         
01023         # double click
01024         elif event.ButtonDClick():
01025             self.OnButtonDClick(event)
01026         
01027         # middle mouse button pressed
01028         elif event.MiddleDown():
01029             self.OnMiddleDown(event)
01030         
01031         # middle mouse button relesed
01032         elif event.MiddleUp():
01033             self.OnMiddleUp(event)
01034         
01035         # right mouse button pressed
01036         elif event.RightDown():
01037             self.OnRightDown(event)
01038         
01039         # right mouse button released
01040         elif event.RightUp():
01041             self.OnRightUp(event)
01042         
01043         elif event.Entering():
01044             self.OnMouseEnter(event)
01045         
01046         elif event.Moving():
01047             self.OnMouseMoving(event)
01048                 
01049     def OnMouseWheel(self, event):
01050         """!Mouse wheel moved
01051         """
01052         self.processMouse = False
01053         current  = event.GetPositionTuple()[:]
01054         wheel = event.GetWheelRotation()
01055         Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
01056         # zoom 1/2 of the screen, centered to current mouse position (TODO: settings)
01057         begin = (current[0] - self.Map.width / 4,
01058                  current[1] - self.Map.height / 4)
01059         end   = (current[0] + self.Map.width / 4,
01060                  current[1] + self.Map.height / 4)
01061         
01062         if wheel > 0:
01063             zoomtype = 1
01064         else:
01065             zoomtype = -1
01066         
01067         # zoom
01068         self.Zoom(begin, end, zoomtype)
01069         
01070         # redraw map
01071         self.UpdateMap()
01072         
01073         # update statusbar
01074         self.parent.StatusbarUpdate()
01075         
01076         self.Refresh()
01077         self.processMouse = True
01078         
01079     def OnDragging(self, event):
01080         """!Mouse dragging
01081         """
01082         Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
01083         current  = event.GetPositionTuple()[:]
01084         previous = self.mouse['begin']
01085         move = (current[0] - previous[0],
01086                 current[1] - previous[1])
01087         
01088         if hasattr(self, "digit"):
01089             digitToolbar = self.toolbar
01090         else:
01091             digitToolbar = None
01092         
01093         # dragging or drawing box with left button
01094         if self.mouse['use'] == 'pan' or \
01095                 event.MiddleIsDown():
01096             self.DragMap(move)
01097         
01098         # dragging decoration overlay item
01099         elif (self.mouse['use'] == 'pointer' and 
01100                 not digitToolbar and 
01101                 self.dragid != None):
01102             self.DragItem(self.dragid, event)
01103         
01104         # dragging anything else - rubber band box or line
01105         else:
01106             if (self.mouse['use'] == 'pointer' and 
01107                 not digitToolbar):
01108                 return
01109             
01110             self.mouse['end'] = event.GetPositionTuple()[:]
01111             if (event.LeftIsDown() and 
01112                 not (digitToolbar and 
01113                     digitToolbar.GetAction() in ("moveLine",) and 
01114                      self.digit.GetDisplay().GetSelected() > 0)):
01115                 self.MouseDraw(pdc = self.pdcTmp)
01116         
01117     def OnLeftDown(self, event):
01118         """!Left mouse button pressed
01119         """
01120         Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
01121                    self.mouse["use"])
01122         
01123         self.mouse['begin'] = event.GetPositionTuple()[:]
01124         
01125         if self.mouse["use"] in ["measure", "profile"]:
01126             # measure or profile
01127             if len(self.polycoords) == 0:
01128                 self.mouse['end'] = self.mouse['begin']
01129                 self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
01130                 self.ClearLines(pdc=self.pdcTmp)
01131             else:
01132                 self.mouse['begin'] = self.mouse['end']
01133         
01134         elif self.mouse['use'] == 'zoom':
01135             pass
01136         
01137         # vector digizer
01138         elif self.mouse["use"] == "pointer" and \
01139                 hasattr(self, "digit"):
01140             if event.ControlDown():
01141                 self.OnLeftDownUndo(event)
01142             else:
01143                 self._onLeftDown(event)
01144         
01145         elif self.mouse['use'] == 'pointer':
01146             # get decoration or text id
01147             self.idlist = []
01148             self.dragid = ''
01149             self.lastpos = self.mouse['begin']
01150             idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
01151                                           self.hitradius)
01152             if 99 in idlist:
01153                 idlist.remove(99)                             
01154             if idlist != []:
01155                 self.dragid = idlist[0] #drag whatever is on top
01156         else:
01157             pass
01158         
01159         event.Skip()
01160         
01161     def OnLeftUp(self, event):
01162         """!Left mouse button released
01163         """
01164         Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
01165                        self.mouse["use"])
01166         
01167         self.mouse['end'] = event.GetPositionTuple()[:]
01168         
01169         if self.mouse['use'] in ["zoom", "pan"]:
01170             # set region in zoom or pan
01171             begin = self.mouse['begin']
01172             end = self.mouse['end']
01173             
01174             if self.mouse['use'] == 'zoom':
01175                 # set region for click (zero-width box)
01176                 if begin[0] - end[0] == 0 or \
01177                         begin[1] - end[1] == 0:
01178                     # zoom 1/2 of the screen (TODO: settings)
01179                     begin = (end[0] - self.Map.width / 4,
01180                              end[1] - self.Map.height / 4)
01181                     end   = (end[0] + self.Map.width / 4,
01182                              end[1] + self.Map.height / 4)
01183             
01184             self.Zoom(begin, end, self.zoomtype)
01185 
01186             # redraw map
01187             self.UpdateMap(render = True)
01188             
01189             # update statusbar
01190             self.parent.StatusbarUpdate()
01191             
01192         elif self.mouse["use"] == "query":
01193             # querying
01194             layers = self.GetSelectedLayer(multi = True)
01195             isRaster = False
01196             nVectors = 0
01197             for l in layers:
01198                 if l.GetType() == 'raster':
01199                     isRaster = True
01200                     break
01201                 if l.GetType() == 'vector':
01202                     nVectors += 1
01203             
01204             if isRaster or nVectors > 1:
01205                 self.parent.QueryMap(self.mouse['begin'][0],self.mouse['begin'][1])
01206             else:
01207                 self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
01208                 # clear temp canvas
01209                 self.UpdateMap(render = False, renderVector = False)
01210             
01211         elif self.mouse["use"] == "queryVector":
01212             # editable mode for vector map layers
01213             self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
01214             
01215             # clear temp canvas
01216             self.UpdateMap(render = False, renderVector = False)
01217         
01218         elif self.mouse["use"] in ["measure", "profile"]:
01219             # measure or profile
01220             if self.mouse["use"] == "measure":
01221                 self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
01222             
01223             self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
01224             self.ClearLines(pdc = self.pdcTmp)
01225             self.DrawLines(pdc = self.pdcTmp)
01226         
01227         elif self.mouse["use"] == "pointer" and \
01228                 self.parent.GetLayerManager().gcpmanagement:
01229             # -> GCP manager
01230             if self.parent.toolbars['gcpdisp']:
01231                 coord = self.Pixel2Cell(self.mouse['end'])
01232                 if self.parent.MapWindow == self.parent.SrcMapWindow:
01233                     coordtype = 'source'
01234                 else:
01235                     coordtype = 'target'
01236                 
01237                 self.parent.GetLayerManager().gcpmanagement.SetGCPData(coordtype, coord, self, confirm = True)
01238                 self.UpdateMap(render = False, renderVector = False)
01239         
01240         elif self.mouse["use"] == "pointer" and \
01241                 self.parent.GetLayerManager().georectifying:
01242             # -> georectifying
01243             coord = self.Pixel2Cell(self.mouse['end'])
01244             if self.parent.toolbars['georect']:
01245                 coordtype = 'gcpcoord'
01246             else:
01247                 coordtype = 'mapcoord'
01248             
01249             self.parent.GetLayerManager().georectifying.SetGCPData(coordtype, coord, self)
01250             self.UpdateMap(render = False, renderVector = False)
01251             
01252         elif self.mouse["use"] == "pointer" and \
01253                 hasattr(self, "digit"):
01254             self._onLeftUp(event)
01255             
01256         elif (self.mouse['use'] == 'pointer' and 
01257                 self.dragid >= 0):
01258             # end drag of overlay decoration
01259             
01260             if self.dragid < 99 and self.dragid in self.overlays:
01261                 self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
01262             elif self.dragid > 100 and self.dragid in self.textdict:
01263                 self.textdict[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
01264             else:
01265                 pass
01266             self.dragid = None
01267             self.currtxtid = None
01268         
01269     def OnButtonDClick(self, event):
01270         """!Mouse button double click
01271         """
01272         Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
01273                    self.mouse["use"])
01274         
01275         if self.mouse["use"] == "measure":
01276             # measure
01277             self.ClearLines(pdc=self.pdcTmp)
01278             self.polycoords = []
01279             self.mouse['use'] = 'pointer'
01280             self.mouse['box'] = 'point'
01281             self.mouse['end'] = [0, 0]
01282             self.Refresh()
01283             self.SetCursor(self.parent.cursors["default"])
01284         
01285         elif self.mouse["use"] != "profile" or \
01286                 (self.mouse['use'] != 'pointer' and \
01287                      hasattr(self, "digit")):
01288                # select overlay decoration options dialog
01289             clickposition = event.GetPositionTuple()[:]
01290             idlist  = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
01291             if idlist == []:
01292                 return
01293             self.dragid = idlist[0]
01294 
01295             # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
01296             if self.dragid > 100:
01297                 self.currtxtid = self.dragid
01298                 self.parent.OnAddText(None)
01299             elif self.dragid == 0:
01300                 self.parent.OnAddBarscale(None)
01301             elif self.dragid == 1:
01302                 self.parent.OnAddLegend(None)
01303         
01304     def OnRightDown(self, event):
01305         """!Right mouse button pressed
01306         """
01307         Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
01308                    self.mouse["use"])
01309         
01310         if hasattr(self, "digit"):
01311             self._onRightDown(event)
01312         
01313         event.Skip()
01314         
01315     def OnRightUp(self, event):
01316         """!Right mouse button released
01317         """
01318         Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
01319                    self.mouse["use"])
01320         
01321         if hasattr(self, "digit"):
01322             self._onRightUp(event)
01323         
01324         self.redrawAll = True
01325         self.Refresh()
01326         
01327         event.Skip()
01328         
01329     def OnMiddleDown(self, event):
01330         """!Middle mouse button pressed
01331         """
01332         if not event:
01333             return
01334         
01335         self.mouse['begin'] = event.GetPositionTuple()[:]
01336         
01337     def OnMiddleUp(self, event):
01338         """!Middle mouse button released
01339         """
01340         self.mouse['end'] = event.GetPositionTuple()[:]
01341         
01342         # set region in zoom or pan
01343         begin = self.mouse['begin']
01344         end   = self.mouse['end']
01345         
01346         self.Zoom(begin, end, 0) # no zoom
01347         
01348         # redraw map
01349         self.UpdateMap(render = True)
01350         
01351         # update statusbar
01352         self.parent.StatusbarUpdate()
01353         
01354     def OnMouseEnter(self, event):
01355         """!Mouse entered window and no mouse buttons were pressed
01356         """
01357         if self.parent.GetLayerManager().gcpmanagement:
01358             if self.parent.toolbars['gcpdisp']:
01359                 if not self.parent.MapWindow == self:
01360                     self.parent.MapWindow = self
01361                     self.parent.Map = self.Map
01362                     self.parent.UpdateActive(self)
01363                     # needed for wingrass
01364                     self.SetFocus()
01365         else:
01366             event.Skip()
01367         
01368     def OnMouseMoving(self, event):
01369         """!Motion event and no mouse buttons were pressed
01370         """
01371         if self.mouse["use"] == "pointer" and \
01372                 hasattr(self, "digit"):
01373             self._onMouseMoving(event)
01374         
01375         event.Skip()
01376         
01377     def ClearLines(self, pdc = None):
01378         """!Clears temporary drawn lines from PseudoDC
01379         """
01380         if not pdc:
01381             pdc = self.pdcTmp
01382         try:
01383             pdc.ClearId(self.lineid)
01384             pdc.RemoveId(self.lineid)
01385         except:
01386             pass
01387         
01388         try:
01389             pdc.ClearId(self.plineid)
01390             pdc.RemoveId(self.plineid)
01391         except:
01392             pass
01393         
01394         Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
01395                   (self.lineid, self.plineid))
01396         
01397         return True
01398 
01399     def Pixel2Cell(self, (x, y)):
01400         """!Convert image coordinates to real word coordinates
01401         
01402         @param x, y image coordinates
01403         
01404         @return easting, northing
01405         @return None on error
01406         """
01407         try:
01408             x = int(x)
01409             y = int(y)
01410         except:
01411             return None
01412         
01413         if self.Map.region["ewres"] > self.Map.region["nsres"]:
01414             res = self.Map.region["ewres"]
01415         else:
01416             res = self.Map.region["nsres"]
01417         
01418         w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
01419         n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
01420         
01421         east  = w + x * res
01422         north = n - y * res
01423         
01424         return (east, north)
01425     
01426     def Cell2Pixel(self, (east, north)):
01427         """!Convert real word coordinates to image coordinates
01428         """
01429         try:
01430             east  = float(east)
01431             north = float(north)
01432         except:
01433             return None
01434         
01435         if self.Map.region["ewres"] > self.Map.region["nsres"]:
01436             res = self.Map.region["ewres"]
01437         else:
01438             res = self.Map.region["nsres"]
01439         
01440         w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
01441         n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
01442         
01443         x = (east  - w) / res
01444         y = (n - north) / res
01445         
01446         return (x, y)
01447     
01448     def Zoom(self, begin, end, zoomtype):
01449         """!
01450         Calculates new region while (un)zoom/pan-ing
01451         """
01452         x1, y1 = begin
01453         x2, y2 = end
01454         newreg = {}
01455         
01456         # threshold - too small squares do not make sense
01457         # can only zoom to windows of > 5x5 screen pixels
01458         if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
01459             if x1 > x2:
01460                 x1, x2 = x2, x1
01461             if y1 > y2:
01462                 y1, y2 = y2, y1
01463             
01464             # zoom in
01465             if zoomtype > 0:
01466                 newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
01467                 newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
01468             
01469             # zoom out
01470             elif zoomtype < 0:
01471                 newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
01472                 newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + 2 * \
01473                                                                 (self.Map.width  - x2),
01474                                                             self.Map.height + 2 * \
01475                                                                 (self.Map.height - y2)))
01476         # pan
01477         elif zoomtype == 0:
01478             dx = x1 - x2
01479             dy = y1 - y2
01480             if dx == 0 and dy == 0:
01481                 dx = x1 - self.Map.width / 2
01482                 dy = y1 - self.Map.height / 2
01483             newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
01484             newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width  + dx,
01485                                                         self.Map.height + dy))
01486         
01487         # if new region has been calculated, set the values
01488         if newreg != {}:
01489             # LL locations
01490             if self.parent.Map.projinfo['proj'] == 'll':
01491                 if newreg['n'] > 90.0:
01492                     newreg['n'] = 90.0
01493                 if newreg['s'] < -90.0:
01494                     newreg['s'] = -90.0
01495             
01496             ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
01497             cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
01498             
01499             # calculate new center point and display resolution
01500             self.Map.region['center_easting'] = ce
01501             self.Map.region['center_northing'] = cn
01502             self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width
01503             self.Map.region["nsres"] = (newreg['n'] - newreg['s']) / self.Map.height
01504             self.Map.AlignExtentFromDisplay()
01505             
01506             if hasattr(self, "digit") and \
01507                     hasattr(self, "moveInfo"):
01508                 self._zoom(None)
01509             
01510             self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
01511                              self.Map.region['e'], self.Map.region['w'])
01512         
01513         if self.redrawAll is False:
01514             self.redrawAll = True
01515         
01516     def ZoomBack(self):
01517         """!Zoom to previous extents in zoomhistory list
01518         """
01519         zoom = list()
01520         
01521         if len(self.zoomhistory) > 1:
01522             self.zoomhistory.pop()
01523             zoom = self.zoomhistory[-1]
01524         
01525         # disable tool if stack is empty
01526         if len(self.zoomhistory) < 2: # disable tool
01527             if self.parent.GetName() == 'MapWindow':
01528                 toolbar = self.parent.toolbars['map']
01529             elif self.parent.GetName() == 'GRMapWindow':
01530                 toolbar = self.parent.toolbars['georect']
01531             elif self.parent.GetName() == 'GCPMapWindow':
01532                 toolbar = self.parent.toolbars['gcpdisp']
01533             
01534             toolbar.Enable('zoomback', enable = False)
01535         
01536         # zoom to selected region
01537         self.Map.GetRegion(n = zoom[0], s = zoom[1],
01538                            e = zoom[2], w = zoom[3],
01539                            update = True)
01540         # update map
01541         self.UpdateMap()
01542         
01543         # update statusbar
01544         self.parent.StatusbarUpdate()
01545 
01546     def ZoomHistory(self, n, s, e, w):
01547         """!Manages a list of last 10 zoom extents
01548 
01549         @param n,s,e,w north, south, east, west
01550 
01551         @return removed history item if exists (or None)
01552         """
01553         removed = None
01554         self.zoomhistory.append((n,s,e,w))
01555         
01556         if len(self.zoomhistory) > 10:
01557             removed = self.zoomhistory.pop(0)
01558         
01559         if removed:
01560             Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
01561                       (self.zoomhistory, removed))
01562         else:
01563             Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
01564                       (self.zoomhistory))
01565         
01566         # update toolbar
01567         if len(self.zoomhistory) > 1:
01568             enable = True
01569         else:
01570             enable = False
01571         
01572         if self.parent.GetName() == 'MapWindow':
01573             toolbar = self.parent.toolbars['map']
01574         elif self.parent.GetName() == 'GRMapWindow':
01575             toolbar = self.parent.toolbars['georect']
01576         elif self.parent.GetName() == 'GCPMapWindow':
01577             toolbar = self.parent.toolbars['gcpdisp']
01578         
01579         toolbar.Enable('zoomback', enable)
01580         
01581         return removed
01582 
01583     def ResetZoomHistory(self):
01584         """!Reset zoom history"""
01585         self.zoomhistory = list()
01586                 
01587     def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
01588         """!Set display extents to match selected raster
01589         or vector map(s).
01590 
01591         @param layers list of layers to be zoom to
01592         @param ignoreNulls True to ignore null-values (valid only for rasters)
01593         @param render True to re-render display
01594         """
01595         zoomreg = {}
01596         
01597         if not layers:
01598             layers = self.GetSelectedLayer(multi = True)
01599         
01600         if not layers:
01601             return
01602         
01603         rast = []
01604         vect = []
01605         updated = False
01606         for l in layers:
01607             # only raster/vector layers are currently supported
01608             if l.type == 'raster':
01609                 rast.append(l.GetName())
01610             elif l.type == 'vector':
01611                 if hasattr(self, "digit") and \
01612                         self.toolbar.GetLayer() == l:
01613                     w, s, b, e, n, t = self.digit.GetDisplay().GetMapBoundingBox()
01614                     self.Map.GetRegion(n = n, s = s, w = w, e = e,
01615                                        update = True)
01616                     updated = True
01617                 else:
01618                     vect.append(l.name)
01619             elif l.type == 'rgb':
01620                 for rname in l.GetName().splitlines():
01621                     rast.append(rname)
01622             
01623         if not updated:
01624             self.Map.GetRegion(rast = rast,
01625                                vect = vect,
01626                                update = True)
01627         
01628         self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
01629                          self.Map.region['e'], self.Map.region['w'])
01630         
01631         if render:
01632             self.UpdateMap()
01633         
01634         self.parent.StatusbarUpdate()
01635         
01636     def ZoomToWind(self):
01637         """!Set display geometry to match computational region
01638         settings (set with g.region)
01639         """
01640         self.Map.region = self.Map.GetRegion()
01641         
01642         self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
01643                          self.Map.region['e'], self.Map.region['w'])
01644         
01645         self.UpdateMap()
01646         
01647         self.parent.StatusbarUpdate()
01648 
01649     def ZoomToDefault(self):
01650         """!Set display geometry to match default region settings
01651         """
01652         self.Map.region = self.Map.GetRegion(default = True)
01653         self.Map.AdjustRegion() # aling region extent to the display
01654 
01655         self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
01656                          self.Map.region['e'], self.Map.region['w'])
01657         
01658         self.UpdateMap()
01659         
01660         self.parent.StatusbarUpdate()
01661         
01662     def DisplayToWind(self):
01663         """!Set computational region (WIND file) to match display
01664         extents
01665         """
01666         tmpreg = os.getenv("GRASS_REGION")
01667         if tmpreg:
01668             del os.environ["GRASS_REGION"]
01669         
01670         # We ONLY want to set extents here. Don't mess with resolution. Leave that
01671         # for user to set explicitly with g.region
01672         new = self.Map.AlignResolution()
01673         gcmd.RunCommand('g.region',
01674                         parent = self,
01675                         overwrite = True,
01676                         n = new['n'],
01677                         s = new['s'],
01678                         e = new['e'],
01679                         w = new['w'],
01680                         rows = int(new['rows']),
01681                         cols = int(new['cols']))
01682         
01683         if tmpreg:
01684             os.environ["GRASS_REGION"] = tmpreg
01685         
01686     def ZoomToSaved(self):
01687         """!Set display geometry to match extents in
01688         saved region file
01689         """
01690         dlg = gdialogs.SavedRegion(parent = self,
01691                                    title = _("Zoom to saved region extents"),
01692                                    loadsave='load')
01693         
01694         if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
01695             dlg.Destroy()
01696             return
01697         
01698         if not grass.find_file(name = dlg.wind, element = 'windows')['name']:
01699             wx.MessageBox(parent = self,
01700                           message = _("Region <%s> not found. Operation canceled.") % dlg.wind,
01701                           caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
01702             dlg.Destroy()
01703             return
01704         
01705         self.Map.GetRegion(regionName = dlg.wind,
01706                            update = True)
01707         
01708         dlg.Destroy()
01709         
01710         self.ZoomHistory(self.Map.region['n'],
01711                          self.Map.region['s'],
01712                          self.Map.region['e'],
01713                          self.Map.region['w'])
01714         
01715         self.UpdateMap()
01716                 
01717     def SaveDisplayRegion(self):
01718         """!Save display extents to named region file.
01719         """
01720         dlg = gdialogs.SavedRegion(parent = self,
01721                                    title = _("Save display extents to region file"),
01722                                    loadsave='save')
01723         
01724         if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
01725             dlg.Destroy()
01726             return
01727         
01728         # test to see if it already exists and ask permission to overwrite
01729         if grass.find_file(name = dlg.wind, element = 'windows')['name']:
01730             overwrite = wx.MessageBox(parent = self,
01731                                       message = _("Region file <%s> already exists. "
01732                                                   "Do you want to overwrite it?") % (dlg.wind),
01733                                       caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
01734             if (overwrite == wx.YES):
01735                 self.SaveRegion(dlg.wind)
01736         else:
01737             self.SaveRegion(dlg.wind)
01738         
01739         dlg.Destroy()
01740         
01741     def SaveRegion(self, wind):
01742         """!Save region settings
01743         
01744         @param wind region name
01745         """
01746         new = self.Map.GetCurrentRegion()
01747         
01748         tmpreg = os.getenv("GRASS_REGION")
01749         if tmpreg:
01750             del os.environ["GRASS_REGION"]
01751         
01752         gcmd.RunCommand('g.region',
01753                         overwrite = True,
01754                         parent = self,
01755                         flags = 'u',
01756                         n = new['n'],
01757                         s = new['s'],
01758                         e = new['e'],
01759                         w = new['w'],
01760                         rows = int(new['rows']),
01761                         cols = int(new['cols']),
01762                         save = wind)
01763         
01764         if tmpreg:
01765             os.environ["GRASS_REGION"] = tmpreg
01766         
01767     def Distance(self, beginpt, endpt, screen = True):
01768         """!Calculete distance
01769         
01770         Ctypes required for LL-locations
01771         
01772         @param beginpt first point
01773         @param endpt second point
01774         @param screen True for screen coordinates otherwise EN
01775         """
01776         if screen:
01777             e1, n1 = self.Pixel2Cell(beginpt)
01778             e2, n2 = self.Pixel2Cell(endpt)
01779         else:
01780             e1, n1 = beginpt
01781             e2, n2 = endpt
01782             
01783         dEast  = (e2 - e1)
01784         dNorth = (n2 - n1)
01785         
01786         if self.parent.Map.projinfo['proj'] == 'll' and haveCtypes:
01787             dist = gislib.G_distance(e1, n1, e2, n2)
01788         else:
01789             dist = math.sqrt(math.pow((dEast), 2) + math.pow((dNorth), 2))
01790         
01791         return (dist, (dEast, dNorth))
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines