""" @package gcp.manager @brief Georectification module for GRASS GIS. Includes ground control point management and interactive point and click GCP creation Classes: - manager::GCPWizard - manager::LocationPage - manager::GroupPage - manager::DispMapPage - manager::GCPPanel - manager::GCPDisplay - manager::GCPList - manager::VectGroup - manager::EditGCP - manager::GrSettingsDialog (C) 2006-2017 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 Original author Michael Barton @author Original version improved by Martin Landa @author Rewritten by Markus Metz redesign georectfier -> GCP Manage @author Support for GraphicsSet added by Stepan Turek (2012) @author port i.image.2target (v6) to version 7 in 2017 by Yann """ # TODO: i.ortho.transform has 6 appearances, check each of them and configure # TODO: i.ortho.transform looks for REF_POINTS/CONTROL_POINTS and not POINTS # TODO: CHECK CONTROL_POINTS format and create it for i.ortho.transform to use. from __future__ import print_function import os import sys import six import shutil from copy import copy import wx from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin import wx.lib.colourselect as csel from core import globalvar if globalvar.wxPythonPhoenix: from wx import adv as wiz else: from wx import wizard as wiz import grass.script as grass from core import utils from core.render import Map from gui_core.gselect import Select, LocationSelect, MapsetSelect from gui_core.dialogs import GroupDialog from gui_core.mapdisp import FrameMixin from core.gcmd import RunCommand, GMessage, GError, GWarning from core.settings import UserSettings from gcp.mapdisplay import MapPanel from core.giface import Notification from gui_core.wrap import ( SpinCtrl, Button, StaticText, StaticBox, CheckListBox, TextCtrl, Menu, ListCtrl, BitmapFromImage, CheckListCtrlMixin, ) from location_wizard.wizard import GridBagSizerTitledPage as TitledPage # # global variables # global src_map global tgt_map global maptype src_map = "" tgt_map = {"raster": "", "vector": ""} maptype = "raster" def getSmallUpArrowImage(): stream = open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") try: img = wx.Image(stream) finally: stream.close() return img def getSmallDnArrowImage(): stream = open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") try: img = wx.Image(stream) finally: stream.close() stream.close() return img class GCPWizard(object): """ Start wizard here and finish wizard here """ # def __init__(self, parent, giface, srcloc, srcmpt, srcgrp, srcras, tgtras, camera, order, extension): # global maptype # global src_map # global tgt_map # maptype = 'raster' # rendertype = 'raster' # self.parent = parent # GMFrame # self._giface = giface # self.srcloc = srcloc # self.srcmpt = srcmpt # self.group = srcgrp # self.src_map = srcras # self.tgt_map = tgtras # self.camera = camera # self.order = int(order) # self.extension = extension # self.src_maps = self.src_map # # location for xy map to georectify # self.newlocation = self.srcloc # # mapset for xy map to georectify # self.newmapset = self.srcmpt def __init__(self, parent, giface): self.parent = parent # GMFrame self._giface = giface # # get environmental variables # self.grassdatabase = grass.gisenv()["GISDBASE"] # # read original environment settings # self.target_gisrc = os.environ["GISRC"] self.gisrc_dict = {} try: f = open(self.target_gisrc, "r") for line in f.readlines(): line = line.replace("\n", "").strip() if len(line) < 1: continue key, value = line.split(":", 1) self.gisrc_dict[key.strip()] = value.strip() finally: f.close() self.currentlocation = self.gisrc_dict["LOCATION_NAME"] self.currentmapset = self.gisrc_dict["MAPSET"] # location for xy map to georectify self.newlocation = "" # mapset for xy map to georectify self.newmapset = "" global maptype global src_map global tgt_map # src_map = '' # tgt_map = '' maptype = "raster" # GISRC file for source location/mapset of map(s) to georectify self.source_gisrc = "" self.src_maps = [] # Raster map with camera angle relative to ground surface (i.ortho.rectify) self.cam_angle = "" # # define wizard pages # self.wizard = wiz.Wizard( parent=parent, id=wx.ID_ANY, title=_("Setup for georectification") ) self.startpage = LocationPage(self.wizard, self) self.grouppage = GroupPage(self.wizard, self) self.mappage = DispMapPage(self.wizard, self) # # set the initial order of the pages # self.startpage.SetNext(self.grouppage) self.grouppage.SetPrev(self.startpage) self.grouppage.SetNext(self.mappage) self.mappage.SetPrev(self.grouppage) # # do pages layout # self.startpage.DoLayout() self.grouppage.DoLayout() self.mappage.DoLayout() self.wizard.FitToPage(self.startpage) # self.Bind(wx.EVT_CLOSE, self.Cleanup) # self.parent.Bind(wx.EVT_ACTIVATE, self.OnGLMFocus) success = False # # run wizard # if self.wizard.RunWizard(self.startpage): success = self.OnWizFinished() if not success: GMessage(parent=self.parent, message=_("Georectifying setup canceled.")) self.Cleanup() else: GMessage(parent=self.parent, message=_("Georectifying setup canceled.")) self.Cleanup() # # start GCP display # if success: # instance of render.Map to be associated with display self.SwitchEnv("source") self.SrcMap = Map(gisrc=self.source_gisrc) self.SwitchEnv("target") self.TgtMap = Map(gisrc=self.target_gisrc) self.Map = self.SrcMap # # add layer to source map # if maptype == "raster": rendertype = "raster" cmdlist = ["d.rast", "map=%s" % src_map] else: # -> vector layer rendertype = "vector" cmdlist = ["d.vect", "map=%s" % src_map] self.SwitchEnv("source") name, found = utils.GetLayerNameFromCmd(cmdlist) self.SrcMap.AddLayer( ltype=rendertype, command=cmdlist, active=True, name=name, hidden=False, opacity=1.0, render=False, ) self.SwitchEnv("target") if tgt_map["raster"]: # # add raster layer to target map # rendertype = "raster" cmdlist = ["d.rast", "map=%s" % tgt_map["raster"]] name, found = utils.GetLayerNameFromCmd(cmdlist) self.TgtMap.AddLayer( ltype=rendertype, command=cmdlist, active=True, name=name, hidden=False, opacity=1.0, render=False, ) if tgt_map["vector"]: # # add raster layer to target map # rendertype = "vector" cmdlist = ["d.vect", "map=%s" % tgt_map["vector"]] name, found = utils.GetLayerNameFromCmd(cmdlist) self.TgtMap.AddLayer( ltype=rendertype, command=cmdlist, active=True, name=name, hidden=False, opacity=1.0, render=False, ) # # start GCP Manager # # create superior Map Display frame mapframe = wx.Frame( parent=None, id=wx.ID_ANY, size=globalvar.MAP_WINDOW_SIZE, style=wx.DEFAULT_FRAME_STYLE, title=name, ) gcpmgr = GCPDisplay( parent=mapframe, giface=self._giface, grwiz=self, id=wx.ID_ANY, Map=self.SrcMap, lmgr=self.parent, title=name, ) # load GCPs gcpmgr.InitMapDisplay() gcpmgr.CenterOnScreen() gcpmgr.Show() # need to update AUI here for wingrass gcpmgr._mgr.Update() else: self.Cleanup() def SetSrcEnv(self, location, mapset): """Create environment to use for location and mapset that are the source of the file(s) to georectify :param location: source location :param mapset: source mapset :return: False on error :return: True on success """ self.newlocation = location self.newmapset = mapset # check to see if we are georectifying map in current working # location/mapset if ( self.newlocation == self.currentlocation and self.newmapset == self.currentmapset ): return False self.gisrc_dict["LOCATION_NAME"] = location self.gisrc_dict["MAPSET"] = mapset self.source_gisrc = utils.GetTempfile() try: f = open(self.source_gisrc, mode="w") for line in self.gisrc_dict.items(): f.write(line[0] + ": " + line[1] + "\n") finally: f.close() return True def SwitchEnv(self, grc): """ Switches between original working location/mapset and location/mapset that is source of file(s) to georectify """ # check to see if we are georectifying map in current working # location/mapset if ( self.newlocation == self.currentlocation and self.newmapset == self.currentmapset ): return False if grc == "target": os.environ["GISRC"] = str(self.target_gisrc) elif grc == "source": os.environ["GISRC"] = str(self.source_gisrc) return True def OnWizFinished(self): # self.Cleanup() return True def OnGLMFocus(self, event): """Layer Manager focus""" # self.SwitchEnv('target') event.Skip() def Cleanup(self): """Return to current location and mapset""" # here was also the cleaning of gcpmanagement from layer manager # which is no longer needed self.SwitchEnv("target") self.wizard.Destroy() class LocationPage(TitledPage): """ Set map type (raster or vector) to georectify and select location/mapset of map(s) to georectify. """ def __init__(self, wizard, parent): TitledPage.__init__(self, wizard, _("Select map type and location/mapset")) self.parent = parent self.grassdatabase = self.parent.grassdatabase self.xylocation = "" self.xymapset = "" # # layout # # map type self.rb_maptype = wx.RadioBox( parent=self, id=wx.ID_ANY, label=" %s " % _("Map type to georectify"), choices=[_("raster"), _("vector")], majorDimension=wx.RA_SPECIFY_COLS, ) self.sizer.Add( self.rb_maptype, flag=wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, border=5, pos=(1, 1), span=(1, 2), ) # location self.sizer.Add( StaticText(parent=self, id=wx.ID_ANY, label=_("Select source location:")), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(2, 1), ) self.cb_location = LocationSelect(parent=self, gisdbase=self.grassdatabase) self.sizer.Add( self.cb_location, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(2, 2), ) # mapset self.sizer.Add( StaticText(parent=self, id=wx.ID_ANY, label=_("Select source mapset:")), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(3, 1), ) self.cb_mapset = MapsetSelect( parent=self, gisdbase=self.grassdatabase, setItems=False ) self.sizer.Add( self.cb_mapset, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(3, 2), ) self.sizer.AddGrowableCol(2) # # bindings # self.Bind(wx.EVT_RADIOBOX, self.OnMaptype, self.rb_maptype) self.Bind(wx.EVT_COMBOBOX, self.OnLocation, self.cb_location) self.cb_mapset.Bind(wx.EVT_TEXT, self.OnMapset) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) # self.Bind(wx.EVT_CLOSE, self.parent.Cleanup) def OnMaptype(self, event): """Change map type""" global maptype if event.GetInt() == 0: maptype = "raster" else: maptype = "vector" def OnLocation(self, event): """Sets source location for map(s) to georectify""" self.xylocation = event.GetString() # create a list of valid mapsets tmplist = os.listdir(os.path.join(self.grassdatabase, self.xylocation)) self.mapsetList = [] for item in tmplist: if os.path.isdir( os.path.join(self.grassdatabase, self.xylocation, item) ) and os.path.exists( os.path.join(self.grassdatabase, self.xylocation, item, "WIND") ): if item != "PERMANENT": self.mapsetList.append(item) self.xymapset = "PERMANENT" utils.ListSortLower(self.mapsetList) self.mapsetList.insert(0, "PERMANENT") self.cb_mapset.SetItems(self.mapsetList) self.cb_mapset.SetStringSelection(self.xymapset) if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled(): wx.FindWindowById(wx.ID_FORWARD).Enable(True) def OnMapset(self, event): """Sets source mapset for map(s) to georectify""" if self.xylocation == "": GMessage( _("You must select a valid location " "before selecting a mapset"), parent=self, ) return self.xymapset = event.GetString() if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled(): wx.FindWindowById(wx.ID_FORWARD).Enable(True) def OnPageChanging(self, event=None): if event.GetDirection() and (self.xylocation == "" or self.xymapset == ""): GMessage( _( "You must select a valid location " "and mapset in order to continue" ), parent=self, ) event.Veto() return self.parent.SetSrcEnv(self.xylocation, self.xymapset) def OnEnterPage(self, event=None): if self.xylocation == "" or self.xymapset == "": wx.FindWindowById(wx.ID_FORWARD).Enable(False) else: wx.FindWindowById(wx.ID_FORWARD).Enable(True) class GroupPage(TitledPage): """ Set group to georectify. Create group if desired. """ def __init__(self, wizard, parent): TitledPage.__init__(self, wizard, _("Select image/map group to georectify")) self.parent = parent self.grassdatabase = self.parent.grassdatabase self.groupList = [] self.xylocation = "" self.xymapset = "" self.xygroup = "" # default extension self.extension = "_georect" + str(os.getpid()) # # layout # # group self.sizer.Add( StaticText(parent=self, id=wx.ID_ANY, label=_("Select group:")), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(1, 1), ) self.cb_group = wx.ComboBox( parent=self, id=wx.ID_ANY, choices=self.groupList, size=(350, -1), style=wx.CB_DROPDOWN | wx.CB_READONLY, ) self.sizer.Add( self.cb_group, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(1, 2), ) # create group self.sizer.Add( StaticText( parent=self, id=wx.ID_ANY, label=_("Create group if none exists") ), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(2, 1), ) btnSizer = wx.BoxSizer(wx.HORIZONTAL) self.btn_mkgroup = Button( parent=self, id=wx.ID_ANY, label=_("Create/edit group...") ) self.btn_vgroup = Button( parent=self, id=wx.ID_ANY, label=_("Add vector map to group...") ) btnSizer.Add(self.btn_mkgroup, flag=wx.RIGHT, border=5) btnSizer.Add(self.btn_vgroup, flag=wx.LEFT, border=5) self.sizer.Add( btnSizer, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(2, 2), ) # extension self.sizer.Add( StaticText( parent=self, id=wx.ID_ANY, label=_("Extension for output maps:") ), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(3, 1), ) self.ext_txt = TextCtrl(parent=self, id=wx.ID_ANY, value="", size=(350, -1)) self.ext_txt.SetValue(self.extension) self.sizer.Add( self.ext_txt, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(3, 2), ) self.sizer.AddGrowableCol(2) # # bindings # self.Bind(wx.EVT_COMBOBOX, self.OnGroup, self.cb_group) self.Bind(wx.EVT_TEXT, self.OnExtension, self.ext_txt) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) self.Bind(wx.EVT_CLOSE, self.parent.Cleanup) # hide vector group button by default self.btn_vgroup.Hide() def OnGroup(self, event): self.xygroup = event.GetString() def OnMkGroup(self, event): """Create new group in source location/mapset""" dlg = GroupDialog(parent=self, defaultGroup=self.xygroup) dlg.DisableSubgroupEdit() dlg.ShowModal() gr, s = dlg.GetSelectedGroup() if gr in dlg.GetExistGroups(): self.xygroup = gr else: gr = "" dlg.Destroy() self.OnEnterPage() self.Update() def OnVGroup(self, event): """Add vector maps to group""" dlg = VectGroup( parent=self, id=wx.ID_ANY, grassdb=self.grassdatabase, location=self.xylocation, mapset=self.xymapset, group=self.xygroup, ) if dlg.ShowModal() != wx.ID_OK: return dlg.MakeVGroup() self.OnEnterPage() def OnExtension(self, event): self.extension = self.ext_txt.GetValue() def OnPageChanging(self, event=None): if event.GetDirection() and self.xygroup == "": GMessage( _("You must select a valid image/map " "group in order to continue"), parent=self, ) event.Veto() return if event.GetDirection() and self.extension == "": GMessage( _("You must enter an map name " "extension in order to continue"), parent=self, ) event.Veto() return def OnEnterPage(self, event=None): global maptype self.groupList = [] self.xylocation = self.parent.gisrc_dict["LOCATION_NAME"] self.xymapset = self.parent.gisrc_dict["MAPSET"] # create a list of groups in selected mapset if os.path.isdir( os.path.join(self.grassdatabase, self.xylocation, self.xymapset, "group") ): tmplist = os.listdir( os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group" ) ) for item in tmplist: if os.path.isdir( os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", item, ) ): self.groupList.append(item) if maptype == "raster": self.btn_vgroup.Hide() self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup) elif maptype == "vector": self.btn_vgroup.Show() self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup) self.Bind(wx.EVT_BUTTON, self.OnVGroup, self.btn_vgroup) utils.ListSortLower(self.groupList) self.cb_group.SetItems(self.groupList) if len(self.groupList) > 0: if self.xygroup and self.xygroup in self.groupList: self.cb_group.SetStringSelection(self.xygroup) else: self.cb_group.SetSelection(0) self.xygroup = self.groupList[0] if self.xygroup == "" or self.extension == "": wx.FindWindowById(wx.ID_FORWARD).Enable(False) else: wx.FindWindowById(wx.ID_FORWARD).Enable(True) # switch to source self.parent.SwitchEnv("source") class DispMapPage(TitledPage): """ Select ungeoreferenced map to display for interactively setting ground control points (GCPs). """ def __init__(self, wizard, parent): TitledPage.__init__( self, wizard, _("Select maps to display for ground control point (GCP) creation"), ) self.parent = parent global maptype # # layout # self.sizer.Add( StaticText( parent=self, id=wx.ID_ANY, label=_("Select source map to display:") ), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(1, 1), ) self.srcselection = Select( self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type=maptype, updateOnPopup=False, ) self.sizer.Add( self.srcselection, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(1, 2), ) self.sizer.Add( StaticText( parent=self, id=wx.ID_ANY, label=_("Select target raster map to display:"), ), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(2, 1), ) self.tgtrastselection = Select( self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type="raster", updateOnPopup=False, ) self.sizer.Add( self.tgtrastselection, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(2, 2), ) self.sizer.Add( StaticText( parent=self, id=wx.ID_ANY, label=_("Select target vector map to display:"), ), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(3, 1), ) self.tgtvectselection = Select( self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type="vector", updateOnPopup=False, ) self.sizer.Add( self.tgtvectselection, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, pos=(3, 2), ) # # bindings # self.srcselection.Bind(wx.EVT_TEXT, self.OnSrcSelection) self.tgtrastselection.Bind(wx.EVT_TEXT, self.OnTgtRastSelection) self.tgtvectselection.Bind(wx.EVT_TEXT, self.OnTgtVectSelection) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) self.Bind(wx.EVT_CLOSE, self.parent.Cleanup) def OnSrcSelection(self, event): """Source map to display selected""" global src_map global maptype src_map = self.srcselection.GetValue() if src_map == "": wx.FindWindowById(wx.ID_FORWARD).Enable(False) else: wx.FindWindowById(wx.ID_FORWARD).Enable(True) try: # set computational region to match selected map and zoom display # to region if maptype == "raster": p = RunCommand("g.region", "raster=src_map") elif maptype == "vector": p = RunCommand("g.region", "vector=src_map") if p.returncode == 0: print("returncode = ", str(p.returncode)) self.parent.Map.region = self.parent.Map.GetRegion() except: pass def OnTgtRastSelection(self, event): """Source map to display selected""" global tgt_map tgt_map["raster"] = self.tgtrastselection.GetValue() def OnTgtVectSelection(self, event): """Source map to display selected""" global tgt_map tgt_map["vector"] = self.tgtvectselection.GetValue() def OnPageChanging(self, event=None): global src_map global tgt_map if event.GetDirection() and (src_map == ""): GMessage( _("You must select a source map " "in order to continue"), parent=self ) event.Veto() return self.parent.SwitchEnv("target") def OnEnterPage(self, event=None): global maptype global src_map global tgt_map self.srcselection.SetElementList(maptype) if maptype == "raster": ret = RunCommand( "i.group", parent=self, read=True, group=self.parent.grouppage.xygroup, flags="g", ) if ret: self.parent.src_maps = ret.splitlines() else: GError( parent=self, message=_( "No maps in selected group <%s>.\n" "Please edit group or select another group." ) % self.parent.grouppage.xygroup, ) return elif maptype == "vector": grassdatabase = self.parent.grassdatabase xylocation = self.parent.gisrc_dict["LOCATION_NAME"] xymapset = self.parent.gisrc_dict["MAPSET"] # make list of vectors to georectify from VREF vgrpfile = os.path.join( grassdatabase, xylocation, xymapset, "group", self.parent.grouppage.xygroup, "VREF", ) f = open(vgrpfile) try: for vect in f.readlines(): vect = vect.strip("\n") if len(vect) < 1: continue self.parent.src_maps.append(vect) finally: f.close() if len(self.parent.src_maps) < 1: GError( parent=self, message=_( "No maps in selected group <%s>.\n" "Please edit group or select another group." ) % self.parent.grouppage.xygroup, ) return # filter out all maps not in group self.srcselection.tcp.GetElementList(elements=self.parent.src_maps) src_map = self.parent.src_maps[0] self.srcselection.SetValue(src_map) self.parent.SwitchEnv("target") self.tgtrastselection.SetElementList("raster") self.tgtrastselection.GetElementList() self.tgtvectselection.SetElementList("vector") self.tgtvectselection.GetElementList() self.parent.SwitchEnv("source") if src_map == "": wx.FindWindowById(wx.ID_FORWARD).Enable(False) else: wx.FindWindowById(wx.ID_FORWARD).Enable(True) class GCPPanel(MapPanel, ColumnSorterMixin): """ Manages ground control points for georectifying. Calculates RMS statistics. Calls i.ortho.rectify or v.rectify to georectify map. """ def __init__( self, parent, giface, grwiz=None, id=wx.ID_ANY, title=_("Manage Ground Control Points"), size=(700, 300), toolbars=["gcpdisp"], Map=None, lmgr=None, ): self.grwiz = grwiz # GR Wizard self._giface = giface if tgt_map["raster"] == "" and tgt_map["vector"] == "": self.show_target = False else: self.show_target = True # wx.Frame.__init__(self, parent, id, title, size = size, name = "GCPFrame") MapPanel.__init__( self, parent=parent, giface=self._giface, title=title, size=size, Map=Map, toolbars=toolbars, name="GCPMapWindow", ) # init variables self.parent = parent # # register data structures for drawing GCP's # self.pointsToDrawTgt = self.TgtMapWindow.RegisterGraphicsToDraw( graphicsType="point", setStatusFunc=self.SetGCPSatus ) self.pointsToDrawSrc = self.SrcMapWindow.RegisterGraphicsToDraw( graphicsType="point", setStatusFunc=self.SetGCPSatus ) # connect to the map windows signals # used to add or edit GCP self.SrcMapWindow.mouseLeftUpPointer.connect( lambda x, y: self._onMouseLeftUpPointer(self.SrcMapWindow, x, y) ) self.TgtMapWindow.mouseLeftUpPointer.connect( lambda x, y: self._onMouseLeftUpPointer(self.TgtMapWindow, x, y) ) # window resized self.resize = False self.grassdatabase = self.grwiz.grassdatabase self.currentlocation = self.grwiz.currentlocation self.currentmapset = self.grwiz.currentmapset self.newlocation = self.grwiz.newlocation self.newmapset = self.grwiz.newmapset self.xylocation = self.grwiz.gisrc_dict["LOCATION_NAME"] self.xymapset = self.grwiz.gisrc_dict["MAPSET"] self.xygroup = self.grwiz.grouppage.xygroup self.src_maps = self.grwiz.src_maps self.extension = self.grwiz.grouppage.extension self.outname = "" self.VectGRList = [] self.file = { "control_points": os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", self.xygroup, "CONTROL_POINTS", ), "control_points_bak": os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", self.xygroup, "CONTROL_POINTS_BAK", ), "rgrp": os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", self.xygroup, "REF", ), "vgrp": os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", self.xygroup, "VREF", ), "target": os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", self.xygroup, "TARGET", ), "elevation": os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", self.xygroup, "ELEVATION", ), } # make a backup of the current points file if os.path.exists(self.file["control_points"]): shutil.copy(self.file["control_points"], self.file["control_points_bak"]) # polynomial order transformation for georectification self.gr_order = 1 # interpolation method for georectification self.gr_method = "nearest" # region clipping for georectified map self.clip_to_region = False # number of GCPs selected to be used for georectification (checked) self.GCPcount = 0 # forward RMS error self.fwd_rmserror = 0.0 # backward RMS error self.bkw_rmserror = 0.0 # list map coords and ID of map display they came from self.mapcoordlist = [] self.mapcoordlist.append( [ 0, # GCP number 0.0, # source east 0.0, # source north 0.0, # source height 0.0, # target east 0.0, # target north 0.0, # target height 0.0, # forward error 0.0, ] ) # backward error # init vars to highlight high RMS errors self.highest_only = True self.show_unused = True self.highest_key = -1 self.rmsthresh = 0 self.rmsmean = 0 self.rmssd = 0 self.SetTarget(self.xygroup, self.currentlocation, self.currentmapset) self.itemDataMap = None # images for column sorting # CheckListCtrlMixin must set an ImageList first self.il = self.list.GetImageList(wx.IMAGE_LIST_SMALL) SmallUpArrow = BitmapFromImage(getSmallUpArrowImage()) SmallDnArrow = BitmapFromImage(getSmallDnArrowImage()) self.sm_dn = self.il.Add(SmallDnArrow) self.sm_up = self.il.Add(SmallUpArrow) # set mouse characteristics self.mapwin = self.SrcMapWindow self.mapwin.mouse["box"] = "point" self.mapwin.mouse["use"] == "pointer" self.mapwin.zoomtype = 0 self.mapwin.pen = wx.Pen(colour="black", width=2, style=wx.SOLID) self.mapwin.SetNamedCursor("cross") self.mapwin = self.TgtMapWindow # set mouse characteristics self.mapwin.mouse["box"] = "point" self.mapwin.mouse["use"] == "pointer" self.mapwin.zoomtype = 0 self.mapwin.pen = wx.Pen(colour="black", width=2, style=wx.SOLID) self.mapwin.SetNamedCursor("cross") # # show new display & draw map # if self.show_target: self.MapWindow = self.TgtMapWindow self.Map = self.TgtMap self.OnZoomToMap(None) self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap self.OnZoomToMap(None) # # bindings # self.Bind(wx.EVT_ACTIVATE, self.OnFocus) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_IDLE, self.OnIdle) self.SetSettings() def __del__(self): """Disable GCP manager mode""" # leaving the method here but was used only to delete gcpmanagement # from layer manager which is now not needed pass def CreateGCPList(self): """Create GCP List Control""" return GCPList(parent=self, gcp=self) # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py def GetListCtrl(self): return self.list def GetMapCoordList(self): return self.mapcoordlist # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py def GetSortImages(self): return (self.sm_dn, self.sm_up) def GetFwdError(self): return self.fwd_rmserror def GetBkwError(self): return self.bkw_rmserror def InitMapDisplay(self): self.list.LoadData() # initialize column sorter self.itemDataMap = self.mapcoordlist ncols = self.list.GetColumnCount() ColumnSorterMixin.__init__(self, ncols) # init to ascending sort on first click self._colSortFlag = [1] * ncols def SetTarget(self, tgroup, tlocation, tmapset): """ Sets rectification target to current location and mapset """ # check to see if we are georectifying map in current working # location/mapset if ( self.newlocation == self.currentlocation and self.newmapset == self.currentmapset ): RunCommand("i.target", parent=self, flags="c", group=tgroup) else: self.grwiz.SwitchEnv("source") RunCommand( "i.target", parent=self, group=tgroup, location=tlocation, mapset=tmapset, ) self.grwiz.SwitchEnv("target") def AddGCP(self, event): """ Appends an item to GCP list """ keyval = self.list.AddGCPItem() + 1 # source east, source north, target east, target north, forward error, # backward error self.mapcoordlist.append( [ keyval, # GCP number 0.0, # source east 0.0, # source north 0.0, # source height 0.0, # target east 0.0, # target north 0.0, # target height 0.0, # forward error 0.0, ] ) # backward error if self.statusbarManager.GetMode() == 8: # go to self.StatusbarUpdate() def DeleteGCP(self, event): """ Deletes selected item in GCP list """ minNumOfItems = self.OnGROrder(None) if self.list.GetItemCount() <= minNumOfItems: GMessage( parent=self, message=_("At least %d GCPs required. Operation canceled.") % minNumOfItems, ) return key = self.list.DeleteGCPItem() del self.mapcoordlist[key] # update key and GCP number for newkey in range(key, len(self.mapcoordlist)): index = self.list.FindItem(-1, newkey + 1) self.mapcoordlist[newkey][0] = newkey self.list.SetItem(index, 0, str(newkey)) self.list.SetItemData(index, newkey) # update selected if self.list.GetItemCount() > 0: if self.list.selected < self.list.GetItemCount(): self.list.selectedkey = self.list.GetItemData(self.list.selected) else: self.list.selected = self.list.GetItemCount() - 1 self.list.selectedkey = self.list.GetItemData(self.list.selected) self.list.SetItemState( self.list.selected, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED ) else: self.list.selected = wx.NOT_FOUND self.list.selectedkey = -1 self.UpdateColours() if self.statusbarManager.GetMode() == 8: # go to self.StatusbarUpdate() if self.list.selectedkey > 0: self.statusbarManager.SetProperty("gotoGCP", self.list.selectedkey) def ClearGCP(self, event): """ Clears all values in selected item of GCP list and unchecks it """ index = self.list.GetSelected() key = self.list.GetItemData(index) for i in range(1, 7): self.list.SetItem(index, i, "0.0") self.list.SetItem(index, 7, "") self.list.SetItem(index, 8, "") self.list.CheckItem(index, False) # GCP number, source E, source N, target E, target N, fwd error, bkwd # error self.mapcoordlist[key] = [key, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] def SetSettings(self): """Sets settings for drawing of GCP's.""" self.highest_only = UserSettings.Get( group="gcpman", key="rms", subkey="highestonly" ) self.show_unused = UserSettings.Get( group="gcpman", key="symbol", subkey="unused" ) colours = { "color": "default", "hcolor": "highest", "scolor": "selected", "ucolor": "unused", } wpx = UserSettings.Get(group="gcpman", key="symbol", subkey="width") for k, v in six.iteritems(colours): col = UserSettings.Get(group="gcpman", key="symbol", subkey=k) self.pointsToDrawSrc.GetPen(v).SetColour( wx.Colour(col[0], col[1], col[2], 255) ) # TODO GetPen neni to spatne? self.pointsToDrawTgt.GetPen(v).SetColour( wx.Colour(col[0], col[1], col[2], 255) ) self.pointsToDrawSrc.GetPen(v).SetWidth(wpx) self.pointsToDrawTgt.GetPen(v).SetWidth(wpx) spx = UserSettings.Get(group="gcpman", key="symbol", subkey="size") self.pointsToDrawSrc.SetPropertyVal("size", int(spx)) self.pointsToDrawTgt.SetPropertyVal("size", int(spx)) font = self.GetFont() font.SetPointSize(int(spx) + 2) textProp = {} textProp["active"] = True textProp["font"] = font self.pointsToDrawSrc.SetPropertyVal("text", textProp) self.pointsToDrawTgt.SetPropertyVal("text", copy(textProp)) def SetGCPSatus(self, item, itemIndex): """Before GCP is drawn, decides it's colour and whether it will be drawed. """ key = self.list.GetItemData(itemIndex) # incremented because of itemDataMap (has one more item) - will be # changed itemIndex += 1 if not self.list.IsItemChecked(key - 1): wxPen = "unused" if not self.show_unused: item.SetPropertyVal("hide", True) else: item.SetPropertyVal("hide", False) else: item.SetPropertyVal("hide", False) if self.highest_only: if itemIndex == self.highest_key: wxPen = "highest" else: wxPen = "default" else: if self.mapcoordlist[key][7] > self.rmsthresh: wxPen = "highest" else: wxPen = "default" if itemIndex == self.list.selectedkey: wxPen = "selected" item.SetPropertyVal("label", str(itemIndex)) item.SetPropertyVal("penName", wxPen) def SetGCPData(self, coordtype, coord, mapdisp=None, confirm=False): """Inserts coordinates from file, mouse click on map, or after editing into selected item of GCP list and checks it for use. """ index = self.list.GetSelected() if index == wx.NOT_FOUND: return coord0 = coord[0] coord1 = coord[1] key = self.list.GetItemData(index) if confirm: if self.MapWindow == self.SrcMapWindow: currloc = _("source") else: currloc = _("target") ret = wx.MessageBox( parent=self, caption=_("Set GCP coordinates"), message=_( "Set %(coor)s coordinates for GCP No. %(key)s? \n\n" "East: %(coor0)s \n" "North: %(coor1)s" ) % { "coor": currloc, "key": str(key), "coor0": str(coord0), "coor1": str(coord1), }, style=wx.ICON_QUESTION | wx.YES_NO | wx.CENTRE, ) # for wingrass if os.name == "nt": self.MapWindow.SetFocus() if ret == wx.NO: return if coordtype == "source": self.list.SetItem(index, 1, str(coord0)) self.list.SetItem(index, 2, str(coord1)) self.mapcoordlist[key][1] = coord[0] self.mapcoordlist[key][2] = coord[1] self.pointsToDrawSrc.GetItem(key - 1).SetCoords([coord0, coord1]) elif coordtype == "target": self.list.SetItem(index, 4, str(coord0)) self.list.SetItem(index, 5, str(coord1)) self.mapcoordlist[key][4] = coord[0] self.mapcoordlist[key][5] = coord[1] # ADD ELEVATION FROM MAP AS HEIGHT PARAMETER if os.path.exists(self.file["elevation"]): # Parse the i.ortho.elev generated file # Get all lines from file lines = open(self.file["elevation"]).read().splitlines() # Remove empty spaces in lines lines = [x.replace(" ", "") for x in lines] # Extract map@mapset elevationmap = lines[0].split(":")[1] + "@" + lines[1].split(":")[1] # Make sure the region is set to the elevation map ret, msg = RunCommand( "g.region", parent=self, getErrorMsg=True, quiet=True, raster=elevationmap, flags=None, ) # Get the elevation height from the map given by i.ortho.elev from subprocess import PIPE from grass.pygrass.modules import Module rwhat = Module( "r.what", map=elevationmap, coordinates=[coord[0], coord[1]], stdout_=PIPE, ) self.mapcoordlist[key][6] = rwhat.outputs.stdout.split("|")[3].rstrip( "\n" ) self.list.SetItem(index, 6, str(self.mapcoordlist[key][6])) self.pointsToDrawTgt.GetItem(key - 1).SetCoords([coord0, coord1]) self.list.SetItem(index, 7, "0") self.list.SetItem(index, 8, "0") self.mapcoordlist[key][7] = 0.0 self.mapcoordlist[key][8] = 0.0 # self.list.ResizeColumns() def SaveGCPs(self, event): """Make a CONTROL_POINTS file or save GCP coordinates to existing POINTS file """ self.GCPcount = 0 try: f = open(self.file["control_points"], mode="w") # use os.linesep or '\n' here ??? f.write("# Ground Control Points File\n") f.write("# \n") f.write("# target location: " + self.currentlocation + "\n") f.write("# target mapset: " + self.currentmapset + "\n") f.write("#\tsource\t\t\ttarget\t\t\tstatus\n") f.write("#\teast\tnorth\theight\teast\tnorth\theight\t(1=ok, 0=ignore)\n") f.write( "#---------------------------- --------------------------- ---------------\n" ) for index in range(self.list.GetItemCount()): if self.list.IsItemChecked(index): check = "1" self.GCPcount += 1 else: check = "0" coord0 = self.list.GetItem(index, 1).GetText() coord1 = self.list.GetItem(index, 2).GetText() coord2 = self.list.GetItem(index, 3).GetText() coord3 = self.list.GetItem(index, 4).GetText() coord4 = self.list.GetItem(index, 5).GetText() coord5 = self.list.GetItem(index, 6).GetText() f.write( coord0 + " " + coord1 + " " + coord2 + " " + coord3 + " " + coord4 + " " + coord5 + " " + check + "\n" ) except IOError as err: GError( parent=self, message="%s <%s>. %s%s" % ( _("Writing CONTROL_POINTS file failed"), self.file["control_points"], os.linesep, err, ), ) return f.close() # if event != None save also to backup file if event: shutil.copy(self.file["control_points"], self.file["control_points_bak"]) self._giface.WriteLog( _("CONTROL_POINTS file saved for group <%s>") % self.xygroup ) # self.SetStatusText(_('POINTS file saved')) def ReadGCPs(self): """ Reads GCPs and georectified coordinates from POINTS file """ self.GCPcount = 0 sourceMapWin = self.SrcMapWindow targetMapWin = self.TgtMapWindow if not sourceMapWin: GError(parent=self, message=_("source mapwin not defined")) if not targetMapWin: GError(parent=self, message=_("target mapwin not defined")) try: f = open(self.file["control_points"], "r") GCPcnt = 0 for line in f.readlines(): if line[0] == "#" or line == "": continue line = line.replace("\n", "").strip() coords = list(map(float, line.split())) if coords[6] == 1: check = True self.GCPcount += 1 else: check = False self.AddGCP(event=None) self.SetGCPData("source", (coords[0], coords[1]), sourceMapWin) self.SetGCPData("target", (coords[3], coords[4]), targetMapWin) index = self.list.GetSelected() if index != wx.NOT_FOUND: self.list.CheckItem(index, check) GCPcnt += 1 except IOError as err: GError( parent=self, message="%s <%s>. %s%s" % ( _("Reading CONTROL_POINTS file failed"), self.file["control_points"], os.linesep, err, ), ) return f.close() if GCPcnt == 0: # 3 gcp is minimum for i in range(3): self.AddGCP(None) if self.CheckGCPcount(): # calculate RMS self.RMSError(self.xygroup, self.gr_order) def ReloadGCPs(self, event): """Reload data from file""" # use backup shutil.copy(self.file["control_points_bak"], self.file["control_points"]) # delete all items in mapcoordlist self.mapcoordlist = [] self.mapcoordlist.append( [ 0, # GCP number 0.0, # source east 0.0, # source north 0.0, # source height 0.0, # target east 0.0, # target north 0.0, # target height 0.0, # forward error 0.0, ] ) # backward error self.list.LoadData() self.itemDataMap = self.mapcoordlist if self._col != -1: self.list.ClearColumnImage(self._col) self._colSortFlag = [1] * self.list.GetColumnCount() # draw GCPs (source and target) sourceMapWin = self.SrcMapWindow sourceMapWin.UpdateMap(render=False, renderVector=False) if self.show_target: targetMapWin = self.TgtMapWindow targetMapWin.UpdateMap(render=False, renderVector=False) def OnFocus(self, event): # TODO: it is here just to remove old or obsolate beavior of base class gcp/MapPanel? # self.grwiz.SwitchEnv('source') pass def _onMouseLeftUpPointer(self, mapWindow, x, y): if mapWindow == self.SrcMapWindow: coordtype = "source" else: coordtype = "target" coord = (x, y) self.SetGCPData(coordtype, coord, self, confirm=True) mapWindow.UpdateMap(render=False, renderVector=False) def OnRMS(self, event): """ RMS button handler """ self.RMSError(self.xygroup, self.gr_order) sourceMapWin = self.SrcMapWindow sourceMapWin.UpdateMap(render=False, renderVector=False) if self.show_target: targetMapWin = self.TgtMapWindow targetMapWin.UpdateMap(render=False, renderVector=False) def CheckGCPcount(self, msg=False): """ Checks to make sure that the minimum number of GCPs have been defined and are active for the selected transformation order """ if ( (self.GCPcount < 3 and self.gr_order == 1) or (self.GCPcount < 6 and self.gr_order == 2) or (self.GCPcount < 10 and self.gr_order == 3) ): if msg: GWarning( parent=self, message=_( "Insufficient points defined and active (checked) " "for selected rectification method (order: %d).\n" "3+ points needed for 1st order,\n" "6+ points for 2nd order, and\n" "10+ points for 3rd order." ) % self.gr_order, ) return False else: return True def OnGeorect(self, event): """ Georectifies map(s) in group using i.ortho.rectify or v.rectify """ global maptype self.SaveGCPs(None) if not self.CheckGCPcount(msg=True): return if maptype == "raster": self.grwiz.SwitchEnv("source") if self.clip_to_region: flags = "ac" else: flags = "a" busy = wx.BusyInfo(_("Rectifying images, please wait..."), parent=self) wx.GetApp().Yield() ret, msg = RunCommand( "i.ortho.rectify", parent=self, getErrorMsg=True, quiet=True, group=self.xygroup, extension=self.extension, method=self.gr_method, angle=self.grwiz.cam_angle, flags=flags, ) del busy # provide feedback on failure if ret != 0: print(msg, file=sys.stderr) elif maptype == "vector": # loop through all vectors in VREF self.grwiz.SwitchEnv("source") # make list of vectors to georectify from VREF f = open(self.file["vgrp"]) vectlist = [] try: for vect in f.readlines(): vect = vect.strip("\n") if len(vect) < 1: continue vectlist.append(vect) finally: f.close() # georectify each vector in VREF using v.rectify for vect in vectlist: self.outname = str(vect.split("@")[0]) + self.extension self._giface.WriteLog( text=_("Transforming <%s>...") % vect, notification=Notification.MAKE_VISIBLE, ) ret = msg = "" busy = wx.BusyInfo( _("Rectifying vector map <%s>, please wait...") % vect, parent=self ) wx.GetApp().Yield() ret, msg = RunCommand( "v.rectify", parent=self, getErrorMsg=True, quiet=True, input=vect, output=self.outname, group=self.xygroup, order=self.gr_order, ) del busy # provide feedback on failure if ret != 0: print(msg, file=sys.stderr) self.grwiz.SwitchEnv("target") def OnGeorectDone(self, **kargs): """Print final message""" global maptype if maptype == "raster": return returncode = kargs["returncode"] if returncode == 0: self.VectGRList.append(self.outname) print("*****vector list = " + str(self.VectGRList), file=sys.stderr) else: self._giface.WriteError( _("Georectification of vector map <%s> failed") % self.outname ) def OnSettings(self, event): """GCP Manager settings""" dlg = GrSettingsDialog( parent=self, giface=self._giface, id=wx.ID_ANY, title=_("GCP Manager settings"), ) if dlg.ShowModal() == wx.ID_OK: pass dlg.Destroy() def UpdateColours( self, srcrender=False, srcrenderVector=False, tgtrender=False, tgtrenderVector=False, ): """update colours""" highest_fwd_err = 0.0 self.highest_key = 0 highest_idx = 0 for index in range(self.list.GetItemCount()): if self.list.IsItemChecked(index): key = self.list.GetItemData(index) fwd_err = self.mapcoordlist[key][5] if self.highest_only: self.list.SetItemTextColour(index, wx.BLACK) if highest_fwd_err < fwd_err: highest_fwd_err = fwd_err self.highest_key = key highest_idx = index elif self.rmsthresh > 0: if fwd_err > self.rmsthresh: self.list.SetItemTextColour(index, wx.RED) else: self.list.SetItemTextColour(index, wx.BLACK) else: self.list.SetItemTextColour(index, wx.BLACK) if self.highest_only and highest_fwd_err > 0.0: self.list.SetItemTextColour(highest_idx, wx.RED) sourceMapWin = self.SrcMapWindow sourceMapWin.UpdateMap(render=srcrender, renderVector=srcrenderVector) if self.show_target: targetMapWin = self.TgtMapWindow targetMapWin.UpdateMap(render=tgtrender, renderVector=tgtrenderVector) def OnQuit(self, event): """Quit georectifier""" ret = wx.MessageBox( parent=self, caption=_("Quit GCP Manager"), message=_("Save ground control points?"), style=wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.CENTRE, ) if ret != wx.CANCEL: if ret == wx.YES: self.SaveGCPs(None) elif ret == wx.NO: # restore POINTS file from backup if os.path.exists(self.file["control_points_bak"]): shutil.copy( self.file["control_points_bak"], self.file["control_points"] ) if os.path.exists(self.file["control_points_bak"]): os.unlink(self.file["control_points_bak"]) self.SrcMap.Clean() self.TgtMap.Clean() self.grwiz.Cleanup() self._mgr.UnInit() self.Destroy() # event.Skip() def OnGROrder(self, event): """ sets transformation order for georectifying """ if event: self.gr_order = event.GetInt() + 1 numOfItems = self.list.GetItemCount() minNumOfItems = numOfItems if self.gr_order == 1: minNumOfItems = 3 # self.SetStatusText(_('Insufficient points, 3+ points needed for 1st order')) elif self.gr_order == 2: minNumOfItems = 6 diff = 6 - numOfItems # self.SetStatusText(_('Insufficient points, 6+ points needed for 2nd order')) elif self.gr_order == 3: minNumOfItems = 10 # self.SetStatusText(_('Insufficient points, 10+ points needed for 3rd order')) for i in range(minNumOfItems - numOfItems): self.AddGCP(None) return minNumOfItems def RMSError(self, xygroup, order): """ Uses i.ortho.transform to calculate forward and backward error for each used GCP in CONTROL_POINTS file and insert error values into GCP list. Calculates total forward and backward RMS error for all used points """ # save GCPs to points file to make sure that all checked GCPs are used self.SaveGCPs(None) # self.SetStatusText('') if not self.CheckGCPcount(msg=True): return # get list of forward and reverse rms error values for each point self.grwiz.SwitchEnv("source") ret = RunCommand("i.ortho.transform", parent=self, read=True, group=xygroup) self.grwiz.SwitchEnv("target") if ret: errlist = ret.splitlines() else: GError( parent=self, message=_( "Could not calculate RMS Error.\n" "Possible error with i.ortho.transform." ), ) return # insert error values into GCP list for checked items sdfactor = float(UserSettings.Get(group="gcpman", key="rms", subkey="sdfactor")) GCPcount = 0 sumsq_fwd_err = 0.0 sumsq_bkw_err = 0.0 sum_fwd_err = 0.0 highest_fwd_err = 0.0 self.highest_key = 0 highest_idx = 0 for index in range(self.list.GetItemCount()): key = self.list.GetItemData(index) if self.list.IsItemChecked(index): fwd_err, bkw_err = errlist[GCPcount].split() self.list.SetItem(index, 7, fwd_err) self.list.SetItem(index, 8, bkw_err) self.mapcoordlist[key][7] = float(fwd_err) self.mapcoordlist[key][8] = float(bkw_err) self.list.SetItemTextColour(index, wx.BLACK) if self.highest_only: if highest_fwd_err < float(fwd_err): highest_fwd_err = float(fwd_err) self.highest_key = key highest_idx = index sumsq_fwd_err += float(fwd_err) ** 2 sumsq_bkw_err += float(bkw_err) ** 2 sum_fwd_err += float(fwd_err) GCPcount += 1 else: self.list.SetItem(index, 7, "") self.list.SetItem(index, 8, "") self.mapcoordlist[key][7] = 0.0 self.mapcoordlist[key][8] = 0.0 self.list.SetItemTextColour(index, wx.BLACK) # SD if GCPcount > 0: self.rmsmean = sum_fwd_err / GCPcount self.rmssd = (sumsq_fwd_err - self.rmsmean ** 2) ** 0.5 self.rmsthresh = self.rmsmean + sdfactor * self.rmssd else: self.rmsthresh = 0 self.rmsmean = 0 self.rmssd = 0 if self.highest_only and highest_fwd_err > 0.0: self.list.SetItemTextColour(highest_idx, wx.RED) elif GCPcount > 0 and self.rmsthresh > 0 and not self.highest_only: for index in range(self.list.GetItemCount()): if self.list.IsItemChecked(index): key = self.list.GetItemData(index) if self.mapcoordlist[key][7] > self.rmsthresh: self.list.SetItemTextColour(index, wx.RED) # calculate global RMS error (geometric mean) self.fwd_rmserror = round((sumsq_fwd_err / GCPcount) ** 0.5, 4) self.bkw_rmserror = round((sumsq_bkw_err / GCPcount) ** 0.5, 4) self.list.ResizeColumns() def GetNewExtent(self, region, map=None): coord_file = utils.GetTempfile() newreg = { "n": 0.0, "s": 0.0, "e": 0.0, "w": 0.0, } try: f = open(coord_file, mode="w") # NW corner f.write(str(region["e"]) + " " + str(region["n"]) + "\n") # NE corner f.write(str(region["e"]) + " " + str(region["s"]) + "\n") # SW corner f.write(str(region["w"]) + " " + str(region["n"]) + "\n") # SE corner f.write(str(region["w"]) + " " + str(region["s"]) + "\n") finally: f.close() # save GCPs to points file to make sure that all checked GCPs are used self.SaveGCPs(None) order = self.gr_order self.gr_order = 1 if not self.CheckGCPcount(msg=True): self.gr_order = order return self.gr_order = order # get list of forward and reverse rms error values for each point self.grwiz.SwitchEnv("source") if map == "source": ret = RunCommand( "i.ortho.transform", parent=self, read=True, group=self.xygroup, format="dst", coords=coord_file, ) elif map == "target": ret = RunCommand( "i.ortho.transform", parent=self, read=True, group=self.xygroup, flags="r", format="src", coords=coord_file, ) os.unlink(coord_file) self.grwiz.SwitchEnv("target") if ret: errlist = ret.splitlines() else: GError( parent=self, message=_( "Could not calculate new extends.\n" "Possible error with i.ortho.transform." ), ) return # fist corner e, n = errlist[0].split() fe = float(e) fn = float(n) newreg["n"] = fn newreg["s"] = fn newreg["e"] = fe newreg["w"] = fe # other three corners for i in range(1, 4): e, n = errlist[i].split() fe = float(e) fn = float(n) if fe < newreg["w"]: newreg["w"] = fe if fe > newreg["e"]: newreg["e"] = fe if fn < newreg["s"]: newreg["s"] = fn if fn > newreg["n"]: newreg["n"] = fn return newreg def OnHelp(self, event): """Show GCP Manager manual page""" self._giface.Help(entry="wxGUI.gcp") def OnUpdateActive(self, event): if self.activemap.GetSelection() == 0: self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap else: self.MapWindow = self.TgtMapWindow self.Map = self.TgtMap self.UpdateActive(self.MapWindow) # for wingrass if os.name == "nt": self.MapWindow.SetFocus() def UpdateActive(self, win): # optionally disable tool zoomback tool self.GetMapToolbar().Enable( "zoomback", enable=(len(self.MapWindow.zoomhistory) > 1) ) if self.activemap.GetSelection() != (win == self.TgtMapWindow): self.activemap.SetSelection(win == self.TgtMapWindow) self.StatusbarUpdate() def AdjustMap(self, newreg): """Adjust map window to new extents""" # adjust map window self.Map.region["n"] = newreg["n"] self.Map.region["s"] = newreg["s"] self.Map.region["e"] = newreg["e"] self.Map.region["w"] = newreg["w"] self.MapWindow.ZoomHistory( self.Map.region["n"], self.Map.region["s"], self.Map.region["e"], self.Map.region["w"], ) # LL locations if self.Map.projinfo["proj"] == "ll": if newreg["n"] > 90.0: newreg["n"] = 90.0 if newreg["s"] < -90.0: newreg["s"] = -90.0 ce = newreg["w"] + (newreg["e"] - newreg["w"]) / 2 cn = newreg["s"] + (newreg["n"] - newreg["s"]) / 2 # calculate new center point and display resolution self.Map.region["center_easting"] = ce self.Map.region["center_northing"] = cn self.Map.region["ewres"] = (newreg["e"] - newreg["w"]) / self.Map.width self.Map.region["nsres"] = (newreg["n"] - newreg["s"]) / self.Map.height self.Map.AlignExtentFromDisplay() self.MapWindow.ZoomHistory( self.Map.region["n"], self.Map.region["s"], self.Map.region["e"], self.Map.region["w"], ) if self.MapWindow.redrawAll is False: self.MapWindow.redrawAll = True self.MapWindow.UpdateMap() self.StatusbarUpdate() def OnZoomToSource(self, event): """Set target map window to match extents of source map window""" if not self.MapWindow == self.TgtMapWindow: self.MapWindow = self.TgtMapWindow self.Map = self.TgtMap self.UpdateActive(self.TgtMapWindow) # get new N, S, E, W for target newreg = self.GetNewExtent(self.SrcMap.region, "source") if newreg: self.AdjustMap(newreg) def OnZoomToTarget(self, event): """Set source map window to match extents of target map window""" if not self.MapWindow == self.SrcMapWindow: self.MapWindow = self.SrcMapWindow self.Map = self.SrcMap self.UpdateActive(self.SrcMapWindow) # get new N, S, E, W for target newreg = self.GetNewExtent(self.TgtMap.region, "target") if newreg: self.AdjustMap(newreg) def OnZoomMenuGCP(self, event): """Popup Zoom menu""" point = wx.GetMousePosition() zoommenu = Menu() # Add items to the menu zoomsource = wx.MenuItem( zoommenu, wx.ID_ANY, _("Adjust source display to target display") ) zoommenu.AppendItem(zoomsource) self.Bind(wx.EVT_MENU, self.OnZoomToTarget, zoomsource) zoomtarget = wx.MenuItem( zoommenu, wx.ID_ANY, _("Adjust target display to source display") ) zoommenu.AppendItem(zoomtarget) self.Bind(wx.EVT_MENU, self.OnZoomToSource, zoomtarget) # Popup the menu. If an item is selected then its handler # will be called before PopupMenu returns. self.PopupMenu(zoommenu) zoommenu.Destroy() def OnSize(self, event): """Adjust Map Windows after GCP Map Display has been resized""" # re-render image on idle self.resize = grass.clock() super(MapPanel, self).OnSize(event) def OnIdle(self, event): """GCP Map Display resized, adjust Map Windows""" if self.GetMapToolbar(): if self.resize and self.resize + 0.2 < grass.clock(): srcwidth, srcheight = self.SrcMapWindow.GetSize() tgtwidth, tgtheight = self.TgtMapWindow.GetSize() srcwidth = (srcwidth + tgtwidth) / 2 if self.show_target: self._mgr.GetPane("target").Hide() self._mgr.Update() self._mgr.GetPane("source").BestSize((srcwidth, srcheight)) self._mgr.GetPane("target").BestSize((srcwidth, tgtheight)) if self.show_target: self._mgr.GetPane("target").Show() self._mgr.Update() self.resize = False elif self.resize: event.RequestMore() pass class GCPDisplay(FrameMixin, GCPPanel): """Map display for wrapping map panel with frame methods""" def __init__(self, parent, giface, grwiz, id, lmgr, Map, title, **kwargs): # init map panel GCPPanel.__init__( self, parent=parent, giface=giface, grwiz=grwiz, id=id, lmgr=lmgr, Map=Map, title=title, **kwargs, ) # set system icon parent.iconsize = (16, 16) parent.SetIcon( wx.Icon( os.path.join(globalvar.ICONDIR, "grass_map.ico"), wx.BITMAP_TYPE_ICO ) ) # bind to frame parent.Bind(wx.EVT_CLOSE, self.OnQuit) # extend shortcuts and create frame accelerator table self.shortcuts_table.append((self.OnFullScreen, wx.ACCEL_NORMAL, wx.WXK_F11)) self._initShortcuts() # add Map Display panel to Map Display frame sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self, proportion=1, flag=wx.EXPAND) parent.SetSizer(sizer) parent.Layout() class GCPList(ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin): def __init__( self, parent, gcp, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES | wx.LC_SINGLE_SEL, ): ListCtrl.__init__(self, parent, id, pos, size, style) self.gcp = gcp # GCP class self.render = True # Mixin settings CheckListCtrlMixin.__init__(self) ListCtrlAutoWidthMixin.__init__(self) # TextEditMixin.__init__(self) # tracks whether list items are checked or not self.CheckList = [] self._Create() self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick) self.selected = wx.NOT_FOUND self.selectedkey = -1 def _Create(self): if 0: # normal, simple columns idx_col = 0 for col in ( _("use"), _("source E"), _("source N"), _("source Z"), _("target E"), _("target N"), _("target Z"), _("Forward error"), _("Backward error"), ): self.InsertColumn(idx_col, col) idx_col += 1 else: # the hard way: we want images on the column header info = wx.ListItem() info.SetMask(wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT) info.SetImage(-1) info.m_format = wx.LIST_FORMAT_LEFT idx_col = 0 for lbl in ( _("use"), _("source E"), _("source N"), _("source Z"), _("target E"), _("target N"), _("target Z"), _("Forward error"), _("Backward error"), ): info.SetText(lbl) self.InsertColumn(idx_col, info) idx_col += 1 def LoadData(self): """Load data into list""" self.DeleteAllItems() self.render = False if os.path.isfile(self.gcp.file["control_points"]): self.gcp.ReadGCPs() else: # 3 gcp is minimum for i in range(3): self.gcp.AddGCP(None) # select first point by default self.selected = 0 self.selectedkey = self.GetItemData(self.selected) self.SetItemState(self.selected, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) self.ResizeColumns() self.render = True self.EnsureVisible(self.selected) def OnCheckItem(self, index, flag): """Item is checked/unchecked""" if self.render: # redraw points sourceMapWin = self.gcp.SrcMapWindow sourceMapWin.UpdateMap(render=False, renderVector=False) if self.gcp.show_target: targetMapWin = self.gcp.TgtMapWindow targetMapWin.UpdateMap(render=False, renderVector=False) def AddGCPItem(self): """ Appends an item to GCP list """ self.selectedkey = self.GetItemCount() + 1 self.Append( [ str(self.selectedkey), # GCP number "0.0", # source E "0.0", # source N "0.0", # source Z "0.0", # target E "0.0", # target N "0.0", # target Z "", # forward error "", ] ) # backward error self.selected = self.GetItemCount() - 1 self.SetItemData(self.selected, self.selectedkey) self.SetItemState(self.selected, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) self.ResizeColumns() self.gcp.pointsToDrawSrc.AddItem(coords=[0, 0], label=str(self.selectedkey)) self.gcp.pointsToDrawTgt.AddItem(coords=[0, 0], label=str(self.selectedkey)) self.EnsureVisible(self.selected) return self.selected def DeleteGCPItem(self): """Deletes selected item in GCP list.""" if self.selected == wx.NOT_FOUND: return key = self.GetItemData(self.selected) self.DeleteItem(self.selected) if self.selected != wx.NOT_FOUND: item = self.gcp.pointsToDrawSrc.GetItem(key - 1) self.gcp.pointsToDrawSrc.DeleteItem(item) item = self.gcp.pointsToDrawTgt.GetItem(key - 1) self.gcp.pointsToDrawTgt.DeleteItem(item) return key def ResizeColumns(self): """Resize columns""" minWidth = [90, 120] for i in range(self.GetColumnCount()): self.SetColumnWidth(i, wx.LIST_AUTOSIZE) # first column is checkbox, don't set to minWidth if i > 0 and self.GetColumnWidth(i) < minWidth[i > 4]: self.SetColumnWidth(i, minWidth[i > 4]) self.SendSizeEvent() def GetSelected(self): """Get index of selected item""" return self.selected def OnItemSelected(self, event): """Item selected""" if self.render and self.selected != event.GetIndex(): self.selected = event.GetIndex() self.selectedkey = self.GetItemData(self.selected) sourceMapWin = self.gcp.SrcMapWindow sourceMapWin.UpdateMap(render=False, renderVector=False) if self.gcp.show_target: targetMapWin = self.gcp.TgtMapWindow targetMapWin.UpdateMap(render=False, renderVector=False) event.Skip() def OnItemActivated(self, event): """ When item double clicked, open editor to update coordinate values """ coords = [] index = event.GetIndex() key = self.GetItemData(index) changed = False for i in range(1, 7): coords.append(self.GetItem(index, i).GetText()) dlg = EditGCP(parent=self, id=wx.ID_ANY, data=coords, gcpno=key) if dlg.ShowModal() == wx.ID_OK: values = dlg.GetValues() # string if len(values) == 0: GError( parent=self, message=_("Invalid coordinate value. Operation canceled."), ) else: for i in range(len(values)): if values[i] != coords[i]: self.SetItem(index, i + 1, values[i]) changed = True if changed: # reset RMS and update mapcoordlist self.SetItem(index, 7, "") self.SetItem(index, 8, "") key = self.GetItemData(index) self.gcp.mapcoordlist[key] = [ key, float(values[0]), float(values[1]), float(values[2]), float(values[3]), float(values[4]), float(values[5]), 0.0, 0.0, ] self.gcp.pointsToDrawSrc.GetItem(key - 1).SetCoords( [float(values[0]), float(values[1])] ) self.gcp.pointsToDrawTgt.GetItem(key - 1).SetCoords( [float(values[3]), float(values[4])] ) self.gcp.UpdateColours() def OnColClick(self, event): """ListCtrl forgets selected item...""" self.selected = self.FindItem(-1, self.selectedkey) self.SetItemState(self.selected, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) event.Skip() class VectGroup(wx.Dialog): """Dialog to create a vector group (VREF file) for georectifying .. todo:: Replace by g.group """ def __init__( self, parent, id, grassdb, location, mapset, group, style=wx.DEFAULT_DIALOG_STYLE, ): wx.Dialog.__init__( self, parent, id, style=style, title=_("Create vector map group") ) self.grassdatabase = grassdb self.xylocation = location self.xymapset = mapset self.xygroup = group # # get list of valid vector directories # vectlist = os.listdir( os.path.join(self.grassdatabase, self.xylocation, self.xymapset, "vector") ) for dir in vectlist: if not os.path.isfile( os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "vector", dir, "coor", ) ): vectlist.remove(dir) utils.ListSortLower(vectlist) # path to vref file self.vgrpfile = os.path.join( self.grassdatabase, self.xylocation, self.xymapset, "group", self.xygroup, "VREF", ) # # buttons # self.btnCancel = Button(parent=self, id=wx.ID_CANCEL) self.btnOK = Button(parent=self, id=wx.ID_OK) self.btnOK.SetDefault() # # list of vector maps # self.listMap = CheckListBox(parent=self, id=wx.ID_ANY, choices=vectlist) if os.path.isfile(self.vgrpfile): f = open(self.vgrpfile) try: checked = [] for line in f.readlines(): line = line.replace("\n", "") if len(line) < 1: continue checked.append(line) self.listMap.SetCheckedStrings(checked) finally: f.close() line = wx.StaticLine( parent=self, id=wx.ID_ANY, size=(20, -1), style=wx.LI_HORIZONTAL ) # # layout # sizer = wx.BoxSizer(wx.VERTICAL) box = wx.BoxSizer(wx.HORIZONTAL) box.Add( StaticText( parent=self, id=wx.ID_ANY, label=_("Select vector map(s) to add to group:"), ), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, border=5, ) box.Add( self.listMap, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, border=5, ) sizer.Add(box, flag=wx.ALIGN_RIGHT | wx.ALL, border=3) sizer.Add( line, proportion=0, flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, border=5, ) # buttons btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(self.btnCancel) btnSizer.AddButton(self.btnOK) btnSizer.Realize() sizer.Add( btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5 ) self.SetSizer(sizer) sizer.Fit(self) self.Layout() def MakeVGroup(self): """Create VREF file""" vgrouplist = [] for item in range(self.listMap.GetCount()): if not self.listMap.IsChecked(item): continue vgrouplist.append(self.listMap.GetString(item) + "@" + self.xymapset) f = open(self.vgrpfile, mode="w") try: for vect in vgrouplist: f.write(vect + "\n") finally: f.close() class EditGCP(wx.Dialog): def __init__( self, parent, data, gcpno, id=wx.ID_ANY, title=_("Edit GCP"), style=wx.DEFAULT_DIALOG_STYLE, ): """Dialog for editing GPC and map coordinates in list control""" wx.Dialog.__init__(self, parent, id, title=title, style=style) panel = wx.Panel(parent=self) sizer = wx.BoxSizer(wx.VERTICAL) box = StaticBox( parent=panel, id=wx.ID_ANY, label=" %s %s " % (_("Ground Control Point No."), str(gcpno)), ) boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) # source coordinates gridSizer = wx.GridBagSizer(vgap=5, hgap=5) self.xcoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) self.ycoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) self.zcoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) self.ecoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) self.ncoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) self.hcoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) # swap source N, target E tmp_coord = data[1] data[1] = data[2] data[2] = tmp_coord row = 0 col = 0 idx = 0 for label, win in ( (_("source E:"), self.xcoord), (_("target E:"), self.ecoord), (_("source N:"), self.ycoord), (_("target N:"), self.ncoord), (_("source Z:"), self.zcoord), (_("target Z:"), self.hcoord), ): label = StaticText(parent=panel, id=wx.ID_ANY, label=label) gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, col)) col += 1 win.SetValue(str(data[idx])) gridSizer.Add(win, pos=(row, col)) col += 1 idx += 1 if col > 3: row += 1 col = 0 boxSizer.Add(gridSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) sizer.Add(boxSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) # # buttons # self.btnCancel = Button(panel, wx.ID_CANCEL) self.btnOk = Button(panel, wx.ID_OK) self.btnOk.SetDefault() btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(self.btnCancel) btnSizer.AddButton(self.btnOk) btnSizer.Realize() sizer.Add(btnSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) panel.SetSizer(sizer) sizer.Fit(self) def GetValues(self, columns=None): """Return list of values (as strings).""" valuelist = [] try: float(self.xcoord.GetValue()) float(self.ycoord.GetValue()) float(self.zcoord.GetValue()) float(self.ecoord.GetValue()) float(self.ncoord.GetValue()) float(self.hcoord.GetValue()) except ValueError: return valuelist valuelist.append(self.xcoord.GetValue()) valuelist.append(self.ycoord.GetValue()) valuelist.append(self.zcoord.GetValue()) valuelist.append(self.ecoord.GetValue()) valuelist.append(self.ncoord.GetValue()) valuelist.append(self.hcoord.GetValue()) return valuelist class GrSettingsDialog(wx.Dialog): def __init__( self, parent, id, giface, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE, ): wx.Dialog.__init__(self, parent, id, title, pos, size, style) """ Dialog to set profile text options: font, title and font size, axis labels and font size """ # # initialize variables # self.parent = parent self.new_src_map = src_map self.new_tgt_map = {"raster": tgt_map["raster"], "vector": tgt_map["vector"]} self.sdfactor = 0 self.symbol = {} self.methods = [ "nearest", "linear", "linear_f", "cubic", "cubic_f", "lanczos", "lanczos_f", ] # notebook notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT) self.__CreateSymbologyPage(notebook) self.__CreateRectificationPage(notebook) # buttons btnSave = Button(self, wx.ID_SAVE) btnApply = Button(self, wx.ID_APPLY) btnClose = Button(self, wx.ID_CLOSE) btnApply.SetDefault() # bindings btnApply.Bind(wx.EVT_BUTTON, self.OnApply) btnApply.SetToolTip(_("Apply changes for the current session")) btnSave.Bind(wx.EVT_BUTTON, self.OnSave) btnSave.SetToolTip( _( "Apply and save changes to user settings file (default for next sessions)" ) ) btnClose.Bind(wx.EVT_BUTTON, self.OnClose) btnClose.SetToolTip(_("Close dialog")) # sizers btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(btnApply, flag=wx.LEFT | wx.RIGHT, border=5) btnSizer.Add(btnSave, flag=wx.LEFT | wx.RIGHT, border=5) btnSizer.Add(btnClose, flag=wx.LEFT | wx.RIGHT, border=5) # sizers mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) mainSizer.Add(btnSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) # flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5) self.SetSizer(mainSizer) mainSizer.Fit(self) def __CreateSymbologyPage(self, notebook): """Create notebook page with symbology settings""" panel = wx.Panel(parent=notebook, id=wx.ID_ANY) notebook.AddPage(page=panel, text=_("Symbology")) sizer = wx.BoxSizer(wx.VERTICAL) rmsgridSizer = wx.GridBagSizer(vgap=5, hgap=5) # highlight only highest forward RMS error self.highlighthighest = wx.CheckBox( parent=panel, id=wx.ID_ANY, label=_("Highlight highest RMS error only") ) hh = UserSettings.Get(group="gcpman", key="rms", subkey="highestonly") self.highlighthighest.SetValue(hh) rmsgridSizer.Add( self.highlighthighest, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0, 0) ) # RMS forward error threshold rmslabel = StaticText( parent=panel, id=wx.ID_ANY, label=_("Highlight RMS error > M + SD * factor:"), ) rmslabel.SetToolTip( _( "Highlight GCPs with an RMS error larger than \n" "mean + standard deviation * given factor. \n" "Recommended values for this factor are between 1 and 2." ) ) rmsgridSizer.Add(rmslabel, flag=wx.ALIGN_CENTER_VERTICAL, pos=(1, 0)) sdfactor = UserSettings.Get(group="gcpman", key="rms", subkey="sdfactor") self.rmsWin = TextCtrl( parent=panel, id=wx.ID_ANY, size=(70, -1), style=wx.TE_NOHIDESEL ) self.rmsWin.SetValue("%s" % str(sdfactor)) if self.parent.highest_only: self.rmsWin.Disable() self.symbol["sdfactor"] = self.rmsWin.GetId() rmsgridSizer.Add(self.rmsWin, flag=wx.ALIGN_RIGHT, pos=(1, 1)) rmsgridSizer.AddGrowableCol(1) sizer.Add(rmsgridSizer, flag=wx.EXPAND | wx.ALL, border=5) box = StaticBox(parent=panel, id=wx.ID_ANY, label=" %s " % _("Symbol settings")) boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) gridSizer = wx.GridBagSizer(vgap=5, hgap=5) # # general symbol color # row = 0 label = StaticText(parent=panel, id=wx.ID_ANY, label=_("Color:")) gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) col = UserSettings.Get(group="gcpman", key="symbol", subkey="color") colWin = csel.ColourSelect( parent=panel, id=wx.ID_ANY, colour=wx.Colour(col[0], col[1], col[2], 255) ) self.symbol["color"] = colWin.GetId() gridSizer.Add(colWin, flag=wx.ALIGN_RIGHT, pos=(row, 1)) # # symbol color for high forward RMS error # row += 1 label = StaticText( parent=panel, id=wx.ID_ANY, label=_("Color for high RMS error:") ) gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) hcol = UserSettings.Get(group="gcpman", key="symbol", subkey="hcolor") hcolWin = csel.ColourSelect( parent=panel, id=wx.ID_ANY, colour=wx.Colour(hcol[0], hcol[1], hcol[2], 255) ) self.symbol["hcolor"] = hcolWin.GetId() gridSizer.Add(hcolWin, flag=wx.ALIGN_RIGHT, pos=(row, 1)) # # symbol color for selected GCP # row += 1 label = StaticText( parent=panel, id=wx.ID_ANY, label=_("Color for selected GCP:") ) gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) scol = UserSettings.Get(group="gcpman", key="symbol", subkey="scolor") scolWin = csel.ColourSelect( parent=panel, id=wx.ID_ANY, colour=wx.Colour(scol[0], scol[1], scol[2], 255) ) self.symbol["scolor"] = scolWin.GetId() gridSizer.Add(scolWin, flag=wx.ALIGN_RIGHT, pos=(row, 1)) # # symbol color for unused GCP # row += 1 label = StaticText( parent=panel, id=wx.ID_ANY, label=_("Color for unused GCPs:") ) gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) ucol = UserSettings.Get(group="gcpman", key="symbol", subkey="ucolor") ucolWin = csel.ColourSelect( parent=panel, id=wx.ID_ANY, colour=wx.Colour(ucol[0], ucol[1], ucol[2], 255) ) self.symbol["ucolor"] = ucolWin.GetId() gridSizer.Add(ucolWin, flag=wx.ALIGN_RIGHT, pos=(row, 1)) # show unused GCPs row += 1 self.showunused = wx.CheckBox( parent=panel, id=wx.ID_ANY, label=_("Show unused GCPs") ) shuu = UserSettings.Get(group="gcpman", key="symbol", subkey="unused") self.showunused.SetValue(shuu) gridSizer.Add(self.showunused, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) # # symbol size # row += 1 label = StaticText(parent=panel, id=wx.ID_ANY, label=_("Symbol size:")) gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) symsize = int(UserSettings.Get(group="gcpman", key="symbol", subkey="size")) sizeWin = SpinCtrl(parent=panel, id=wx.ID_ANY, min=1, max=20) sizeWin.SetValue(symsize) self.symbol["size"] = sizeWin.GetId() gridSizer.Add(sizeWin, flag=wx.ALIGN_RIGHT, pos=(row, 1)) # # symbol width # row += 1 label = StaticText(parent=panel, id=wx.ID_ANY, label=_("Line width:")) gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) width = int(UserSettings.Get(group="gcpman", key="symbol", subkey="width")) widWin = SpinCtrl(parent=panel, id=wx.ID_ANY, min=1, max=10) widWin.SetValue(width) self.symbol["width"] = widWin.GetId() gridSizer.Add(widWin, flag=wx.ALIGN_RIGHT, pos=(row, 1)) gridSizer.AddGrowableCol(1) boxSizer.Add(gridSizer, flag=wx.EXPAND) sizer.Add(boxSizer, flag=wx.EXPAND | wx.ALL, border=5) # # maps to display # # source map to display self.srcselection = Select( panel, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type="maptype", updateOnPopup=False, ) self.parent.grwiz.SwitchEnv("source") self.srcselection.SetElementList(maptype) # filter out all maps not in group self.srcselection.tcp.GetElementList(elements=self.parent.src_maps) # target map(s) to display self.parent.grwiz.SwitchEnv("target") self.tgtrastselection = Select( panel, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type="raster", updateOnPopup=False, ) self.tgtrastselection.SetElementList("cell") self.tgtrastselection.GetElementList() self.tgtvectselection = Select( panel, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, type="vector", updateOnPopup=False, ) self.tgtvectselection.SetElementList("vector") self.tgtvectselection.GetElementList() sizer.Add( StaticText( parent=panel, id=wx.ID_ANY, label=_("Select source map to display:") ), proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) sizer.Add( self.srcselection, proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) self.srcselection.SetValue(src_map) sizer.Add( StaticText( parent=panel, id=wx.ID_ANY, label=_("Select target raster map to display:"), ), proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) sizer.Add( self.tgtrastselection, proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) self.tgtrastselection.SetValue(tgt_map["raster"]) sizer.Add( StaticText( parent=panel, id=wx.ID_ANY, label=_("Select target vector map to display:"), ), proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) sizer.Add( self.tgtvectselection, proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) self.tgtvectselection.SetValue(tgt_map["vector"]) # bindings self.highlighthighest.Bind(wx.EVT_CHECKBOX, self.OnHighlight) self.rmsWin.Bind(wx.EVT_TEXT, self.OnSDFactor) self.srcselection.Bind(wx.EVT_TEXT, self.OnSrcSelection) self.tgtrastselection.Bind(wx.EVT_TEXT, self.OnTgtRastSelection) self.tgtvectselection.Bind(wx.EVT_TEXT, self.OnTgtVectSelection) panel.SetSizer(sizer) return panel def __CreateRectificationPage(self, notebook): """Create notebook page with symbology settings""" panel = wx.Panel(parent=notebook, id=wx.ID_ANY) notebook.AddPage(page=panel, text=_("Rectification")) sizer = wx.BoxSizer(wx.VERTICAL) # transformation order self.rb_grorder = wx.RadioBox( parent=panel, id=wx.ID_ANY, label=" %s " % _("Select rectification order"), choices=[_("1st order"), _("2nd order"), _("3rd order")], majorDimension=wx.RA_SPECIFY_COLS, ) sizer.Add(self.rb_grorder, proportion=0, flag=wx.EXPAND | wx.ALL, border=5) self.rb_grorder.SetSelection(self.parent.gr_order - 1) # interpolation method gridSizer = wx.GridBagSizer(vgap=5, hgap=5) gridSizer.Add( StaticText( parent=panel, id=wx.ID_ANY, label=_("Select interpolation method:") ), pos=(0, 0), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) self.grmethod = wx.Choice(parent=panel, id=wx.ID_ANY, choices=self.methods) gridSizer.Add(self.grmethod, pos=(0, 1), flag=wx.ALIGN_RIGHT, border=5) self.grmethod.SetStringSelection(self.parent.gr_method) gridSizer.AddGrowableCol(1) sizer.Add(gridSizer, flag=wx.EXPAND | wx.ALL, border=5) # clip to region self.check = wx.CheckBox( parent=panel, id=wx.ID_ANY, label=_("clip to computational region in target location"), ) sizer.Add(self.check, proportion=0, flag=wx.EXPAND | wx.ALL, border=5) self.check.SetValue(self.parent.clip_to_region) # extension sizer.Add( StaticText( parent=panel, id=wx.ID_ANY, label=_("Extension for output maps:") ), proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) self.ext_txt = TextCtrl(parent=panel, id=wx.ID_ANY, value="", size=(350, -1)) self.ext_txt.SetValue(self.parent.extension) sizer.Add( self.ext_txt, proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5, ) # bindings self.ext_txt.Bind(wx.EVT_TEXT, self.OnExtension) self.Bind(wx.EVT_RADIOBOX, self.parent.OnGROrder, self.rb_grorder) self.Bind(wx.EVT_CHOICE, self.OnMethod, self.grmethod) self.Bind(wx.EVT_CHECKBOX, self.OnClipRegion, self.check) panel.SetSizer(sizer) return panel def OnHighlight(self, event): """Checkbox 'highlighthighest' checked/unchecked""" if self.highlighthighest.IsChecked(): self.parent.highest_only = True self.rmsWin.Disable() else: self.parent.highest_only = False self.rmsWin.Enable() def OnSDFactor(self, event): """New factor for RMS threshold = M + SD * factor""" try: self.sdfactor = float(self.rmsWin.GetValue()) except ValueError: return if self.sdfactor <= 0: GError(parent=self, message=_("RMS threshold factor must be > 0")) elif self.sdfactor < 1: GError( parent=self, message=_( "RMS threshold factor is < 1\n" "Too many points might be highlighted" ), ) def OnSrcSelection(self, event): """Source map to display selected""" global src_map tmp_map = self.srcselection.GetValue() if not tmp_map == "" and not tmp_map == src_map: self.new_src_map = tmp_map def OnTgtRastSelection(self, event): """Target map to display selected""" global tgt_map self.new_tgt_map["raster"] = self.tgtrastselection.GetValue() def OnTgtVectSelection(self, event): """Target map to display selected""" global tgt_map self.new_tgt_map["vector"] = self.tgtvectselection.GetValue() def OnMethod(self, event): self.parent.gr_method = self.methods[event.GetSelection()] def OnClipRegion(self, event): self.parent.clip_to_region = event.IsChecked() def OnExtension(self, event): self.parent.extension = self.ext_txt.GetValue() def UpdateSettings(self): global src_map global tgt_map global maptype layers = None UserSettings.Set( group="gcpman", key="rms", subkey="highestonly", value=self.highlighthighest.GetValue(), ) if self.sdfactor > 0: UserSettings.Set( group="gcpman", key="rms", subkey="sdfactor", value=self.sdfactor ) self.parent.sdfactor = self.sdfactor if self.parent.rmsthresh > 0: self.parent.rmsthresh = ( self.parent.rmsmean + self.parent.sdfactor * self.parent.rmssd ) UserSettings.Set( group="gcpman", key="symbol", subkey="color", value=tuple(wx.FindWindowById(self.symbol["color"]).GetColour()), ) UserSettings.Set( group="gcpman", key="symbol", subkey="hcolor", value=tuple(wx.FindWindowById(self.symbol["hcolor"]).GetColour()), ) UserSettings.Set( group="gcpman", key="symbol", subkey="scolor", value=tuple(wx.FindWindowById(self.symbol["scolor"]).GetColour()), ) UserSettings.Set( group="gcpman", key="symbol", subkey="ucolor", value=tuple(wx.FindWindowById(self.symbol["ucolor"]).GetColour()), ) UserSettings.Set( group="gcpman", key="symbol", subkey="unused", value=self.showunused.GetValue(), ) UserSettings.Set( group="gcpman", key="symbol", subkey="size", value=wx.FindWindowById(self.symbol["size"]).GetValue(), ) UserSettings.Set( group="gcpman", key="symbol", subkey="width", value=wx.FindWindowById(self.symbol["width"]).GetValue(), ) srcrender = False srcrenderVector = False tgtrender = False tgtrenderVector = False reload_target = False if self.new_src_map != src_map: # remove old layer layers = self.parent.grwiz.SrcMap.GetListOfLayers() self.parent.grwiz.SrcMap.DeleteLayer(layers[0]) src_map = self.new_src_map if maptype == "raster": cmdlist = ["d.rast", "map=%s" % src_map] srcrender = True else: cmdlist = ["d.vect", "map=%s" % src_map] srcrenderVector = True self.parent.grwiz.SwitchEnv("source") name, found = utils.GetLayerNameFromCmd(cmdlist) self.parent.grwiz.SrcMap.AddLayer( ltype=maptype, command=cmdlist, active=True, name=name, hidden=False, opacity=1.0, render=False, ) self.parent.grwiz.SwitchEnv("target") if ( self.new_tgt_map["raster"] != tgt_map["raster"] or self.new_tgt_map["vector"] != tgt_map["vector"] ): # remove all layers layers = self.parent.grwiz.TgtMap.GetListOfLayers() while layers: self.parent.grwiz.TgtMap.DeleteLayer(layers[0]) del layers[0] layers = self.parent.grwiz.TgtMap.GetListOfLayers() # self.parent.grwiz.TgtMap.DeleteAllLayers() reload_target = True tgt_map["raster"] = self.new_tgt_map["raster"] tgt_map["vector"] = self.new_tgt_map["vector"] if tgt_map["raster"] != "": cmdlist = ["d.rast", "map=%s" % tgt_map["raster"]] name, found = utils.GetLayerNameFromCmd(cmdlist) self.parent.grwiz.TgtMap.AddLayer( ltype="raster", command=cmdlist, active=True, name=name, hidden=False, opacity=1.0, render=False, ) tgtrender = True if tgt_map["vector"] != "": cmdlist = ["d.vect", "map=%s" % tgt_map["vector"]] name, found = utils.GetLayerNameFromCmd(cmdlist) self.parent.grwiz.TgtMap.AddLayer( ltype="vector", command=cmdlist, active=True, name=name, hidden=False, opacity=1.0, render=False, ) tgtrenderVector = True if tgt_map["raster"] == "" and tgt_map["vector"] == "": if self.parent.show_target: self.parent.show_target = False self.parent._mgr.GetPane("target").Hide() self.parent._mgr.Update() self.parent.activemap.SetSelection(0) self.parent.activemap.Enable(False) self.parent.GetMapToolbar().Enable("zoommenu", enable=False) else: if not self.parent.show_target: self.parent.show_target = True self.parent._mgr.GetPane("target").Show() self.parent._mgr.Update() self.parent.activemap.SetSelection(0) self.parent.activemap.Enable(True) self.parent.GetMapToolbar().Enable("zoommenu", enable=True) self.parent.TgtMapWindow.ZoomToMap( layers=self.parent.TgtMap.GetListOfLayers() ) self.parent.UpdateColours( srcrender, srcrenderVector, tgtrender, tgtrenderVector ) self.parent.SetSettings() def OnSave(self, event): """Button 'Save' pressed""" self.UpdateSettings() fileSettings = {} UserSettings.ReadSettingsFile(settings=fileSettings) fileSettings["gcpman"] = UserSettings.Get(group="gcpman") file = UserSettings.SaveToFile(fileSettings) self.parent._giface.WriteLog( _("GCP Manager settings saved to file '%s'.") % file ) # self.Close() def OnApply(self, event): """Button 'Apply' pressed""" self.UpdateSettings() # self.Close() def OnClose(self, event): """Button 'Cancel' pressed""" self.Close()