""" @package photo2image.ip2i_mapdisplay @brief Display to manage ground control points with two toolbars, one for various display management functions, one for manipulating GCPs. Classes: - mapdisplay::MapPanel (C) 2006-2011 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Markus Metz """ import os import platform from core import globalvar import wx import wx.aui from mapdisp.toolbars import MapToolbar from gcp.toolbars import GCPDisplayToolbar, GCPManToolbar from mapdisp.gprint import PrintOptions from core.gcmd import GMessage from gui_core.dialogs import GetImageHandlers, ImageSizeDialog from gui_core.mapdisp import SingleMapPanel from gui_core.wrap import Menu from mapwin.buffered import BufferedMapWindow import mapdisp.statusbar as sb import gcp.statusbar as sbgcp # for standalone app cmdfilename = None class MapPanel(SingleMapPanel): """Main panel for map display window. Drawing takes place in child double buffered drawing window. """ def __init__( self, parent, giface, title=_("GRASS GIS Manage Location of Tick Points on a Scanned Photo"), toolbars=["gcpdisp"], Map=None, auimgr=None, name="GCPMapWindow", **kwargs, ): """Main map display window with toolbars, statusbar and DrawWindow :param giface: GRASS interface instance :param title: window title :param toolbars: array of activated toolbars, e.g. ['map', 'digit'] :param map: instance of render.Map :param auimgs: AUI manager :param kwargs: wx.Frame attribures """ SingleMapPanel.__init__( self, parent=parent, giface=giface, title=title, Map=Map, auimgr=auimgr, name=name, **kwargs, ) self._giface = giface self.mapWindowProperties.alignExtent = True # # Add toolbars # for toolb in toolbars: self.AddToolbar(toolb) self.activemap = self.toolbars["gcpdisp"].togglemap self.activemap.SetSelection(0) self.SrcMap = self.grwiz.SrcMap # instance of render.Map self.TgtMap = self.grwiz.TgtMap # instance of render.Map self._mgr.SetDockSizeConstraint(0.5, 0.5) # # Add statusbar # # items for choice statusbarItems = [ sb.SbCoordinates, sb.SbRegionExtent, sb.SbCompRegionExtent, sb.SbDisplayGeometry, sb.SbMapScale, sbgcp.SbGoToGCP, sbgcp.SbRMSError, ] # create statusbar and its manager self.statusbar = self.CreateStatusbar(statusbarItems) # # Init map display (buffered DC & set default cursor) # self.grwiz.SwitchEnv("source") self.SrcMapWindow = BufferedMapWindow( parent=self, giface=self._giface, id=wx.ID_ANY, properties=self.mapWindowProperties, Map=self.SrcMap, ) self.grwiz.SwitchEnv("target") self.TgtMapWindow = BufferedMapWindow( parent=self, giface=self._giface, id=wx.ID_ANY, properties=self.mapWindowProperties, Map=self.TgtMap, ) self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap self._setUpMapWindow(self.SrcMapWindow) self._setUpMapWindow(self.TgtMapWindow) self.SrcMapWindow.SetNamedCursor("cross") self.TgtMapWindow.SetNamedCursor("cross") # used to switch current map (combo box in toolbar) self.SrcMapWindow.mouseEntered.connect( lambda: self._setActiveMapWindow(self.SrcMapWindow) ) self.TgtMapWindow.mouseEntered.connect( lambda: self._setActiveMapWindow(self.TgtMapWindow) ) # # initialize region values # self._initMap(Map=self.SrcMap) self._initMap(Map=self.TgtMap) self.GetMapToolbar().SelectDefault() # # Bind various events # self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive) self.Bind(wx.EVT_SIZE, self.OnSize) # # Update fancy gui style # # AuiManager wants a CentrePane, workaround to get two equally sized # windows self.list = self.CreateGCPList() # set Go To GCP item as active in statusbar self.mapWindowProperties.sbItem = 5 # self.SrcMapWindow.SetSize((300, 300)) # self.TgtMapWindow.SetSize((300, 300)) self.list.SetSize((100, 150)) self._addPanes() srcwidth, srcheight = self.SrcMapWindow.GetSize() tgtwidth, tgtheight = self.TgtMapWindow.GetSize() srcwidth = (srcwidth + tgtwidth) / 2 self._mgr.GetPane("target").Hide() self._mgr.Update() self._mgr.GetPane("source").BestSize((srcwidth, srcheight)) self._mgr.GetPane("target").BestSize((srcwidth, srcheight)) if self.show_target: self._mgr.GetPane("target").Show() else: self.activemap.Enable(False) # needed by Mac OS, does not harm on Linux, breaks display on Windows if platform.system() != "Windows": self._mgr.Update() # # Init print module and classes # self.printopt = PrintOptions(self, self.MapWindow) # # Initialization of digitization tool # self.digit = None # set active map self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap # do not init zoom history here, that happens when zooming to map(s) # # Re-use dialogs # self.dialogs = {} self.dialogs["attributes"] = None self.dialogs["category"] = None self.dialogs["barscale"] = None self.dialogs["legend"] = None self.decorationDialog = None # decoration/overlays def _setUpMapWindow(self, mapWindow): # TODO: almost the same implementation as for MapPanelBase (only names differ) # enable or disable zoom history tool mapWindow.zoomHistoryAvailable.connect( lambda: self.GetMapToolbar().Enable("zoomback", enable=True) ) mapWindow.zoomHistoryUnavailable.connect( lambda: self.GetMapToolbar().Enable("zoomback", enable=False) ) mapWindow.mouseMoving.connect(self.CoordinatesChanged) def AddToolbar(self, name): """Add defined toolbar to the window Currently known toolbars are: - 'map' - basic map toolbar - 'gcpdisp' - GCP Manager, Display - 'gcpman' - GCP Manager, points management - 'nviz' - 3D view mode """ # default toolbar if name == "map": if "map" not in self.toolbars: self.toolbars["map"] = MapToolbar( self, self._toolSwitcher, self._giface ) self._mgr.AddPane( self.toolbars["map"], wx.aui.AuiPaneInfo() .Name("maptoolbar") .Caption(_("Map Toolbar")) .ToolbarPane() .Top() .LeftDockable(False) .RightDockable(False) .BottomDockable(False) .TopDockable(True) .CloseButton(False) .Layer(2) .BestSize((self.toolbars["map"].GetSize())), ) # GCP display elif name == "gcpdisp": if "gcpdisp" not in self.toolbars: self.toolbars["gcpdisp"] = GCPDisplayToolbar(self, self._toolSwitcher) self._mgr.AddPane( self.toolbars["gcpdisp"], wx.aui.AuiPaneInfo() .Name("gcpdisplaytoolbar") .Caption(_("GCP Display toolbar")) .ToolbarPane() .Top() .LeftDockable(False) .RightDockable(False) .BottomDockable(False) .TopDockable(True) .CloseButton(False) .Layer(2), ) if self.show_target is False: self.toolbars["gcpdisp"].Enable("zoommenu", enable=False) if "gcpman" not in self.toolbars: self.toolbars["gcpman"] = GCPManToolbar(self) self._mgr.AddPane( self.toolbars["gcpman"], wx.aui.AuiPaneInfo() .Name("gcpmanagertoolbar") .Caption(_("GCP Manager toolbar")) .ToolbarPane() .Top() .Row(1) .LeftDockable(False) .RightDockable(False) .BottomDockable(False) .TopDockable(True) .CloseButton(False) .Layer(2), ) self._mgr.Update() def _addPanes(self): """Add mapwindows, toolbars and statusbar to aui manager""" self._mgr.AddPane( self.list, wx.aui.AuiPaneInfo() .Name("gcplist") .Caption(_("GCP List")) .LeftDockable(False) .RightDockable(False) .PinButton() .FloatingSize((600, 200)) .CloseButton(False) .DestroyOnClose(True) .Top() .Layer(1) .MinSize((200, 100)), ) self._mgr.AddPane( self.SrcMapWindow, wx.aui.AuiPaneInfo() .Name("source") .Caption(_("Source Display")) .Dockable(False) .CloseButton(False) .DestroyOnClose(True) .Floatable(False) .Centre(), ) self._mgr.AddPane( self.TgtMapWindow, wx.aui.AuiPaneInfo() .Name("target") .Caption(_("Target Display")) .Dockable(False) .CloseButton(False) .DestroyOnClose(True) .Floatable(False) .Right() .Layer(0), ) # statusbar self.AddStatusbarPane() def OnUpdateProgress(self, event): """ Update progress bar info """ self.GetProgressBar().UpdateProgress(event.layer, event.map) event.Skip() def OnFocus(self, event): """ Change choicebook page to match display. Or set display for georectifying """ # was in if layer manager but considering the state it was executed # always, moreover, there is no layer manager dependent code # in GCP Management, set focus to current MapWindow for mouse actions self.OnPointer(event) self.MapWindow.SetFocus() event.Skip() def OnDraw(self, event): """Re-display current map composition""" self.MapWindow.UpdateMap(render=False) def OnRender(self, event): """Re-render map composition (each map layer)""" # FIXME: remove qlayer code or use RemoveQueryLayer() now in mapdisp.frame # delete tmp map layers (queries) qlayer = self.Map.GetListOfLayers(name=globalvar.QUERYLAYER) for layer in qlayer: self.Map.DeleteLayer(layer) self.SrcMapWindow.UpdateMap(render=True) if self.show_target: self.TgtMapWindow.UpdateMap(render=True) # update statusbar self.StatusbarUpdate() def OnPointer(self, event): """Pointer button clicked""" self.SrcMapWindow.SetModePointer() self.TgtMapWindow.SetModePointer() # change the default cursor self.SrcMapWindow.SetNamedCursor("cross") self.TgtMapWindow.SetNamedCursor("cross") def OnZoomIn(self, event): """Zoom in the map.""" self.SrcMapWindow.SetModeZoomIn() self.TgtMapWindow.SetModeZoomIn() def OnZoomOut(self, event): """Zoom out the map.""" self.SrcMapWindow.SetModeZoomOut() self.TgtMapWindow.SetModeZoomOut() def OnPan(self, event): """Panning, set mouse to drag""" self.SrcMapWindow.SetModePan() self.TgtMapWindow.SetModePan() def OnErase(self, event): """ Erase the canvas """ self.MapWindow.EraseMap() if self.MapWindow == self.SrcMapWindow: win = self.TgtMapWindow elif self.MapWindow == self.TgtMapWindow: win = self.SrcMapWindow win.EraseMap() def SaveToFile(self, event): """Save map to image""" img = self.MapWindow.img if not img: GMessage( parent=self, message=_("Nothing to render (empty map). Operation canceled."), ) return filetype, ltype = GetImageHandlers(img) # get size dlg = ImageSizeDialog(self) dlg.CentreOnParent() if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return width, height = dlg.GetValues() dlg.Destroy() # get filename dlg = wx.FileDialog( parent=self, message=_( "Choose a file name to save the image " "(no need to add extension)" ), wildcard=filetype, style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, ) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() if not path: dlg.Destroy() return base, ext = os.path.splitext(path) fileType = ltype[dlg.GetFilterIndex()]["type"] extType = ltype[dlg.GetFilterIndex()]["ext"] if ext != extType: path = base + "." + extType self.MapWindow.SaveToFile(path, fileType, width, height) dlg.Destroy() def PrintMenu(self, event): """ Print options and output menu for map display """ point = wx.GetMousePosition() printmenu = Menu() # Add items to the menu setup = wx.MenuItem(printmenu, wx.ID_ANY, _("Page setup")) printmenu.AppendItem(setup) self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup) preview = wx.MenuItem(printmenu, wx.ID_ANY, _("Print preview")) printmenu.AppendItem(preview) self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview) doprint = wx.MenuItem(printmenu, wx.ID_ANY, _("Print display")) printmenu.AppendItem(doprint) self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint) # Popup the menu. If an item is selected then its handler # will be called before PopupMenu returns. self.PopupMenu(printmenu) printmenu.Destroy() def OnZoomToRaster(self, event): """ Set display extents to match selected raster map (ignore NULLs) """ self.MapWindow.ZoomToMap(ignoreNulls=True) def OnZoomToSaved(self, event): """Set display geometry to match extents in saved region file """ self.MapWindow.SetRegion(zoomOnly=True) def OnDisplayToWind(self, event): """Set computational region (WIND file) to match display extents """ self.MapWindow.DisplayToWind() def SaveDisplayRegion(self, event): """Save display extents to named region file.""" self.MapWindow.SaveDisplayRegion() def OnZoomMenu(self, event): """Popup Zoom menu""" point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu zoomwind = wx.MenuItem( zoommenu, wx.ID_ANY, _("Zoom to computational region (set with g.region)") ) zoommenu.AppendItem(zoomwind) self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind) zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _("Zoom to default region")) zoommenu.AppendItem(zoomdefault) self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault) zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _("Zoom to saved region")) zoommenu.AppendItem(zoomsaved) self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved) savewind = wx.MenuItem( zoommenu, wx.ID_ANY, _("Set computational region from display") ) zoommenu.AppendItem(savewind) self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind) savezoom = wx.MenuItem( zoommenu, wx.ID_ANY, _("Save display geometry to named region") ) zoommenu.AppendItem(savezoom) self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom) # Popup the menu. If an item is selected then its handler # will be called before PopupMenu returns. self.PopupMenu(zoommenu) zoommenu.Destroy() def GetSrcWindow(self): return self.SrcMapWindow def GetTgtWindow(self): return self.TgtMapWindow def GetShowTarget(self): return self.show_target def GetMapToolbar(self): """Returns toolbar with zooming tools""" return self.toolbars["gcpdisp"] def _setActiveMapWindow(self, mapWindow): if not self.MapWindow == mapWindow: self.MapWindow = mapWindow self.Map = mapWindow.Map self.UpdateActive(mapWindow) # needed for wingrass self.SetFocus()