GRASS Programmer's Manual
6.4.2(2012)
|
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))