Просмотр исходного кода

Added g.gui.iphoto2image, part of i.ortho.photo suite

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@70552 15284696-431f-4ddb-bdfa-cd5b030d7da7
Yann Chemin 8 лет назад
Родитель
Сommit
22f86d9e46

+ 3 - 3
gui/wxpython/Makefile

@@ -1,6 +1,6 @@
 MODULE_TOPDIR = ../..
 
-SUBDIRS = docs animation datacatalog mapswipe gmodeler rlisetup psmap dbmgr vdigit iclass gcp timeline tplot
+SUBDIRS = docs animation datacatalog mapswipe gmodeler rlisetup psmap dbmgr vdigit iclass gcp timeline tplot iphoto2image
 EXTRA_CLEAN_FILES = menustrings.py build_ext.pyc xml/menudata.xml xml/module_tree_menudata.xml */*.pyc
 
 include $(MODULE_TOPDIR)/include/Make/Dir.make
@@ -12,7 +12,7 @@ SRCFILES := $(wildcard icons/*.py scripts/*.py xml/*) \
 	$(wildcard animation/*.py core/*.py datacatalog/*.py dbmgr/*.py gcp/*.py gmodeler/*.py \
 	gui_core/*.py iclass/*.py lmgr/*.py location_wizard/*.py mapwin/*.py mapdisp/*.py \
 	mapswipe/*.py modules/*.py nviz/*.py psmap/*.py rdigit/*.py rlisetup/*.py timeline/*.py vdigit/*.py \
-	vnet/*.py web_services/*.py wxplot/*.py iscatt/*.py tplot/*.py) \
+	vnet/*.py web_services/*.py wxplot/*.py iscatt/*.py tplot/*.py iphoto2image/*.py) \
 	gis_set.py gis_set_error.py wxgui.py README
 
 DSTFILES := $(patsubst %,$(DSTDIR)/%,$(SRCFILES)) \
@@ -20,7 +20,7 @@ DSTFILES := $(patsubst %,$(DSTDIR)/%,$(SRCFILES)) \
 
 PYDSTDIRS := $(patsubst %,$(DSTDIR)/%,animation core datacatalog dbmgr gcp gmodeler \
 	gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
-	mapswipe vdigit wxplot web_services rdigit rlisetup vnet timeline iscatt tplot)
+	mapswipe vdigit wxplot web_services rdigit rlisetup vnet timeline iscatt tplot iphoto2image)
 
 
 DSTDIRS := $(patsubst %,$(DSTDIR)/%,icons scripts xml)

+ 5 - 0
gui/wxpython/iphoto2image/Makefile

@@ -0,0 +1,5 @@
+MODULE_TOPDIR = ../../..
+
+include $(MODULE_TOPDIR)/include/Make/GuiScript.make
+
+default: guiscript

+ 5 - 0
gui/wxpython/iphoto2image/__init__.py

@@ -0,0 +1,5 @@
+all = [
+    'ip2i_manager',
+    'ip2i_mapdisplay',
+    'ip2i_toolbars',
+]

+ 65 - 0
gui/wxpython/iphoto2image/g.gui.iphoto2image.html

@@ -0,0 +1,65 @@
+<!-- meta page description: wxGUI GCP Manager for photo to image registration -->
+<!-- meta page index: topic_gui|GUI -->
+<h2>DESCRIPTION</h2>
+This module is based on <b>g.gui.gcp</b>, the GCP manager of GRASS GIS.
+It is part of i.ortho.photo suite.
+
+<p>
+The aim of this module is to give absolute location values to the fiducial 
+points present (in number of 4 or 8) in a <i>scanned</i> aerial photo. 
+
+<p>
+This is necessary as (manual) scanning introduces distortions, rotations and also 
+may not be limited to scan the boundary of the photo itself. It is thus necessary
+to give to each fiducial the exact coordinates in mm as given by the aerial 
+photographic instrument design, which is unique per camera.
+
+<p>
+This module requires you to have made a group with your aerial photo <b>(i.group)</b>, a camera
+description file <b>(i.ortho.target)</b> and use them to launch the module. Additional requirements
+are the order of rectification (1 if no of Fiducials is 4, 2 if no of Fiducials is 8) and 
+an extension file (if not given, defaults to \$filename_ip2i_out)
+
+<p>
+An example for Location <b>imagery60</b>:
+<br>
+g.gui.iphoto2image group=aerial@PERMANENT raster=gs13.1@PERMANENT camera=gscamera order=2 extension=try --o
+
+<p>
+<h3>Screenshot of g.gui.iphoto2image</h3>
+
+<center>
+  <br><img src="wxGUI_iphoto2image_frame.jpg" border="0" alt="iphoto2image"><br><br>
+</center>
+
+
+<h2>For a detailed operation manual please read</h2>
+<em>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.components.html">wxGUI components</a>
+</em>
+
+<p>
+See also <a href="http://grasswiki.osgeo.org/wiki/WxGUI/Video_tutorials#Georectifier">video
+tutorials</a> on GRASS Wiki.
+
+<p>
+<h2>SEE ALSO</h2>
+<em>
+<a href="i.ortho.photo.html">i.rectify</a>,
+<a href="i.group.html">i.rectify</a>,
+<a href="i.ortho.camera.html">i.rectify</a>,
+<a href="i.ortho.target.html">i.rectify</a>,
+<a href="i.rectify.html">i.rectify</a>,
+<a href="m.transform.html">m.transform</a>,
+<a href="v.rectify.html">v.rectify</a>
+</em>
+
+<h2>AUTHORS</h2>
+
+Markus Metz<br><br>
+<em>Based on the Georectifier (GRASS 6.4.0)</em> by Michael Barton<br>
+Martin Landa, Czech Technical University in Prague, Czech Republic
+
+<p>
+<i>$Date: 2016-09-19 11:37:30 +0200 (lun., 19 sept. 2016) $</i>

+ 127 - 0
gui/wxpython/iphoto2image/g.gui.iphoto2image.py

@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+
+############################################################################
+#
+# MODULE:    Correcting distortions of a scanned photo (modified from GCP Manager)
+# AUTHOR(S): Yann modified the code (was Markus Metz for the GCP Manager)
+# PURPOSE:   Takes a scanned photo and fits fiducial points to known geometry
+# COPYRIGHT: (C) 2012-2017 by Markus Metz, and the GRASS Development Team
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+############################################################################
+
+#%module
+#% description: Corrects scanning distortions of a paper photo.
+#% keyword: imagery
+#% keyword: aerial
+#% keyword: photo
+#% keyword: GUI
+#%end
+
+#%option G_OPT_I_GROUP
+#% key: group
+#% required: yes
+#%end
+
+#%option G_OPT_R_INPUT
+#% key: raster
+#% required: yes
+#%end
+
+#%option 
+#% key: camera
+#% type: string
+#% label: The name of the camera (generated in i.ortho.camera)
+#% description: The name of the camera (generated in i.ortho.camera)
+#% required: yes
+#%end
+
+#%option 
+#% key: order
+#% type: string
+#% label: The rectification order (no of Fiducial=4 -> order=1, no of Fiducial=8 -> order=2)
+#% description: The rectification order (no of Fiducial=4 -> order=1, no of Fiducial=8 -> order=2)
+#% required: yes
+#% answer: 1
+#%end
+
+#%option 
+#% key: extension
+#% type: string
+#% label: The name of the output files extension (used in i.rectify)
+#% description: The name of the output files extension (used in i.rectify)
+#% required: yes
+#% answer: _ip2i_out
+#%end
+
+"""
+Module to run GCP management tool as stadalone application.
+@author Vaclav Petras  <wenzeslaus gmail.com> (standalone module)
+"""
+import os
+import grass.script as gscript
+
+def main():
+    """Sets the GRASS display driver
+    """
+    options, flags = gscript.parser()
+
+    import wx
+    from grass.script.setup import set_gui_path
+    set_gui_path()
+
+    from core.settings import UserSettings
+    from core.globalvar import CheckWxVersion
+    from core.giface import StandaloneGrassInterface
+    from iphoto2image.ip2i_manager import GCPWizard
+
+    driver = UserSettings.Get(group='display', key='driver', subkey='type')
+    if driver == 'png':
+        os.environ['GRASS_RENDER_IMMEDIATE'] = 'png'
+    else:
+        os.environ['GRASS_RENDER_IMMEDIATE'] = 'cairo'
+
+    if options['group']:
+        group = options['group']
+    else:
+        gscript.fatal(_("Please provide a group name to process"))
+    
+    if options['raster']:
+        raster = options['raster']
+    else:
+        gscript.fatal(_("Please provide a raster map name to process"))
+
+    if options['camera']:
+        camera = options['camera']
+    else:
+        gscript.fatal(_("Please provide a camera name (generated by i.ortho.camera)"))
+
+    if options['order']:
+        order = options['order']
+    else:
+        gscript.fatal(_("Please provive an order value (1 if 4 Fiducials, 2 if 8 Fiducials)"))
+
+    if options['extension']:
+        extension = options['extension']
+    else:
+        gscript.fatal(_("Please provive an output files extension (used by i.rectify)"))
+
+    app = wx.App()
+    if not CheckWxVersion([2, 9]):
+        wx.InitAllImageHandlers()
+
+    wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface(), group=group, 
+            raster=raster, raster1=raster, camera=camera, order=order, extension=extension)
+    app.MainLoop()
+
+if __name__ == '__main__':
+    main()

Разница между файлами не показана из-за своего большого размера
+ 2397 - 0
gui/wxpython/iphoto2image/ip2i_manager.py


+ 532 - 0
gui/wxpython/iphoto2image/ip2i_mapdisplay.py

@@ -0,0 +1,532 @@
+"""
+@package iphoto2image.ip2i_mapdisplay
+
+@brief Display to manage ground control points with two toolbars, one
+for various display management functions, one for manipulating GCPs.
+
+Classes:
+- mapdisplay::MapFrame
+
+(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 math
+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 core.utils import _
+from gui_core.dialogs import GetImageHandlers, ImageSizeDialog
+from gui_core.mapdisp import SingleMapFrame
+from core.settings import UserSettings
+from mapwin.buffered import BufferedMapWindow
+from mapwin.base import MapWindowProperties
+
+import mapdisp.statusbar as sb
+import gcp.statusbar as sbgcp
+
+# for standalone app
+cmdfilename = None
+
+
+class MapFrame(SingleMapFrame):
+    """Main frame 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
+        """
+
+        SingleMapFrame.__init__(
+            self,
+            parent=parent,
+            giface=giface,
+            title=title,
+            Map=Map,
+            auimgr=auimgr,
+            name=name,
+            **kwargs)
+
+        self._giface = giface
+        # properties are shared in other objects, so defining here
+        self.mapWindowProperties = MapWindowProperties()
+        self.mapWindowProperties.setValuesFromUserSettings()
+        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
+        self.statusbarItems = [sb.SbCoordinates,
+                               sb.SbRegionExtent,
+                               sb.SbCompRegionExtent,
+                               sb.SbShowRegion,
+                               sb.SbResolution,
+                               sb.SbDisplayGeometry,
+                               sb.SbMapScale,
+                               sb.SbProjection,
+                               sbgcp.SbGoToGCP,
+                               sbgcp.SbRMSError]
+
+        # create statusbar and its manager
+        statusbar = self.CreateStatusBar(number=4, style=0)
+        statusbar.SetStatusWidths([-5, -2, -1, -1])
+        self.statusbarManager = sb.SbManager(
+            mapframe=self, statusbar=statusbar)
+
+        # fill statusbar manager
+        self.statusbarManager.AddStatusbarItemsByClass(
+            self.statusbarItems, mapframe=self, statusbar=statusbar)
+        self.statusbarManager.AddStatusbarItem(
+            sb.SbMask(self, statusbar=statusbar, position=2))
+        self.statusbarManager.AddStatusbarItem(
+            sb.SbRender(self, statusbar=statusbar, position=3))
+
+        self.statusbarManager.SetMode(8)  # goto GCP
+
+        #
+        # 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()
+
+        #self.SrcMapWindow.SetSize((300, 300))
+        #self.TgtMapWindow.SetSize((300, 300))
+        self.list.SetSize((100, 150))
+        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))
+
+        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
+
+        # doing nice things in statusbar when other things are ready
+        self.statusbarManager.Update()
+
+    def _setUpMapWindow(self, mapWindow):
+        # TODO: almost the smae implementation as for MapFrameBase (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":
+            self.toolbars['map'] = MapToolbar(self, self._toolSwitcher)
+
+            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":
+            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 == False:
+                self.toolbars['gcpdisp'].Enable('zoommenu', enable=False)
+
+            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 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 = wx.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 = wx.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 IsStandalone(self):
+        """Check if Map display is standalone"""
+        # we do not know and we do not care, so always False
+        return True
+
+    def GetLayerManager(self):
+        """Get reference to Layer Manager
+
+        :return: always None
+        """
+        return None
+
+    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()

+ 128 - 0
gui/wxpython/iphoto2image/ip2i_statusbar.py

@@ -0,0 +1,128 @@
+"""
+@package iphoto2image.ip2i_statusbar
+
+@brief Classes for statusbar management in GCP Manager
+
+Classes:
+ - statusbar::SbRMSError
+ - statusbar::SbGoToGCP
+
+(C) 2012-2013 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 Vaclav Petras <wenzeslaus gmail.com> (statusbar refactoring)
+@author Anna Kratochvilova <kratochanna gmail.com> (statusbar refactoring)
+"""
+
+
+import wx
+
+from core.gcmd import GMessage
+from core.utils import _
+from mapdisp.statusbar import SbItem, SbTextItem
+from gui_core.wrap import SpinCtrl
+
+
+class SbGoToGCP(SbItem):
+    """SpinCtrl to select GCP to focus on
+
+    Requires MapFrame.GetSrcWindow, MapFrame.GetTgtWindow,
+    MapFrame.GetListCtrl, MapFrame.GetMapCoordList.
+    """
+
+    def __init__(self, mapframe, statusbar, position=0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'gotoGCP'
+        self.label = _("Go to GCP No.")
+
+        self.widget = SpinCtrl(parent=self.statusbar, id=wx.ID_ANY,
+                               value="", min=0)
+        self.widget.Hide()
+
+        self.widget.Bind(wx.EVT_TEXT_ENTER, self.OnGoToGCP)
+        self.widget.Bind(wx.EVT_SPINCTRL, self.OnGoToGCP)
+
+    def OnGoToGCP(self, event):
+        """Zooms to given GCP."""
+        gcpNumber = self.GetValue()
+        mapCoords = self.mapFrame.GetMapCoordList()
+
+        # always false, spin checks it
+        if gcpNumber < 0 or gcpNumber > len(mapCoords):
+            GMessage(parent=self,
+                     message="%s 1 - %s." % (_("Valid Range:"),
+                                             len(mapCoords)))
+            return
+
+        if gcpNumber == 0:
+            return
+
+        listCtrl = self.mapFrame.GetListCtrl()
+
+        listCtrl.selectedkey = gcpNumber
+        listCtrl.selected = listCtrl.FindItemData(-1, gcpNumber)
+        listCtrl.render = False
+        listCtrl.SetItemState(listCtrl.selected,
+                              wx.LIST_STATE_SELECTED,
+                              wx.LIST_STATE_SELECTED)
+        listCtrl.render = True
+
+        listCtrl.EnsureVisible(listCtrl.selected)
+
+        srcWin = self.mapFrame.GetSrcWindow()
+        tgtWin = self.mapFrame.GetTgtWindow()
+
+        # Source MapWindow:
+        begin = (mapCoords[gcpNumber][1], mapCoords[gcpNumber][2])
+        begin = srcWin.Cell2Pixel(begin)
+        end = begin
+        srcWin.Zoom(begin, end, 0)
+
+        # redraw map
+        srcWin.UpdateMap()
+
+        if self.mapFrame.GetShowTarget():
+            # Target MapWindow:
+            begin = (mapCoords[gcpNumber][3], mapCoords[gcpNumber][4])
+            begin = tgtWin.Cell2Pixel(begin)
+            end = begin
+            tgtWin.Zoom(begin, end, 0)
+
+            # redraw map
+            tgtWin.UpdateMap()
+
+        self.GetWidget().SetFocus()
+
+    def Update(self):
+        """Checks the number of items in the gcp list
+        and sets the spin limits accordingly."""
+        self.statusbar.SetStatusText("")
+        maximum = self.mapFrame.GetListCtrl().GetItemCount()
+        if maximum < 1:
+            maximum = 1
+        self.widget.SetRange(0, maximum)
+        self.Show()
+
+        # disable long help
+        self.mapFrame.StatusbarEnableLongHelp(False)
+
+
+class SbRMSError(SbTextItem):
+    """Shows RMS error.
+
+    Requires MapFrame.GetFwdError, MapFrame.GetBkwError.
+    """
+
+    def __init__(self, mapframe, statusbar, position=0):
+        SbTextItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'RMSError'
+        self.label = _("RMS error")
+
+    def Show(self):
+        """Shows the RMS errors."""
+        self.SetValue(_("Forward: %(forw)s, Backward: %(back)s") %
+                      {'forw': self.mapFrame.GetFwdError(),
+                       'back': self.mapFrame.GetBkwError()})
+        SbTextItem.Show(self)

+ 163 - 0
gui/wxpython/iphoto2image/ip2i_toolbars.py

@@ -0,0 +1,163 @@
+"""
+@package iphoto2image.ip2i_toolbars
+
+@brief Georectification module - toolbars
+
+Classes:
+ - toolbars::GCPMapToolbar
+ - toolbars::GCPDisplayToolbar
+
+(C) 2007-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 sys
+
+import wx
+
+from core import globalvar
+from core.utils import _
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from icons.icon import MetaIcon
+
+
+class GCPManToolbar(BaseToolbar):
+    """Toolbar for managing ground control points
+
+    :param parent: reference to GCP widget
+    """
+
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+
+        self.InitToolbar(self._toolbarData())
+
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        icons = {
+            'gcpAdd': MetaIcon(img='gcp-add',
+                               label=_('Add new GCP to the list')),
+            'gcpDelete': MetaIcon(img='gcp-delete',
+                                  label=_('Delete selected GCP')),
+            'gcpClear': MetaIcon(img='gcp-remove',
+                                 label=_('Clear selected GCP')),
+            'gcpRms': MetaIcon(img='gcp-rms',
+                               label=_('Recalculate RMS error')),
+            'georectify': MetaIcon(img='georectify',
+                                   label=_('Georectify')),
+            'gcpSave': MetaIcon(img='gcp-save',
+                                label=_('Save GCPs to POINTS file')),
+            'gcpReload': MetaIcon(img='reload',
+                                  label=_('Reload GCPs from POINTS file')),
+        }
+
+        return self._getToolbarData((('gcpAdd', icons["gcpAdd"],
+                                      self.parent.AddGCP),
+                                     ('gcpDelete', icons["gcpDelete"],
+                                      self.parent.DeleteGCP),
+                                     ('gcpClear', icons["gcpClear"],
+                                      self.parent.ClearGCP),
+                                     (None, ),
+                                     ('rms', icons["gcpRms"],
+                                      self.parent.OnRMS),
+                                     ('georect', icons["georectify"],
+                                      self.parent.OnGeorect),
+                                     (None, ),
+                                     ('gcpSave', icons["gcpSave"],
+                                      self.parent.SaveGCPs),
+                                     ('gcpReload', icons["gcpReload"],
+                                      self.parent.ReloadGCPs))
+                                    )
+
+
+class GCPDisplayToolbar(BaseToolbar):
+    """GCP Display toolbar
+    """
+
+    def __init__(self, parent, toolSwitcher):
+        """GCP Display toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent, toolSwitcher)
+
+        self.InitToolbar(self._toolbarData())
+        self._default = self.gcpset
+
+        # add tool to toggle active map window
+        self.togglemapid = wx.NewId()
+        self.togglemap = wx.Choice(parent=self, id=self.togglemapid,
+                                   choices=[_('source'), _('target')])
+
+        self.InsertControl(10, self.togglemap)
+
+        self.SetToolShortHelp(
+            self.togglemapid, '%s %s %s' %
+            (_('Set map canvas for '),
+             BaseIcons["zoomBack"].GetLabel(),
+             _(' / Zoom to map')))
+
+        for tool in (self.gcpset, self.pan, self.zoomin, self.zoomout):
+            self.toolSwitcher.AddToolToGroup(
+                group='mouseUse', toolbar=self, tool=tool)
+
+        # realize the toolbar
+        self.Realize()
+
+        self.EnableTool(self.zoomback, False)
+
+    def _toolbarData(self):
+        """Toolbar data"""
+        icons = {
+            'gcpSet': MetaIcon(
+                img='gcp-create',
+                label=_('Update GCP coordinates'),
+                desc=_('Update GCP coordinates)')),
+            'quit': BaseIcons['quit'].SetLabel(
+                _('Quit georectification tool')),
+            'settings': BaseIcons['settings'].SetLabel(
+                _('Georectifier settings')),
+            'help': BaseIcons['help'].SetLabel(
+                _('Georectifier manual')),
+        }
+
+        return self._getToolbarData((("displaymap", BaseIcons["display"],
+                                      self.parent.OnDraw),
+                                     ("rendermap", BaseIcons["render"],
+                                      self.parent.OnRender),
+                                     ("erase", BaseIcons["erase"],
+                                      self.parent.OnErase),
+                                     (None, ),
+                                     ("gcpset", icons["gcpSet"],
+                                      self.parent.OnPointer,
+                                      wx.ITEM_CHECK),
+                                     ("pan", BaseIcons["pan"],
+                                      self.parent.OnPan,
+                                      wx.ITEM_CHECK),
+                                     ("zoomin", BaseIcons["zoomIn"],
+                                      self.parent.OnZoomIn,
+                                      wx.ITEM_CHECK),
+                                     ("zoomout", BaseIcons["zoomOut"],
+                                      self.parent.OnZoomOut,
+                                      wx.ITEM_CHECK),
+                                     ("zoommenu", BaseIcons["zoomMenu"],
+                                      self.parent.OnZoomMenuGCP),
+                                     (None, ),
+                                     ("zoomback", BaseIcons["zoomBack"],
+                                      self.parent.OnZoomBack),
+                                     ("zoomtomap", BaseIcons["zoomExtent"],
+                                      self.parent.OnZoomToMap),
+                                     (None, ),
+                                     ('settings', icons["settings"],
+                                      self.parent.OnSettings),
+                                     ('help', icons["help"],
+                                      self.parent.OnHelp),
+                                     (None, ),
+                                     ('quit', icons["quit"],
+                                      self.parent.OnQuit))
+                                    )

BIN
gui/wxpython/iphoto2image/wxGUI_iphoto2image_frame.jpg