""" @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::GCP - 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 from wx.adv import Wizard else: from wx import wizard as wiz from wx.wizard import Wizard 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 core.gcmd import RunCommand, GMessage, GError, GWarning from core.settings import UserSettings from gcp.mapdisplay import MapFrame 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 TitledPage 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 success == False: 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 != False: # 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 # self.gcpmgr = GCP(self.parent, giface=self._giface, grwiz=self, size=globalvar.MAP_WINDOW_SIZE, toolbars=["gcpdisp"], Map=self.SrcMap, lmgr=self.parent) # load GCPs self.gcpmgr.InitMapDisplay() self.gcpmgr.CenterOnScreen() self.gcpmgr.Show() # need to update AUI here for wingrass self.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 GCP(MapFrame, 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") MapFrame.__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.Bind(wx.EVT_CLOSE, self.OnQuit) 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 == True: 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="%s. %s%s" % (_("source mapwin not defined"), os.linesep, err)) if not targetMapWin: GError(parent=self, message="%s. %s%s" % (_("target mapwin not defined"), os.linesep, err)) 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/MapFrame? # 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 self.CheckGCPcount(msg=True) == False: 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 == True: 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 self.CheckGCPcount(msg=True) == False: 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 self.CheckGCPcount(msg=True) == False: 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(MapFrame, 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 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 == True): 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 == True: 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 self.parent.show_target == False: 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()