4 @brief display to manage ground control points with two toolbars, one for
5 various display management functions, one for manipulating GCPs.
10 (C) 2006-2010 by the GRASS Development Team
11 This program is free software under the GNU General Public
12 License (>=v2). Read the file COPYING that comes with GRASS
15 Derived from mapdisp.py
35 CompatPath = os.path.join(globalvar.ETCWXDIR)
36 sys.path.append(CompatPath)
37 from compat
import subprocess
39 gmpath = os.path.join(globalvar.ETCWXDIR,
"icons")
40 sys.path.append(gmpath)
42 grassPath = os.path.join(globalvar.ETCDIR,
"python")
43 sys.path.append(grassPath)
57 from debug
import Debug
58 from icon
import Icons
59 from preferences
import globalSettings
as UserSettings
61 from mapdisp_command
import Command
62 from mapdisp_window
import BufferedWindow
68 """!Main frame for map display window. Drawing takes place in
69 child double buffered drawing window.
71 def __init__(self, parent=None, id=wx.ID_ANY, title=_(
"GRASS GIS Manage Ground Control Points"),
72 style=wx.DEFAULT_FRAME_STYLE, toolbars=[
"gcpdisp"],
73 tree=
None, notebook=
None, lmgr=
None, page=
None,
74 Map=
None, auimgr=
None, **kwargs):
75 """!Main map display window with toolbars, statusbar and
78 @param toolbars array of activated toolbars, e.g. ['map', 'digit']
79 @param tree reference to layer tree
80 @param notebook control book ID in Layer Manager
81 @param lmgr Layer Manager
82 @param page notebook page with layer tree
83 @param Map instance of render.Map
84 @param auimgs AUI manager
85 @param kwargs wx.Frame attribures
94 if 'name' not in kwargs:
95 kwargs[
'name'] =
'GCPMapWindow'
96 wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
102 "default" : wx.StockCursor(wx.CURSOR_ARROW),
103 "cross" : wx.StockCursor(wx.CURSOR_CROSS),
104 "hand" : wx.StockCursor(wx.CURSOR_HAND),
105 "pencil" : wx.StockCursor(wx.CURSOR_PENCIL),
106 "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
112 self.SetClientSize(self.GetSize())
115 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR,
'grass_map.ico'), wx.BITMAP_TYPE_ICO))
120 self.
_mgr = wx.aui.AuiManager(self)
132 for toolb
in toolbars:
136 self.activemap.SetSelection(0)
139 self._mgr.SetDockSizeConstraint(0.5, 0.5)
144 self.
statusbar = self.CreateStatusBar(number=4, style=0)
145 self.statusbar.SetStatusWidths([-5, -2, -1, -1])
148 choices = [_(
"Coordinates"),
151 _(
"Show comp. extent"),
153 _(
"Display geometry"),
165 self.
statusbarWin[
'render'].SetValue(UserSettings.Get(group=
'display',
168 self.
statusbarWin[
'render'].SetToolTip(wx.ToolTip (_(
"Enable/disable auto-rendering")))
171 label=_(
"Show computational extent"))
176 self.
statusbarWin[
'region'].SetToolTip(wx.ToolTip (_(
"Show/hide computational "
177 "region extent (set with g.region). "
178 "Display region drawn as a blue box inside the "
179 "computational region, "
180 "computational region inside a display region "
184 label=_(
"Constrain display resolution to computational settings"))
186 self.
statusbarWin[
'resolution'].SetValue(UserSettings.Get(group=
'display', key=
'compResolution', subkey=
'enabled'))
188 self.
statusbarWin[
'resolution'].SetToolTip(wx.ToolTip (_(
"Constrain display resolution "
189 "to computational region settings. "
190 "Default value for new map displays can "
191 "be set up in 'User GUI settings' dialog.")))
194 style = wx.TE_PROCESS_ENTER,
216 label=_(
"Use defined projection"))
219 self.
statusbarWin[
'projection'].SetMinSize((size[0] + 150, size[1]))
220 self.
statusbarWin[
'projection'].SetToolTip(wx.ToolTip (_(
"Reproject coordinates displayed "
221 "in the statusbar. Projection can be "
222 "defined in GUI preferences dialog "
229 self.
statusbarWin[
'mask'].SetForegroundColour(wx.Colour(255, 0, 0))
233 range=0, style=wx.GA_HORIZONTAL)
241 self.grwiz.SwitchEnv(
'source')
245 self.grwiz.SwitchEnv(
'target')
250 self.SrcMapWindow.SetCursor(self.
cursors[
"cross"])
251 self.TgtMapWindow.SetCursor(self.
cursors[
"cross"])
261 self.Bind(wx.EVT_ACTIVATE, self.
OnFocus)
263 self.Bind(wx.EVT_SIZE, self.OnDispResize)
264 self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
270 self.
list = self.CreateGCPList()
274 self.list.SetSize((100, 150))
275 self._mgr.AddPane(self.
list, wx.aui.AuiPaneInfo().
276 Name(
"gcplist").Caption(_(
"GCP List")).LeftDockable(
False).
277 RightDockable(
False).PinButton().FloatingSize((600,200)).
278 CloseButton(
False).DestroyOnClose(
True).
279 Top().Layer(1).MinSize((200,100)))
280 self._mgr.AddPane(self.
SrcMapWindow, wx.aui.AuiPaneInfo().
281 Name(
"source").Caption(_(
"Source Display")).Dockable(
False).
282 CloseButton(
False).DestroyOnClose(
True).Floatable(
False).
284 self._mgr.AddPane(self.
TgtMapWindow, wx.aui.AuiPaneInfo().
285 Name(
"target").Caption(_(
"Target Display")).Dockable(
False).
286 CloseButton(
False).DestroyOnClose(
True).Floatable(
False).
289 srcwidth, srcheight = self.SrcMapWindow.GetSize()
290 tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
291 srcwidth = (srcwidth + tgtwidth) / 2
292 self._mgr.GetPane(
"target").Hide()
294 self._mgr.GetPane(
"source").BestSize((srcwidth, srcheight))
295 self._mgr.GetPane(
"target").BestSize((srcwidth, srcheight))
297 self._mgr.GetPane(
"target").Show()
299 self.activemap.Enable(
False)
301 if platform.system() !=
'Windows':
324 self.
dialogs[
'attributes'] =
None
325 self.
dialogs[
'category'] =
None
326 self.
dialogs[
'barscale'] =
None
332 """!Add defined toolbar to the window
334 Currently known toolbars are:
335 - 'map' - basic map toolbar
336 - 'vdigit' - vector digitizer
337 - 'gcpdisp' - GCP Manager, Display
338 - 'gcpman' - GCP Manager, points management
339 - 'georect' - georectifier
340 - 'nviz' - 3D view mode
346 self._mgr.AddPane(self.
toolbars[
'map'],
347 wx.aui.AuiPaneInfo().
348 Name(
"maptoolbar").Caption(_(
"Map Toolbar")).
350 LeftDockable(
False).RightDockable(
False).
351 BottomDockable(
False).TopDockable(
True).
352 CloseButton(
False).Layer(2).
353 BestSize((self.
toolbars[
'map'].GetSize())))
356 elif name ==
"gcpdisp":
359 self._mgr.AddPane(self.
toolbars[
'gcpdisp'],
360 wx.aui.AuiPaneInfo().
361 Name(
"gcpdisplaytoolbar").Caption(_(
"GCP Display toolbar")).
363 LeftDockable(
False).RightDockable(
False).
364 BottomDockable(
False).TopDockable(
True).
365 CloseButton(
False).Layer(2))
368 self.
toolbars[
'gcpdisp'].Enable(
'zoommenu', enable =
False)
372 self._mgr.AddPane(self.
toolbars[
'gcpman'],
373 wx.aui.AuiPaneInfo().
374 Name(
"gcpmanagertoolbar").Caption(_(
"GCP Manager toolbar")).
375 ToolbarPane().Top().Row(1).
376 LeftDockable(
False).RightDockable(
False).
377 BottomDockable(
False).TopDockable(
True).
378 CloseButton(
False).Layer(2))
382 def __InitDisplay(self):
384 Initialize map display, set dimensions and map region
386 self.width, self.
height = self.GetClientSize()
388 Debug.msg(2,
"MapFrame.__InitDisplay():")
389 self.grwiz.SwitchEnv(
'source')
390 self.SrcMap.ChangeMapSize(self.GetClientSize())
391 self.SrcMap.region = self.SrcMap.GetRegion()
392 self.grwiz.SwitchEnv(
'target')
393 self.TgtMap.ChangeMapSize(self.GetClientSize())
394 self.TgtMap.region = self.TgtMap.GetRegion()
400 Update progress bar info
408 Change choicebook page to match display.
409 Or set display for georectifying
412 self._layerManager.gcpmanagement:
415 self.MapWindow.SetFocus()
420 pgnum = self.layerbook.GetPageIndex(self.
page)
422 self.layerbook.SetSelection(pgnum)
427 """!Re-display current map composition
429 self.MapWindow.UpdateMap(render =
False)
432 """!Re-render map composition (each map layer)
435 qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)
437 self.Map.DeleteLayer(layer)
439 self.SrcMapWindow.UpdateMap(render=
True)
441 self.TgtMapWindow.UpdateMap(render=
True)
447 """!Pointer button clicked
450 self.SrcMapWindow.SetCursor(self.
cursors[
"cross"])
451 self.SrcMapWindow.mouse[
'use'] =
"pointer"
452 self.SrcMapWindow.mouse[
'box'] =
"point"
453 self.TgtMapWindow.SetCursor(self.
cursors[
"cross"])
454 self.TgtMapWindow.mouse[
'use'] =
"pointer"
455 self.TgtMapWindow.mouse[
'box'] =
"point"
460 Set mouse cursor, zoombox attributes, and zoom direction
464 self.
toolbars[
'map'].action[
'desc'] =
''
466 self.MapWindow.mouse[
'use'] =
"zoom"
467 self.MapWindow.mouse[
'box'] =
"box"
468 self.MapWindow.zoomtype = 1
469 self.MapWindow.pen = wx.Pen(colour=
'Red', width=2, style=wx.SHORT_DASH)
472 self.MapWindow.SetCursor(self.
cursors[
"cross"])
479 win.mouse[
'use'] =
"zoom"
480 win.mouse[
'box'] =
"box"
482 win.pen = wx.Pen(colour=
'Red', width=2, style=wx.SHORT_DASH)
485 win.SetCursor(self.
cursors[
"cross"])
490 Set mouse cursor, zoombox attributes, and zoom direction
494 self.
toolbars[
'map'].action[
'desc'] =
''
496 self.MapWindow.mouse[
'use'] =
"zoom"
497 self.MapWindow.mouse[
'box'] =
"box"
498 self.MapWindow.zoomtype = -1
499 self.MapWindow.pen = wx.Pen(colour=
'Red', width=2, style=wx.SHORT_DASH)
502 self.MapWindow.SetCursor(self.
cursors[
"cross"])
509 win.mouse[
'use'] =
"zoom"
510 win.mouse[
'box'] =
"box"
512 win.pen = wx.Pen(colour=
'Red', width=2, style=wx.SHORT_DASH)
515 win.SetCursor(self.
cursors[
"cross"])
519 Zoom last (previously stored position)
521 self.MapWindow.ZoomBack()
525 Panning, set mouse to drag
529 self.
toolbars[
'map'].action[
'desc'] =
''
531 self.MapWindow.mouse[
'use'] =
"pan"
532 self.MapWindow.mouse[
'box'] =
"pan"
533 self.MapWindow.zoomtype = 0
536 self.MapWindow.SetCursor(self.
cursors[
"hand"])
543 win.mouse[
'use'] =
"pan"
544 win.mouse[
'box'] =
"pan"
548 win.SetCursor(self.
cursors[
"hand"])
554 self.MapWindow.EraseMap()
568 self.Map.getResolution()
576 if not self.Map.alignRegion:
577 self.Map.alignRegion =
True
579 self.Map.alignRegion =
False
584 Enable/disable auto-rendering
591 Show/Hide extent in map canvas
595 self.MapWindow.regionCoords = []
597 del self.MapWindow.regionCoords
605 Use resolution of computation region settings
606 for redering image instead of display resolution
620 Map scale changed by user
622 scale = event.GetString()
625 if scale[:2] !=
'1:':
627 value = int(scale[2:])
632 dEW = value * (self.Map.region[
'cols'] / self.
ppm[0])
633 dNS = value * (self.Map.region[
'rows'] / self.
ppm[1])
634 self.Map.region[
'n'] = self.Map.region[
'center_northing'] + dNS / 2.
635 self.Map.region[
's'] = self.Map.region[
'center_northing'] - dNS / 2.
636 self.Map.region[
'w'] = self.Map.region[
'center_easting'] - dEW / 2.
637 self.Map.region[
'e'] = self.Map.region[
'center_easting'] + dEW / 2.
640 self.MapWindow.ZoomHistory(self.Map.region[
'n'], self.Map.region[
's'],
641 self.Map.region[
'e'], self.Map.region[
'w'])
644 self.MapWindow.UpdateMap()
654 if GCPNo < 0
or GCPNo > len(self.mapcoordlist):
655 wx.MessageBox(parent=self,
656 message=
"%s 1 - %s." % (_(
"Valid Range:"),
657 len(self.mapcoordlist)),
658 caption=_(
"Invalid GCP Number"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
664 self.list.selectedkey = GCPNo
665 self.list.selected = self.list.FindItemData(-1, GCPNo)
666 self.list.render =
False
667 self.list.SetItemState(self.list.selected,
668 wx.LIST_STATE_SELECTED,
669 wx.LIST_STATE_SELECTED)
670 self.list.render =
True
673 begin = (self.mapcoordlist[GCPNo][1], self.mapcoordlist[GCPNo][2])
674 begin = self.SrcMapWindow.Cell2Pixel(begin)
676 self.SrcMapWindow.Zoom(begin, end, 0)
679 self.SrcMapWindow.UpdateMap()
683 begin = (self.mapcoordlist[GCPNo][3], self.mapcoordlist[GCPNo][4])
684 begin = self.TgtMapWindow.Cell2Pixel(begin)
686 self.TgtMapWindow.Zoom(begin, end, 0)
689 self.TgtMapWindow.UpdateMap()
694 """!Update statusbar content"""
703 self.statusbar.SetStatusText(
"", 0)
707 elif self.
statusbarWin[
'toggle'].GetSelection()
in (1, 2):
710 region = self.Map.region
712 region = self.Map.GetRegion()
714 precision = int(UserSettings.Get(group =
'projection', key =
'format',
715 subkey =
'precision'))
716 format = UserSettings.Get(group =
'projection', key =
'format',
719 if self.Map.projinfo[
'proj'] ==
'll' and format ==
'DMS':
720 w, s = utils.Deg2DMS(region[
"w"], region[
"s"],
721 string =
False, precision = precision)
722 e, n = utils.Deg2DMS(region[
"e"], region[
"n"],
723 string =
False, precision = precision)
725 self.statusbar.SetStatusText(
"%s - %s, %s - %s" %
728 ewres, nsres = utils.Deg2DMS(region[
'ewres'], region[
'nsres'],
729 string =
False, precision = precision)
730 self.statusbar.SetStatusText(
"%s - %s, %s - %s (%s, %s)" %
731 (w, e, s, n, ewres, nsres), 0)
733 w, s = region[
"w"], region[
"s"]
734 e, n = region[
"e"], region[
"n"]
736 self.statusbar.SetStatusText(
"%.*f - %.*f, %.*f - %.*f" %
737 (precision, w, precision, e,
738 precision, s, precision, n), 0)
740 ewres, nsres = region[
'ewres'], region[
'nsres']
741 self.statusbar.SetStatusText(
"%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" %
742 (precision, w, precision, e,
743 precision, s, precision, n,
744 precision, ewres, precision, nsres), 0)
749 self.statusbar.SetStatusText(
"", 0)
755 self.statusbar.SetStatusText(
"", 0)
761 self.statusbar.SetStatusText(
"rows=%d; cols=%d; nsres=%.2f; ewres=%.2f" %
762 (self.Map.region[
"rows"], self.Map.region[
"cols"],
763 self.Map.region[
"nsres"], self.Map.region[
"ewres"]), 0)
772 dpSizePx = wx.DisplaySize()
773 dpSizeMM = wx.DisplaySizeMM()
774 dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4)
776 comPpi = (dpSizePx[0] / dpSizeIn[0],
777 dpSizePx[1] / dpSizeIn[1])
780 self.
ppm = ((ppi[0] / 2.54) * 100,
781 (ppi[1] / 2.54) * 100)
783 Debug.msg(4,
"MapFrame.StatusbarUpdate(mapscale): size: px=%d,%d mm=%f,%f "
784 "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" % \
785 (dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
786 dpSizeIn[0], dpSizeIn[1],
787 sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
788 self.
ppm[0], self.
ppm[1]))
790 region = self.Map.region
792 heightCm = region[
'rows'] / self.
ppm[1] * 100
793 widthCm = region[
'cols'] / self.
ppm[0] * 100
795 Debug.msg(4,
"MapFrame.StatusbarUpdate(mapscale): width_cm=%f, height_cm=%f" %
798 xscale = (region[
'e'] - region[
'w']) / (region[
'cols'] / self.
ppm[0])
799 yscale = (region[
'n'] - region[
's']) / (region[
'rows'] / self.
ppm[1])
800 scale = (xscale + yscale) / 2.
802 Debug.msg(3,
"MapFrame.StatusbarUpdate(mapscale): xscale=%f, yscale=%f -> scale=%f" % \
803 (xscale, yscale, scale))
805 self.statusbar.SetStatusText(
"")
807 self.
statusbarWin[
'mapscale'].SetValue(
"1:%ld" % (scale + 0.5))
818 self.statusbar.SetStatusText(
"")
819 max = self.list.GetItemCount()
830 self.statusbar.SetStatusText(_(
"Forward: %(forw)s, Backward: %(back)s") %
831 {
'forw' : self.fwd_rmserror,
832 'back' : self.bkw_rmserror })
837 self.statusbar.SetStatusText(
"", 1)
840 """!Enable/disable toolbars long help"""
841 for toolbar
in self.toolbars.itervalues():
843 toolbar.EnableLongHelp(enable)
846 """!Reposition checkbox in statusbar"""
856 for idx, win
in widgets:
857 rect = self.statusbar.GetFieldRect(idx)
858 wWin, hWin = win.GetBestSize()
862 wWin = rect.width - 6
868 x, y = rect.x + 3, rect.y - 1
869 w, h = wWin, rect.height + 2
871 x, y = rect.x, rect.y - 1
872 w, h = rect.width, rect.height + 2
881 win.SetPosition((x, y))
885 """!Save map to image
887 img = self.MapWindow.img
890 message = _(
"Nothing to render (empty map). Operation canceled."))
892 filetype, ltype = gdialogs.GetImageHandlers(img)
897 if dlg.ShowModal() != wx.ID_OK:
900 width, height = dlg.GetValues()
904 dlg = wx.FileDialog(parent = self,
905 message = _(
"Choose a file name to save the image "
906 "(no need to add extension)"),
908 style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)
910 if dlg.ShowModal() == wx.ID_OK:
916 base, ext = os.path.splitext(path)
917 fileType = ltype[dlg.GetFilterIndex()][
'type']
918 extType = ltype[dlg.GetFilterIndex()][
'ext']
920 path = base +
'.' + extType
922 self.MapWindow.SaveToFile(path, fileType,
929 Print options and output menu for map display
931 point = wx.GetMousePosition()
932 printmenu = wx.Menu()
934 setup = wx.MenuItem(printmenu, wx.ID_ANY, _(
'Page setup'))
935 printmenu.AppendItem(setup)
936 self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
938 preview = wx.MenuItem(printmenu, wx.ID_ANY, _(
'Print preview'))
939 printmenu.AppendItem(preview)
940 self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
942 doprint = wx.MenuItem(printmenu, wx.ID_ANY, _(
'Print display'))
943 printmenu.AppendItem(doprint)
944 self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
948 self.PopupMenu(printmenu)
952 """!Returns current instance of render.Map()
957 """!Get map window"""
961 """!Format length numbers and units in a nice way,
962 as a function of length. From code by Hamish Bowman
963 Grass Development Team 2006"""
965 mapunits = self.Map.projinfo[
'units']
966 if mapunits ==
'metres': mapunits =
'meters'
972 if mapunits ==
'meters':
977 elif mapunits ==
'feet':
987 elif 'degree' in mapunits:
995 if (dist/divisor) >= 2500.0:
996 outdist =
round(dist/divisor)
997 elif (dist/divisor) >= 1000.0:
998 outdist =
round(dist/divisor,1)
999 elif (dist/divisor) > 0.0:
1000 outdist =
round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
1002 outdist = float(dist/divisor)
1004 return (outdist, outunits)
1008 Set display extents to match selected raster (including NULLs)
1011 self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
1015 Set display extents to match selected raster map (ignore NULLs)
1017 self.MapWindow.ZoomToMap(ignoreNulls =
True)
1020 """!Set display geometry to match computational region
1021 settings (set with g.region)
1023 self.MapWindow.ZoomToWind()
1026 """!Set display geometry to match default region settings
1028 self.MapWindow.ZoomToDefault()
1031 """!Set display geometry to match extents in
1034 self.MapWindow.ZoomToSaved()
1037 """!Set computational region (WIND file) to match display
1040 self.MapWindow.DisplayToWind()
1043 """!Save display extents to named region file.
1045 self.MapWindow.SaveDisplayRegion()
1050 point = wx.GetMousePosition()
1051 zoommenu = wx.Menu()
1054 zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
'Zoom to computational region (set with g.region)'))
1055 zoommenu.AppendItem(zoomwind)
1058 zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _(
'Zoom to default region'))
1059 zoommenu.AppendItem(zoomdefault)
1062 zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _(
'Zoom to saved region'))
1063 zoommenu.AppendItem(zoomsaved)
1066 savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _(
'Set computational region from display'))
1067 zoommenu.AppendItem(savewind)
1070 savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _(
'Save display geometry to named region'))
1071 zoommenu.AppendItem(savezoom)
1076 self.PopupMenu(zoommenu)
1079 def SetProperties(self, render=False, mode=0, showCompExtent=False,
1080 constrainRes=
False, projection=
False):
1081 """!Set properies of map display window"""
1088 self.MapWindow.regionCoords = []
1091 """!Check if Map display is standalone"""
1098 """!Get reference to Layer Manager
1100 @return window reference
1101 @return None (if standalone)