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