|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
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))