|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
00001 """! 00002 @package gcp.mapdisplay 00003 00004 @brief Display to manage ground control points with two toolbars, one 00005 for various display management functions, one for manipulating GCPs. 00006 00007 Classes: 00008 - mapdisplay::MapFrame 00009 00010 (C) 2006-2011 by the GRASS Development Team 00011 00012 This program is free software under the GNU General Public License 00013 (>=v2). Read the file COPYING that comes with GRASS for details. 00014 00015 @author Markus Metz 00016 """ 00017 00018 import os 00019 import math 00020 import platform 00021 00022 from core import globalvar 00023 import wx 00024 import wx.aui 00025 00026 from core.render import EVT_UPDATE_PRGBAR 00027 from mapdisp.toolbars import MapToolbar 00028 from gcp.toolbars import GCPDisplayToolbar, GCPManToolbar 00029 from mapdisp.gprint import PrintOptions 00030 from core.gcmd import GMessage 00031 from gui_core.dialogs import GetImageHandlers, ImageSizeDialog 00032 from gui_core.mapdisp import MapFrameBase 00033 from core.settings import UserSettings 00034 from mapdisp.mapwindow import BufferedWindow 00035 00036 import mapdisp.statusbar as sb 00037 00038 # for standalone app 00039 cmdfilename = None 00040 00041 class MapFrame(MapFrameBase): 00042 """!Main frame for map display window. Drawing takes place in 00043 child double buffered drawing window. 00044 """ 00045 def __init__(self, parent=None, title=_("GRASS GIS Manage Ground Control Points"), 00046 toolbars=["gcpdisp"], tree=None, notebook=None, lmgr=None, 00047 page=None, Map=None, auimgr=None, name = 'GCPMapWindow', **kwargs): 00048 """!Main map display window with toolbars, statusbar and 00049 DrawWindow 00050 00051 @param toolbars array of activated toolbars, e.g. ['map', 'digit'] 00052 @param tree reference to layer tree 00053 @param notebook control book ID in Layer Manager 00054 @param lmgr Layer Manager 00055 @param page notebook page with layer tree 00056 @param Map instance of render.Map 00057 @param auimgs AUI manager 00058 @param kwargs wx.Frame attribures 00059 """ 00060 00061 MapFrameBase.__init__(self, parent = parent, title = title, toolbars = toolbars, 00062 Map = Map, auimgr = auimgr, name = name, **kwargs) 00063 00064 self._layerManager = lmgr # Layer Manager object 00065 self.tree = tree # Layer Manager layer tree object 00066 self.page = page # Notebook page holding the layer tree 00067 self.layerbook = notebook # Layer Manager layer tree notebook 00068 # 00069 # Add toolbars 00070 # 00071 for toolb in toolbars: 00072 self.AddToolbar(toolb) 00073 00074 self.activemap = self.toolbars['gcpdisp'].togglemap 00075 self.activemap.SetSelection(0) 00076 00077 self.SrcMap = self.grwiz.SrcMap # instance of render.Map 00078 self.TgtMap = self.grwiz.TgtMap # instance of render.Map 00079 self._mgr.SetDockSizeConstraint(0.5, 0.5) 00080 00081 # 00082 # Add statusbar 00083 # 00084 00085 # items for choice 00086 self.statusbarItems = [sb.SbCoordinates, 00087 sb.SbRegionExtent, 00088 sb.SbCompRegionExtent, 00089 sb.SbShowRegion, 00090 sb.SbResolution, 00091 sb.SbDisplayGeometry, 00092 sb.SbMapScale, 00093 sb.SbProjection, 00094 sb.SbGoToGCP, 00095 sb.SbRMSError] 00096 00097 00098 # create statusbar and its manager 00099 statusbar = self.CreateStatusBar(number = 4, style = 0) 00100 statusbar.SetStatusWidths([-5, -2, -1, -1]) 00101 self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar) 00102 00103 # fill statusbar manager 00104 self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar) 00105 self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2)) 00106 self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3)) 00107 00108 self.statusbarManager.SetMode(8) # goto GCP 00109 self.statusbarManager.Update() 00110 00111 00112 # 00113 # Init map display (buffered DC & set default cursor) 00114 # 00115 self.grwiz.SwitchEnv('source') 00116 self.SrcMapWindow = BufferedWindow(self, id=wx.ID_ANY, 00117 Map=self.SrcMap, tree=self.tree, lmgr=self._layerManager) 00118 00119 self.grwiz.SwitchEnv('target') 00120 self.TgtMapWindow = BufferedWindow(self, id=wx.ID_ANY, 00121 Map=self.TgtMap, tree=self.tree, lmgr=self._layerManager) 00122 self.MapWindow = self.SrcMapWindow 00123 self.Map = self.SrcMap 00124 self.SrcMapWindow.SetCursor(self.cursors["cross"]) 00125 self.TgtMapWindow.SetCursor(self.cursors["cross"]) 00126 00127 # 00128 # initialize region values 00129 # 00130 self._initMap(map = self.SrcMap) 00131 self._initMap(map = self.TgtMap) 00132 00133 # 00134 # Bind various events 00135 # 00136 self.Bind(wx.EVT_ACTIVATE, self.OnFocus) 00137 self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress) 00138 self.Bind(wx.EVT_SIZE, self.OnDispResize) 00139 self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive) 00140 00141 # 00142 # Update fancy gui style 00143 # 00144 # AuiManager wants a CentrePane, workaround to get two equally sized windows 00145 self.list = self.CreateGCPList() 00146 00147 #self.SrcMapWindow.SetSize((300, 300)) 00148 #self.TgtMapWindow.SetSize((300, 300)) 00149 self.list.SetSize((100, 150)) 00150 self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo(). 00151 Name("gcplist").Caption(_("GCP List")).LeftDockable(False). 00152 RightDockable(False).PinButton().FloatingSize((600,200)). 00153 CloseButton(False).DestroyOnClose(True). 00154 Top().Layer(1).MinSize((200,100))) 00155 self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo(). 00156 Name("source").Caption(_("Source Display")).Dockable(False). 00157 CloseButton(False).DestroyOnClose(True).Floatable(False). 00158 Centre()) 00159 self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo(). 00160 Name("target").Caption(_("Target Display")).Dockable(False). 00161 CloseButton(False).DestroyOnClose(True).Floatable(False). 00162 Right().Layer(0)) 00163 00164 srcwidth, srcheight = self.SrcMapWindow.GetSize() 00165 tgtwidth, tgtheight = self.TgtMapWindow.GetSize() 00166 srcwidth = (srcwidth + tgtwidth) / 2 00167 self._mgr.GetPane("target").Hide() 00168 self._mgr.Update() 00169 self._mgr.GetPane("source").BestSize((srcwidth, srcheight)) 00170 self._mgr.GetPane("target").BestSize((srcwidth, srcheight)) 00171 if self.show_target: 00172 self._mgr.GetPane("target").Show() 00173 else: 00174 self.activemap.Enable(False) 00175 # needed by Mac OS, does not harm on Linux, breaks display on Windows 00176 if platform.system() != 'Windows': 00177 self._mgr.Update() 00178 00179 # 00180 # Init print module and classes 00181 # 00182 self.printopt = PrintOptions(self, self.MapWindow) 00183 00184 # 00185 # Initialization of digitization tool 00186 # 00187 self.digit = None 00188 00189 # set active map 00190 self.MapWindow = self.SrcMapWindow 00191 self.Map = self.SrcMap 00192 00193 # do not init zoom history here, that happens when zooming to map(s) 00194 00195 # 00196 # Re-use dialogs 00197 # 00198 self.dialogs = {} 00199 self.dialogs['attributes'] = None 00200 self.dialogs['category'] = None 00201 self.dialogs['barscale'] = None 00202 self.dialogs['legend'] = None 00203 00204 self.decorationDialog = None # decoration/overlays 00205 00206 def AddToolbar(self, name): 00207 """!Add defined toolbar to the window 00208 00209 Currently known toolbars are: 00210 - 'map' - basic map toolbar 00211 - 'vdigit' - vector digitizer 00212 - 'gcpdisp' - GCP Manager, Display 00213 - 'gcpman' - GCP Manager, points management 00214 - 'nviz' - 3D view mode 00215 """ 00216 # default toolbar 00217 if name == "map": 00218 self.toolbars['map'] = MapToolbar(self, self.Map) 00219 00220 self._mgr.AddPane(self.toolbars['map'], 00221 wx.aui.AuiPaneInfo(). 00222 Name("maptoolbar").Caption(_("Map Toolbar")). 00223 ToolbarPane().Top(). 00224 LeftDockable(False).RightDockable(False). 00225 BottomDockable(False).TopDockable(True). 00226 CloseButton(False).Layer(2). 00227 BestSize((self.toolbars['map'].GetSize()))) 00228 00229 # GCP display 00230 elif name == "gcpdisp": 00231 self.toolbars['gcpdisp'] = GCPDisplayToolbar(self) 00232 00233 self._mgr.AddPane(self.toolbars['gcpdisp'], 00234 wx.aui.AuiPaneInfo(). 00235 Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")). 00236 ToolbarPane().Top(). 00237 LeftDockable(False).RightDockable(False). 00238 BottomDockable(False).TopDockable(True). 00239 CloseButton(False).Layer(2)) 00240 00241 if self.show_target == False: 00242 self.toolbars['gcpdisp'].Enable('zoommenu', enable = False) 00243 00244 self.toolbars['gcpman'] = GCPManToolbar(self) 00245 00246 self._mgr.AddPane(self.toolbars['gcpman'], 00247 wx.aui.AuiPaneInfo(). 00248 Name("gcpmanagertoolbar").Caption(_("GCP Manager toolbar")). 00249 ToolbarPane().Top().Row(1). 00250 LeftDockable(False).RightDockable(False). 00251 BottomDockable(False).TopDockable(True). 00252 CloseButton(False).Layer(2)) 00253 00254 self._mgr.Update() 00255 00256 def OnUpdateProgress(self, event): 00257 """ 00258 Update progress bar info 00259 """ 00260 self.GetProgressBar().SetValue(event.value) 00261 00262 event.Skip() 00263 00264 def OnFocus(self, event): 00265 """ 00266 Change choicebook page to match display. 00267 Or set display for georectifying 00268 """ 00269 if self._layerManager and \ 00270 self._layerManager.gcpmanagement: 00271 # in GCP Management, set focus to current MapWindow for mouse actions 00272 self.OnPointer(event) 00273 self.MapWindow.SetFocus() 00274 else: 00275 # change bookcontrol page to page associated with display 00276 # GCP Manager: use bookcontrol? 00277 if self.page: 00278 pgnum = self.layerbook.GetPageIndex(self.page) 00279 if pgnum > -1: 00280 self.layerbook.SetSelection(pgnum) 00281 00282 event.Skip() 00283 00284 def OnDraw(self, event): 00285 """!Re-display current map composition 00286 """ 00287 self.MapWindow.UpdateMap(render = False) 00288 00289 def OnRender(self, event): 00290 """!Re-render map composition (each map layer) 00291 """ 00292 # delete tmp map layers (queries) 00293 qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER) 00294 for layer in qlayer: 00295 self.Map.DeleteLayer(layer) 00296 00297 self.SrcMapWindow.UpdateMap(render=True) 00298 if self.show_target: 00299 self.TgtMapWindow.UpdateMap(render=True) 00300 00301 # update statusbar 00302 self.StatusbarUpdate() 00303 00304 def OnPointer(self, event): 00305 """!Pointer button clicked 00306 """ 00307 # change the cursor 00308 self.SrcMapWindow.SetCursor(self.cursors["cross"]) 00309 self.SrcMapWindow.mouse['use'] = "pointer" 00310 self.SrcMapWindow.mouse['box'] = "point" 00311 self.TgtMapWindow.SetCursor(self.cursors["cross"]) 00312 self.TgtMapWindow.mouse['use'] = "pointer" 00313 self.TgtMapWindow.mouse['box'] = "point" 00314 00315 def OnZoomIn(self, event): 00316 """ 00317 Zoom in the map. 00318 Set mouse cursor, zoombox attributes, and zoom direction 00319 """ 00320 if self.GetToolbar('map'): 00321 self.toolbars['map'].OnTool(event) 00322 self.toolbars['map'].action['desc'] = '' 00323 00324 self.MapWindow.mouse['use'] = "zoom" 00325 self.MapWindow.mouse['box'] = "box" 00326 self.MapWindow.zoomtype = 1 00327 self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH) 00328 00329 # change the cursor 00330 self.MapWindow.SetCursor(self.cursors["cross"]) 00331 00332 if self.MapWindow == self.SrcMapWindow: 00333 win = self.TgtMapWindow 00334 elif self.MapWindow == self.TgtMapWindow: 00335 win = self.SrcMapWindow 00336 00337 win.mouse['use'] = "zoom" 00338 win.mouse['box'] = "box" 00339 win.zoomtype = 1 00340 win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH) 00341 00342 # change the cursor 00343 win.SetCursor(self.cursors["cross"]) 00344 00345 def OnZoomOut(self, event): 00346 """ 00347 Zoom out the map. 00348 Set mouse cursor, zoombox attributes, and zoom direction 00349 """ 00350 if self.GetToolbar('map'): 00351 self.toolbars['map'].OnTool(event) 00352 self.toolbars['map'].action['desc'] = '' 00353 00354 self.MapWindow.mouse['use'] = "zoom" 00355 self.MapWindow.mouse['box'] = "box" 00356 self.MapWindow.zoomtype = -1 00357 self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH) 00358 00359 # change the cursor 00360 self.MapWindow.SetCursor(self.cursors["cross"]) 00361 00362 if self.MapWindow == self.SrcMapWindow: 00363 win = self.TgtMapWindow 00364 elif self.MapWindow == self.TgtMapWindow: 00365 win = self.SrcMapWindow 00366 00367 win.mouse['use'] = "zoom" 00368 win.mouse['box'] = "box" 00369 win.zoomtype = -1 00370 win.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH) 00371 00372 # change the cursor 00373 win.SetCursor(self.cursors["cross"]) 00374 00375 def OnPan(self, event): 00376 """ 00377 Panning, set mouse to drag 00378 """ 00379 if self.GetToolbar('map'): 00380 self.toolbars['map'].OnTool(event) 00381 self.toolbars['map'].action['desc'] = '' 00382 00383 self.MapWindow.mouse['use'] = "pan" 00384 self.MapWindow.mouse['box'] = "pan" 00385 self.MapWindow.zoomtype = 0 00386 00387 # change the cursor 00388 self.MapWindow.SetCursor(self.cursors["hand"]) 00389 00390 if self.MapWindow == self.SrcMapWindow: 00391 win = self.TgtMapWindow 00392 elif self.MapWindow == self.TgtMapWindow: 00393 win = self.SrcMapWindow 00394 00395 win.mouse['use'] = "pan" 00396 win.mouse['box'] = "pan" 00397 win.zoomtype = 0 00398 00399 # change the cursor 00400 win.SetCursor(self.cursors["hand"]) 00401 00402 def OnErase(self, event): 00403 """ 00404 Erase the canvas 00405 """ 00406 self.MapWindow.EraseMap() 00407 00408 if self.MapWindow == self.SrcMapWindow: 00409 win = self.TgtMapWindow 00410 elif self.MapWindow == self.TgtMapWindow: 00411 win = self.SrcMapWindow 00412 00413 win.EraseMap() 00414 00415 def OnZoomRegion(self, event): 00416 """ 00417 Zoom to region 00418 """ 00419 self.Map.getRegion() 00420 self.Map.getResolution() 00421 self.UpdateMap() 00422 # event.Skip() 00423 00424 def OnAlignRegion(self, event): 00425 """ 00426 Align region 00427 """ 00428 if not self.Map.alignRegion: 00429 self.Map.alignRegion = True 00430 else: 00431 self.Map.alignRegion = False 00432 # event.Skip() 00433 00434 def SaveToFile(self, event): 00435 """!Save map to image 00436 """ 00437 img = self.MapWindow.img 00438 if not img: 00439 GMessage(parent = self, 00440 message = _("Nothing to render (empty map). Operation canceled.")) 00441 return 00442 filetype, ltype = GetImageHandlers(img) 00443 00444 # get size 00445 dlg = ImageSizeDialog(self) 00446 dlg.CentreOnParent() 00447 if dlg.ShowModal() != wx.ID_OK: 00448 dlg.Destroy() 00449 return 00450 width, height = dlg.GetValues() 00451 dlg.Destroy() 00452 00453 # get filename 00454 dlg = wx.FileDialog(parent = self, 00455 message = _("Choose a file name to save the image " 00456 "(no need to add extension)"), 00457 wildcard = filetype, 00458 style=wx.SAVE | wx.FD_OVERWRITE_PROMPT) 00459 00460 if dlg.ShowModal() == wx.ID_OK: 00461 path = dlg.GetPath() 00462 if not path: 00463 dlg.Destroy() 00464 return 00465 00466 base, ext = os.path.splitext(path) 00467 fileType = ltype[dlg.GetFilterIndex()]['type'] 00468 extType = ltype[dlg.GetFilterIndex()]['ext'] 00469 if ext != extType: 00470 path = base + '.' + extType 00471 00472 self.MapWindow.SaveToFile(path, fileType, 00473 width, height) 00474 00475 dlg.Destroy() 00476 00477 def PrintMenu(self, event): 00478 """ 00479 Print options and output menu for map display 00480 """ 00481 point = wx.GetMousePosition() 00482 printmenu = wx.Menu() 00483 # Add items to the menu 00484 setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup')) 00485 printmenu.AppendItem(setup) 00486 self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup) 00487 00488 preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview')) 00489 printmenu.AppendItem(preview) 00490 self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview) 00491 00492 doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('Print display')) 00493 printmenu.AppendItem(doprint) 00494 self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint) 00495 00496 # Popup the menu. If an item is selected then its handler 00497 # will be called before PopupMenu returns. 00498 self.PopupMenu(printmenu) 00499 printmenu.Destroy() 00500 00501 def FormatDist(self, dist): 00502 """!Format length numbers and units in a nice way, 00503 as a function of length. From code by Hamish Bowman 00504 Grass Development Team 2006""" 00505 00506 mapunits = self.Map.projinfo['units'] 00507 if mapunits == 'metres': mapunits = 'meters' 00508 outunits = mapunits 00509 dist = float(dist) 00510 divisor = 1.0 00511 00512 # figure out which units to use 00513 if mapunits == 'meters': 00514 if dist > 2500.0: 00515 outunits = 'km' 00516 divisor = 1000.0 00517 else: outunits = 'm' 00518 elif mapunits == 'feet': 00519 # nano-bug: we match any "feet", but US Survey feet is really 00520 # 5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000 00521 # miles the tick markers are rounded to the nearest 10th of a 00522 # mile (528'), the difference in foot flavours is ignored. 00523 if dist > 5280.0: 00524 outunits = 'miles' 00525 divisor = 5280.0 00526 else: 00527 outunits = 'ft' 00528 elif 'degree' in mapunits: 00529 if dist < 1: 00530 outunits = 'min' 00531 divisor = (1/60.0) 00532 else: 00533 outunits = 'deg' 00534 00535 # format numbers in a nice way 00536 if (dist/divisor) >= 2500.0: 00537 outdist = round(dist/divisor) 00538 elif (dist/divisor) >= 1000.0: 00539 outdist = round(dist/divisor,1) 00540 elif (dist/divisor) > 0.0: 00541 outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor)))) 00542 else: 00543 outdist = float(dist/divisor) 00544 00545 return (outdist, outunits) 00546 00547 def OnZoomToRaster(self, event): 00548 """! 00549 Set display extents to match selected raster map (ignore NULLs) 00550 """ 00551 self.MapWindow.ZoomToMap(ignoreNulls = True) 00552 00553 def OnZoomToSaved(self, event): 00554 """!Set display geometry to match extents in 00555 saved region file 00556 """ 00557 self.MapWindow.ZoomToSaved() 00558 00559 def OnDisplayToWind(self, event): 00560 """!Set computational region (WIND file) to match display 00561 extents 00562 """ 00563 self.MapWindow.DisplayToWind() 00564 00565 def SaveDisplayRegion(self, event): 00566 """!Save display extents to named region file. 00567 """ 00568 self.MapWindow.SaveDisplayRegion() 00569 00570 def OnZoomMenu(self, event): 00571 """!Popup Zoom menu 00572 """ 00573 point = wx.GetMousePosition() 00574 zoommenu = wx.Menu() 00575 # Add items to the menu 00576 00577 zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)')) 00578 zoommenu.AppendItem(zoomwind) 00579 self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind) 00580 00581 zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region')) 00582 zoommenu.AppendItem(zoomdefault) 00583 self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault) 00584 00585 zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region')) 00586 zoommenu.AppendItem(zoomsaved) 00587 self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved) 00588 00589 savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display')) 00590 zoommenu.AppendItem(savewind) 00591 self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind) 00592 00593 savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region')) 00594 zoommenu.AppendItem(savezoom) 00595 self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom) 00596 00597 # Popup the menu. If an item is selected then its handler 00598 # will be called before PopupMenu returns. 00599 self.PopupMenu(zoommenu) 00600 zoommenu.Destroy() 00601 00602 00603 def IsStandalone(self): 00604 """!Check if Map display is standalone""" 00605 if self._layerManager: 00606 return False 00607 00608 return True 00609 00610 def GetLayerManager(self): 00611 """!Get reference to Layer Manager 00612 00613 @return window reference 00614 @return None (if standalone) 00615 """ 00616 return self._layerManager 00617 00618 def GetSrcWindow(self): 00619 return self.SrcMapWindow 00620 00621 def GetTgtWindow(self): 00622 return self.TgtMapWindow 00623 00624 def GetShowTarget(self): 00625 return self.show_target 00626 00627 def GetMapToolbar(self): 00628 """!Returns toolbar with zooming tools""" 00629 return self.toolbars['gcpdisp']