1""" 2@package photo2image.ip2i_manager 3 4@brief Scanning distortion correction of a photo for GRASS GIS. 5Includes ground control point management and interactive point 6and click GCP creation 7 8Classes: 9 - ip2i_manager::GCPWizard 10 - ip2i_manager::GCP 11 - ip2i_manager::GCPList 12 - ip2i_manager::EditGCP 13 - ip2i_manager::GrSettingsDialog 14 15(C) 2006-2017 by the GRASS Development Team 16 17This program is free software under the GNU General Public License 18(>=v2). Read the file COPYING that comes with GRASS for details. 19 20@author Original author Michael Barton 21@author Original version improved by Martin Landa <landa.martin gmail.com> 22@author Rewritten by Markus Metz redesign georectfier -> GCP Manage 23@author Support for GraphicsSet added by Stepan Turek <stepan.turek seznam.cz> (2012) 24@author Yann modified: graphical replacement of i.photo.2image (was in v6 using Vask lib) 25""" 26 27from __future__ import print_function 28 29import os 30import sys 31import six 32import shutil 33from copy import copy 34 35import wx 36from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin 37import wx.lib.colourselect as csel 38 39import grass.script as grass 40 41from core import utils, globalvar 42from core.render import Map 43from gui_core.gselect import Select, LocationSelect, MapsetSelect 44from gui_core.dialogs import GroupDialog 45from core.gcmd import RunCommand, GMessage, GError, GWarning 46from core.settings import UserSettings 47from photo2image.ip2i_mapdisplay import MapFrame 48from core.giface import Notification 49from gui_core.wrap import SpinCtrl, Button, StaticText, StaticBox, \ 50 TextCtrl, Menu, ListCtrl, BitmapFromImage, CheckListCtrlMixin 51 52from location_wizard.wizard import TitledPage as TitledPage 53 54# 55# global variables 56# 57global src_map 58global tgt_map 59global maptype 60 61src_map = '' 62tgt_map = '' 63maptype = 'raster' 64 65 66def getSmallUpArrowImage(): 67 stream = open(os.path.join(globalvar.IMGDIR, 'small_up_arrow.png'), 'rb') 68 try: 69 img = wx.Image(stream) 70 finally: 71 stream.close() 72 return img 73 74 75def getSmallDnArrowImage(): 76 stream = open(os.path.join(globalvar.IMGDIR, 'small_down_arrow.png'), 'rb') 77 try: 78 img = wx.Image(stream) 79 finally: 80 stream.close() 81 stream.close() 82 return img 83 84 85class GCPWizard(object): 86 """ 87 Not a wizard anymore 88 """ 89 90 def __init__(self, parent, giface, group, raster, raster1, camera, order, extension): 91 global maptype 92 global src_map 93 global tgt_map 94 maptype = 'raster' 95 rendertype = 'raster' 96 self.parent = parent # GMFrame 97 self._giface = giface 98 self.group = group 99 self.src_map = raster 100 self.tgt_map = raster1 101 self.camera = camera 102 self.order = int(order) 103 self.extension = extension 104 self.src_maps=self.src_map 105 106 # 107 # get environmental variables 108 # 109 self.grassdatabase = grass.gisenv()['GISDBASE'] 110 111 # 112 # read original environment settings 113 # 114 self.target_gisrc = os.environ['GISRC'] 115 self.source_gisrc = os.environ['GISRC'] 116 self.gisrc_dict = {} 117 try: 118 f = open(self.target_gisrc, 'r') 119 for line in f.readlines(): 120 line = line.replace('\n', '').strip() 121 if len(line) < 1: 122 continue 123 key, value = line.split(':', 1) 124 self.gisrc_dict[key.strip()] = value.strip() 125 finally: 126 f.close() 127 128 self.currentlocation = self.gisrc_dict['LOCATION_NAME'] 129 self.currentmapset = self.gisrc_dict['MAPSET'] 130 # location for xy map to georectify 131 self.newlocation = self.currentlocation 132 self.xylocation = self.currentlocation 133 # mapset for xy map to georectify 134 self.newmapset = self.currentmapset 135 self.xymapset = self.currentmapset 136 # get group name from command line 137 self.xygroup = self.group 138 139 # GISRC file for source location/mapset of map(s) to georectify 140 self.SetSrcEnv(self.currentlocation,self.currentmapset) 141 142 # 143 # start GCP display 144 # 145 # instance of render.Map to be associated with display 146 self.SwitchEnv('source') 147 self.SrcMap = Map(gisrc=self.source_gisrc) 148 self.SwitchEnv('target') 149 self.TgtMap = Map(gisrc=self.target_gisrc) 150 self.Map = self.SrcMap 151 152 # 153 # add layer to source map 154 # 155 try: 156 # set computational region to match selected map and zoom display 157 # to region 158 p = RunCommand('g.region', 'raster='+self.src_map) 159 160 if p.returncode == 0: 161 print('returncode = ', str(p.returncode)) 162 self.Map.region = self.Map.GetRegion() 163 except: 164 pass 165 166 self.SwitchEnv('source') 167 cmdlist = ['d.rast', 'map=%s' % self.src_map] 168 name, found = utils.GetLayerNameFromCmd(cmdlist) 169 self.SrcMap.AddLayer( 170 ltype=rendertype, 171 command=cmdlist, 172 active=True, 173 name=name, 174 hidden=False, 175 opacity=1.0, 176 render=False) 177 178 # 179 # add raster layer to target map 180 # 181 self.SwitchEnv('target') 182 cmdlist = ['d.rast', 'map=%s' % self.tgt_map] 183 name, found = utils.GetLayerNameFromCmd(cmdlist) 184 self.TgtMap.AddLayer( 185 ltype=rendertype, 186 command=cmdlist, 187 active=True, 188 name=name, 189 hidden=False, 190 opacity=1.0, 191 render=False) 192 193 # 194 # start GCP Manager 195 # 196 self.gcpmgr = GCP(self.parent, giface=self._giface, 197 grwiz=self, size=globalvar.MAP_WINDOW_SIZE, 198 toolbars=["gcpdisp"], 199 Map=self.SrcMap, lmgr=self.parent, camera=camera) 200 201 # load GCPs 202 self.gcpmgr.InitMapDisplay() 203 self.gcpmgr.CenterOnScreen() 204 self.gcpmgr.Show() 205 # need to update AUI here for wingrass 206 self.gcpmgr._mgr.Update() 207 self.SwitchEnv('target') 208 209 def SetSrcEnv(self, location, mapset): 210 """Create environment to use for location and mapset 211 that are the source of the file(s) to georectify 212 213 :param location: source location 214 :param mapset: source mapset 215 216 :return: False on error 217 :return: True on success 218 """ 219 220 self.newlocation = location 221 self.newmapset = mapset 222 223 # check to see if we are georectifying map in current working 224 # location/mapset 225 if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset: 226 return False 227 228 self.gisrc_dict['LOCATION_NAME'] = location 229 self.gisrc_dict['MAPSET'] = mapset 230 231 self.source_gisrc = utils.GetTempfile() 232 233 try: 234 f = open(self.source_gisrc, mode='w') 235 for line in self.gisrc_dict.items(): 236 f.write(line[0] + ": " + line[1] + "\n") 237 finally: 238 f.close() 239 240 return True 241 242 def SwitchEnv(self, grc): 243 """ 244 Switches between original working location/mapset and 245 location/mapset that is source of file(s) to georectify 246 """ 247 # check to see if we are georectifying map in current working 248 # location/mapset 249 if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset: 250 return False 251 252 if grc == 'target': 253 os.environ['GISRC'] = str(self.target_gisrc) 254 elif grc == 'source': 255 os.environ['GISRC'] = str(self.source_gisrc) 256 257 return True 258 259 def OnGLMFocus(self, event): 260 """Layer Manager focus""" 261 # self.SwitchEnv('target') 262 263 event.Skip() 264 265class GCP(MapFrame, ColumnSorterMixin): 266 """ 267 Manages ground control points for georectifying. Calculates RMS statistics. 268 Calls i.rectify or v.rectify to georectify map. 269 """ 270 271 def __init__(self, parent, giface, grwiz=None, id=wx.ID_ANY, 272 title=_("Manage Location of Fiducial Points on a Scanned Photo"), 273 size=(700, 300), toolbars=["gcpdisp"], Map=None, lmgr=None, camera=None): 274 275 self.grwiz = grwiz # GR Wizard 276 self._giface = giface 277 278 if tgt_map == '': 279 self.show_target = False 280 else: 281 self.show_target = True 282 283 self.camera = camera 284 285 #wx.Frame.__init__(self, parent, id, title, size = size, name = "GCPFrame") 286 MapFrame.__init__( 287 self, 288 parent=parent, 289 giface=self._giface, 290 title=title, 291 size=size, 292 Map=Map, 293 toolbars=toolbars, 294 name='GCPMapWindow') 295 296 # init variables 297 self.parent = parent 298 299 # 300 # register data structures for drawing GCP's 301 # 302 self.pointsToDrawTgt = self.TgtMapWindow.RegisterGraphicsToDraw( 303 graphicsType="point", setStatusFunc=self.SetGCPSatus) 304 self.pointsToDrawSrc = self.SrcMapWindow.RegisterGraphicsToDraw( 305 graphicsType="point", setStatusFunc=self.SetGCPSatus) 306 307 # connect to the map windows signals 308 # used to add or edit GCP 309 self.SrcMapWindow.mouseLeftUpPointer.connect( 310 lambda x, y: 311 self._onMouseLeftUpPointer(self.SrcMapWindow, x, y)) 312 self.TgtMapWindow.mouseLeftUpPointer.connect( 313 lambda x, y: 314 self._onMouseLeftUpPointer(self.TgtMapWindow, x, y)) 315 316 # window resized 317 self.resize = False 318 319 self.grassdatabase = self.grwiz.grassdatabase 320 321 self.currentlocation = self.grwiz.currentlocation 322 self.currentmapset = self.grwiz.currentmapset 323 324 self.newlocation = self.grwiz.newlocation 325 self.newmapset = self.grwiz.newmapset 326 327 self.xylocation = self.grwiz.gisrc_dict['LOCATION_NAME'] 328 self.xymapset = self.grwiz.gisrc_dict['MAPSET'] 329 self.xygroup = self.grwiz.xygroup.split("@")[0] 330 self.src_maps = self.grwiz.src_maps 331 self.extension = self.grwiz.extension 332 self.outname = '' 333 334 self.file = { 335 'camera': os.path.join(self.grassdatabase, 336 self.xylocation, 337 self.xymapset, 338 'camera', 339 self.camera), 340 'ref_points': os.path.join(self.grassdatabase, 341 self.xylocation, 342 self.xymapset, 343 'group', 344 self.xygroup, 345 'REF_POINTS'), 346 'points': os.path.join(self.grassdatabase, 347 self.xylocation, 348 self.xymapset, 349 'group', 350 self.xygroup, 351 'POINTS'), 352 'points_bak': os.path.join(self.grassdatabase, 353 self.xylocation, 354 self.xymapset, 355 'group', 356 self.xygroup, 357 'POINTS_BAK'), 358 'rgrp': os.path.join(self.grassdatabase, 359 self.xylocation, 360 self.xymapset, 361 'group', 362 self.xygroup, 363 'REF'), 364 'target': os.path.join(self.grassdatabase, 365 self.xylocation, 366 self.xymapset, 367 'group', 368 self.xygroup, 369 'TARGET'), 370 } 371 372 # make a backup of the current points file if exists 373 if os.path.exists(self.file['points']): 374 shutil.copy(self.file['points'], self.file['points_bak']) 375 shutil.copy(self.file['points'], self.file['ref_points']) 376 GMessage (_("A POINTS file exists, renaming it to POINTS_BAK")) 377 378 #"""Make a POINTS file """ 379 import re,sys 380 try: 381 fc = open(self.file['camera'], mode='r') 382 fc_count=0 383 for line in fc: 384 fc_count+=1 385 if re.search("NUM", line): 386 storeLine=fc_count 387 numberOfFiducial = int(line.split()[-1]) 388 dataFiducialX=[] 389 dataFiducialY=[] 390 fc = open(self.file['camera'], mode='r') 391 fc_count=0 392 for line in fc: 393 fc_count+=1 394 if fc_count > storeLine : 395 dataFiducialX.append(line.split()[1]) 396 dataFiducialY.append(line.split()[2]) 397 398 except IOError as err: 399 GError( 400 parent=self, 401 message="%s <%s>. %s%s" % 402 (_("Opening CAMERA file failed"), 403 self.file['camera'], 404 os.linesep, 405 err)) 406 return 407 408 self.GCPcount = 0 409 try: 410 f = open(self.file['points'], mode='w') 411 # use os.linesep or '\n' here ??? 412 f.write('# Ground Control Points File\n') 413 f.write("# \n") 414 f.write("# target location: " + self.currentlocation + '\n') 415 f.write("# target mapset: " + self.currentmapset + '\n') 416 f.write("#\tsource\t\ttarget\t\tstatus\n") 417 f.write("#\teast\tnorth\teast\tnorth\t(1=ok, 0=ignore)\n") 418 f.write( 419 "#----------------------- ----------------------- ---------------\n") 420 421 check = "0" 422 for index in range(numberOfFiducial): 423 coordX0 = "0" 424 coordY0 = "0" 425 coordX1 = dataFiducialX[index] 426 coordY1 = dataFiducialY[index] 427 f.write(coordX0 + ' ' + 428 coordY0 + ' ' + 429 coordX1 + ' ' + 430 coordY1 + ' ' + 431 check + '\n') 432 433 except IOError as err: 434 GError( 435 parent=self, 436 message="%s <%s>. %s%s" % 437 (_("Writing POINTS file failed"), 438 self.file['points'], 439 os.linesep, 440 err)) 441 return 442 443 f.close() 444 445 # polynomial order transformation for georectification 446 self.gr_order = self.grwiz.order 447 # interpolation method for georectification 448 self.gr_method = 'nearest' 449 # region clipping for georectified map 450 self.clip_to_region = False 451 # number of GCPs selected to be used for georectification (checked) 452 self.GCPcount = 0 453 # forward RMS error 454 self.fwd_rmserror = 0.0 455 # backward RMS error 456 self.bkw_rmserror = 0.0 457 # list map coords and ID of map display they came from 458 self.mapcoordlist = [] 459 self.mapcoordlist.append([0, # GCP number 460 0.0, # source east 461 0.0, # source north 462 0.0, # target east 463 0.0, # target north 464 0.0, # forward error 465 0.0]) # backward error 466 467 # init vars to highlight high RMS errors 468 self.highest_only = True 469 self.show_unused = True 470 self.highest_key = -1 471 self.rmsthresh = 0 472 self.rmsmean = 0 473 self.rmssd = 0 474 475 self.SetTarget(self.xygroup, self.currentlocation, self.currentmapset) 476 477 self.itemDataMap = None 478 479 # images for column sorting 480 # CheckListCtrlMixin must set an ImageList first 481 self.il = self.list.GetImageList(wx.IMAGE_LIST_SMALL) 482 483 SmallUpArrow = BitmapFromImage(getSmallUpArrowImage()) 484 SmallDnArrow = BitmapFromImage(getSmallDnArrowImage()) 485 self.sm_dn = self.il.Add(SmallDnArrow) 486 self.sm_up = self.il.Add(SmallUpArrow) 487 488 # set mouse characteristics 489 self.mapwin = self.SrcMapWindow 490 self.mapwin.mouse['box'] = 'point' 491 self.mapwin.mouse["use"] == "pointer" 492 self.mapwin.zoomtype = 0 493 self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID) 494 self.mapwin.SetNamedCursor('cross') 495 496 self.mapwin = self.TgtMapWindow 497 498 # set mouse characteristics 499 self.mapwin.mouse['box'] = 'point' 500 self.mapwin.mouse["use"] == "pointer" 501 self.mapwin.zoomtype = 0 502 self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID) 503 self.mapwin.SetNamedCursor('cross') 504 505 # 506 # show new display & draw map 507 # 508 if self.show_target: 509 self.MapWindow = self.TgtMapWindow 510 self.Map = self.TgtMap 511 self.OnZoomToMap(None) 512 513 self.MapWindow = self.SrcMapWindow 514 self.Map = self.SrcMap 515 self.OnZoomToMap(None) 516 517 # 518 # bindings 519 # 520 self.Bind(wx.EVT_ACTIVATE, self.OnFocus) 521 self.Bind(wx.EVT_SIZE, self.OnSize) 522 self.Bind(wx.EVT_IDLE, self.OnIdle) 523 self.Bind(wx.EVT_CLOSE, self.OnQuit) 524 525 self.SetSettings() 526 527 def __del__(self): 528 """Disable GCP manager mode""" 529 # leaving the method here but was used only to delete gcpmanagement 530 # from layer manager which is now not needed 531 pass 532 533 def CreateGCPList(self): 534 """Create GCP List Control""" 535 536 return GCPList(parent=self, gcp=self) 537 538 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py 539 def GetListCtrl(self): 540 return self.list 541 542 def GetMapCoordList(self): 543 return self.mapcoordlist 544 545 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py 546 def GetSortImages(self): 547 return (self.sm_dn, self.sm_up) 548 549 def GetFwdError(self): 550 return self.fwd_rmserror 551 552 def GetBkwError(self): 553 return self.bkw_rmserror 554 555 def InitMapDisplay(self): 556 self.list.LoadData() 557 558 # initialize column sorter 559 self.itemDataMap = self.mapcoordlist 560 ncols = self.list.GetColumnCount() 561 ColumnSorterMixin.__init__(self, ncols) 562 # init to ascending sort on first click 563 self._colSortFlag = [1] * ncols 564 565 def SetTarget(self, tgroup, tlocation, tmapset): 566 """ 567 Sets rectification target to current location and mapset 568 """ 569 # check to see if we are georectifying map in current working 570 # location/mapset 571 if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset: 572 RunCommand('i.target', 573 parent=self, 574 flags='c', 575 group=tgroup) 576 else: 577 self.grwiz.SwitchEnv('source') 578 RunCommand('i.target', 579 parent=self, 580 group=tgroup, 581 location=tlocation, 582 mapset=tmapset) 583 self.grwiz.SwitchEnv('target') 584 585 def AddGCP(self, event): 586 """ 587 Appends an item to GCP list 588 """ 589 keyval = self.list.AddGCPItem() + 1 590 # source east, source north, target east, target north, forward error, 591 # backward error 592 self.mapcoordlist.append([keyval, # GCP number 593 0.0, # source east 594 0.0, # source north 595 0.0, # target east 596 0.0, # target north 597 0.0, # forward error 598 0.0]) # backward error 599 600 if self.statusbarManager.GetMode() == 8: # go to 601 self.StatusbarUpdate() 602 603 def DeleteGCP(self, event): 604 """ 605 Deletes selected item in GCP list 606 """ 607 minNumOfItems = self.OnGROrder(None) 608 609 if self.list.GetItemCount() <= minNumOfItems: 610 GMessage( 611 parent=self, 612 message=_("At least %d GCPs required. Operation canceled.") % 613 minNumOfItems) 614 return 615 616 key = self.list.DeleteGCPItem() 617 del self.mapcoordlist[key] 618 619 # update key and GCP number 620 for newkey in range(key, len(self.mapcoordlist)): 621 index = self.list.FindItem(-1, newkey + 1) 622 self.mapcoordlist[newkey][0] = newkey 623 self.list.SetItem(index, 0, str(newkey)) 624 self.list.SetItemData(index, newkey) 625 626 # update selected 627 if self.list.GetItemCount() > 0: 628 if self.list.selected < self.list.GetItemCount(): 629 self.list.selectedkey = self.list.GetItemData( 630 self.list.selected) 631 else: 632 self.list.selected = self.list.GetItemCount() - 1 633 self.list.selectedkey = self.list.GetItemData( 634 self.list.selected) 635 636 self.list.SetItemState(self.list.selected, 637 wx.LIST_STATE_SELECTED, 638 wx.LIST_STATE_SELECTED) 639 else: 640 self.list.selected = wx.NOT_FOUND 641 self.list.selectedkey = -1 642 643 self.UpdateColours() 644 645 if self.statusbarManager.GetMode() == 8: # go to 646 self.StatusbarUpdate() 647 if self.list.selectedkey > 0: 648 self.statusbarManager.SetProperty( 649 'gotoGCP', self.list.selectedkey) 650 651 def ClearGCP(self, event): 652 """ 653 Clears all values in selected item of GCP list and unchecks it 654 """ 655 index = self.list.GetSelected() 656 key = self.list.GetItemData(index) 657 658 for i in range(1, 5): 659 self.list.SetItem(index, i, '0.0') 660 self.list.SetItem(index, 5, '') 661 self.list.SetItem(index, 6, '') 662 self.list.CheckItem(index, False) 663 664 # GCP number, source E, source N, target E, target N, fwd error, bkwd 665 # error 666 self.mapcoordlist[key] = [key, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 667 668 def SetSettings(self): 669 """Sets settings for drawing of GCP's. 670 """ 671 self.highest_only = UserSettings.Get( 672 group='gcpman', key='rms', subkey='highestonly') 673 self.show_unused = UserSettings.Get( 674 group='gcpman', key='symbol', subkey='unused') 675 676 colours = {"color": "default", 677 "hcolor": "highest", 678 "scolor": "selected", 679 "ucolor": "unused"} 680 wpx = UserSettings.Get(group='gcpman', key='symbol', subkey='width') 681 682 for k, v in six.iteritems(colours): 683 col = UserSettings.Get(group='gcpman', key='symbol', subkey=k) 684 self.pointsToDrawSrc.GetPen(v).SetColour(wx.Colour( 685 col[0], col[1], col[2], 255)) # TODO GetPen neni to spatne? 686 self.pointsToDrawTgt.GetPen(v).SetColour( 687 wx.Colour(col[0], col[1], col[2], 255)) 688 689 self.pointsToDrawSrc.GetPen(v).SetWidth(wpx) 690 self.pointsToDrawTgt.GetPen(v).SetWidth(wpx) 691 692 spx = UserSettings.Get(group='gcpman', key='symbol', subkey='size') 693 self.pointsToDrawSrc.SetPropertyVal("size", int(spx)) 694 self.pointsToDrawTgt.SetPropertyVal("size", int(spx)) 695 696 font = self.GetFont() 697 font.SetPointSize(int(spx) + 2) 698 699 textProp = {} 700 textProp['active'] = True 701 textProp['font'] = font 702 self.pointsToDrawSrc.SetPropertyVal("text", textProp) 703 self.pointsToDrawTgt.SetPropertyVal("text", copy(textProp)) 704 705 def SetGCPSatus(self, item, itemIndex): 706 """Before GCP is drawn, decides it's colour and whether it 707 will be drawed. 708 """ 709 key = self.list.GetItemData(itemIndex) 710 # incremented because of itemDataMap (has one more item) - will be 711 # changed 712 itemIndex += 1 713 714 if not self.list.IsItemChecked(key - 1): 715 wxPen = "unused" 716 if not self.show_unused: 717 item.SetPropertyVal('hide', True) 718 else: 719 item.SetPropertyVal('hide', False) 720 721 else: 722 item.SetPropertyVal('hide', False) 723 if self.highest_only == True: 724 if itemIndex == self.highest_key: 725 wxPen = "highest" 726 else: 727 wxPen = "default" 728 else: 729 if (self.mapcoordlist[key][5] > self.rmsthresh): 730 wxPen = "highest" 731 else: 732 wxPen = "default" 733 734 if itemIndex == self.list.selectedkey: 735 wxPen = "selected" 736 737 item.SetPropertyVal('label', str(itemIndex)) 738 item.SetPropertyVal('penName', wxPen) 739 740 def SetGCPData(self, coordtype, coord, mapdisp=None, confirm=False): 741 """Inserts coordinates from file, mouse click on map, or 742 after editing into selected item of GCP list and checks it for 743 use. 744 """ 745 index = self.list.GetSelected() 746 if index == wx.NOT_FOUND: 747 return 748 749 coord0 = coord[0] 750 coord1 = coord[1] 751 752 key = self.list.GetItemData(index) 753 if confirm: 754 if self.MapWindow == self.SrcMapWindow: 755 currloc = _("source") 756 else: 757 currloc = _("target") 758 ret = wx.MessageBox( 759 parent=self, caption=_("Set GCP coordinates"), 760 message=_( 761 'Set %(coor)s coordinates for GCP No. %(key)s? \n\n' 762 'East: %(coor0)s \n' 763 'North: %(coor1)s') % 764 {'coor': currloc, 'key': str(key), 765 'coor0': str(coord0), 766 'coor1': str(coord1)}, 767 style=wx.ICON_QUESTION | wx.YES_NO | wx.CENTRE) 768 769 # for wingrass 770 if os.name == 'nt': 771 self.MapWindow.SetFocus() 772 if ret == wx.NO: 773 return 774 775 if coordtype == 'source': 776 self.list.SetItem(index, 1, str(coord0)) 777 self.list.SetItem(index, 2, str(coord1)) 778 self.mapcoordlist[key][1] = coord[0] 779 self.mapcoordlist[key][2] = coord[1] 780 self.pointsToDrawSrc.GetItem(key - 1).SetCoords([coord0, coord1]) 781 782 elif coordtype == 'target': 783 self.list.SetItem(index, 3, str(coord0)) 784 self.list.SetItem(index, 4, str(coord1)) 785 self.mapcoordlist[key][3] = coord[0] 786 self.mapcoordlist[key][4] = coord[1] 787 self.pointsToDrawTgt.GetItem(key - 1).SetCoords([coord0, coord1]) 788 789 self.list.SetItem(index, 5, '0') 790 self.list.SetItem(index, 6, '0') 791 self.mapcoordlist[key][5] = 0.0 792 self.mapcoordlist[key][6] = 0.0 793 794 # self.list.ResizeColumns() 795 796 def SaveGCPs(self, event): 797 """Make a POINTS file or save GCP coordinates to existing 798 POINTS file 799 """ 800 self.GCPcount = 0 801 try: 802 f = open(self.file['points'], mode='w') 803 # use os.linesep or '\n' here ??? 804 f.write('# Ground Control Points File\n') 805 f.write("# \n") 806 f.write("# target location: " + self.currentlocation + '\n') 807 f.write("# target mapset: " + self.currentmapset + '\n') 808 f.write("#\tsource\t\ttarget\t\tstatus\n") 809 f.write("#\teast\tnorth\teast\tnorth\t(1=ok, 0=ignore)\n") 810 f.write( 811 "#----------------------- ----------------------- ---------------\n") 812 813 for index in range(self.list.GetItemCount()): 814 if self.list.IsItemChecked(index): 815 check = "1" 816 self.GCPcount += 1 817 else: 818 check = "0" 819 coord0 = self.list.GetItem(index, 1).GetText() 820 coord1 = self.list.GetItem(index, 2).GetText() 821 coord2 = self.list.GetItem(index, 3).GetText() 822 coord3 = self.list.GetItem(index, 4).GetText() 823 f.write( 824 coord0 + 825 ' ' + 826 coord1 + 827 ' ' + 828 coord2 + 829 ' ' + 830 coord3 + 831 ' ' + 832 check + 833 '\n') 834 835 except IOError as err: 836 GError( 837 parent=self, 838 message="%s <%s>. %s%s" % 839 (_("Writing POINTS file failed"), 840 self.file['points'], 841 os.linesep, 842 err)) 843 return 844 845 f.close() 846 847 # if event != None save also to backup file 848 if event: 849 shutil.copy(self.file['points'], self.file['points_bak']) 850 shutil.copy(self.file['points'], self.file['ref_points']) 851 self._giface.WriteLog( 852 _('POINTS file saved for group <%s>') % 853 self.xygroup) 854 #self.SetStatusText(_('POINTS file saved')) 855 856 def ReadGCPs(self): 857 """ 858 Reads GCPs and georectified coordinates from POINTS file 859 """ 860 861 self.GCPcount = 0 862 863 sourceMapWin = self.SrcMapWindow 864 targetMapWin = self.TgtMapWindow 865 866 if not sourceMapWin: 867 GError(parent=self, 868 message="%s. %s%s" % (_("source mapwin not defined"), 869 os.linesep, err)) 870 871 if not targetMapWin: 872 GError(parent=self, 873 message="%s. %s%s" % (_("target mapwin not defined"), 874 os.linesep, err)) 875 876 try: 877 f = open(self.file['points'], 'r') 878 GCPcnt = 0 879 880 for line in f.readlines(): 881 if line[0] == '#' or line == '': 882 continue 883 line = line.replace('\n', '').strip() 884 coords = list(map(float, line.split())) 885 if coords[4] == 1: 886 check = True 887 self.GCPcount += 1 888 else: 889 check = False 890 891 self.AddGCP(event=None) 892 self.SetGCPData('source', (coords[0], coords[1]), sourceMapWin) 893 self.SetGCPData('target', (coords[2], coords[3]), targetMapWin) 894 index = self.list.GetSelected() 895 if index != wx.NOT_FOUND: 896 self.list.CheckItem(index, check) 897 GCPcnt += 1 898 899 except IOError as err: 900 GError( 901 parent=self, 902 message="%s <%s>. %s%s" % 903 (_("Reading POINTS file failed"), 904 self.file['points'], 905 os.linesep, 906 err)) 907 return 908 909 f.close() 910 911 if GCPcnt == 0: 912 # 3 gcp is minimum 913 for i in range(3): 914 self.AddGCP(None) 915 916 if self.CheckGCPcount(): 917 # calculate RMS 918 self.RMSError(self.xygroup, self.gr_order) 919 920 def ReloadGCPs(self, event): 921 """Reload data from file""" 922 923 # use backup 924 shutil.copy(self.file['points_bak'], self.file['points']) 925 926 # delete all items in mapcoordlist 927 self.mapcoordlist = [] 928 self.mapcoordlist.append([0, # GCP number 929 0.0, # source east 930 0.0, # source north 931 0.0, # target east 932 0.0, # target north 933 0.0, # forward error 934 0.0]) # backward error 935 936 self.list.LoadData() 937 self.itemDataMap = self.mapcoordlist 938 939 if self._col != -1: 940 self.list.ClearColumnImage(self._col) 941 self._colSortFlag = [1] * self.list.GetColumnCount() 942 943 # draw GCPs (source and target) 944 sourceMapWin = self.SrcMapWindow 945 sourceMapWin.UpdateMap(render=False) 946 if self.show_target: 947 targetMapWin = self.TgtMapWindow 948 targetMapWin.UpdateMap(render=False) 949 950 def OnFocus(self, event): 951 # TODO: it is here just to remove old or obsolate beavior of base class gcp/MapFrame? 952 # self.grwiz.SwitchEnv('source') 953 pass 954 955 def _onMouseLeftUpPointer(self, mapWindow, x, y): 956 if mapWindow == self.SrcMapWindow: 957 coordtype = 'source' 958 else: 959 coordtype = 'target' 960 961 coord = (x, y) 962 self.SetGCPData(coordtype, coord, self, confirm=True) 963 mapWindow.UpdateMap(render=False) 964 965 def OnRMS(self, event): 966 """ 967 RMS button handler 968 """ 969 self.RMSError(self.xygroup, self.gr_order) 970 971 sourceMapWin = self.SrcMapWindow 972 sourceMapWin.UpdateMap(render=False) 973 if self.show_target: 974 targetMapWin = self.TgtMapWindow 975 targetMapWin.UpdateMap(render=False) 976 977 def CheckGCPcount(self, msg=False): 978 """ 979 Checks to make sure that the minimum number of GCPs have been defined and 980 are active for the selected transformation order 981 """ 982 if (self.GCPcount < 3 and self.gr_order == 1) or \ 983 (self.GCPcount < 6 and self.gr_order == 2) or \ 984 (self.GCPcount < 10 and self.gr_order == 3): 985 if msg: 986 GWarning( 987 parent=self, message=_( 988 'Insufficient points defined and active (checked) ' 989 'for selected rectification method (order: %d).\n' 990 '3+ points needed for 1st order,\n' 991 '6+ points for 2nd order, and\n' 992 '10+ points for 3rd order.') % 993 self.gr_order) 994 return False 995 else: 996 return True 997 998 def OnGeorect(self, event): 999 """ 1000 Georectifies map(s) in group using i.rectify 1001 """ 1002 global maptype 1003 self.SaveGCPs(None) 1004 1005 if self.CheckGCPcount(msg=True) == False: 1006 return 1007 1008 if maptype == 'raster': 1009 self.grwiz.SwitchEnv('source') 1010 1011 if self.clip_to_region: 1012 flags = "ac" 1013 else: 1014 flags = "a" 1015 1016 busy = wx.BusyInfo(_("Rectifying images, please wait..."), 1017 parent=self) 1018 wx.GetApp().Yield() 1019 1020 ret, msg = RunCommand('i.rectify', 1021 parent=self, 1022 getErrorMsg=True, 1023 quiet=True, 1024 group=self.xygroup, 1025 extension=self.extension, 1026 order=self.gr_order, 1027 method=self.gr_method, 1028 flags=flags) 1029 1030 del busy 1031 1032 # provide feedback on failure 1033 if ret != 0: 1034 print('ip2i: Error in i.rectify', file=sys.stderr) 1035 print(self.grwiz.src_map, file=sys.stderr) 1036 print(msg, file=sys.stderr) 1037 1038 busy = wx.BusyInfo(_("Writing output image to group, please wait..."), 1039 parent=self) 1040 wx.GetApp().Yield() 1041 1042 ret1, msg1 = RunCommand('i.group', 1043 parent=self, 1044 getErrorMsg=True, 1045 quiet=False, 1046 group=self.xygroup, 1047 input=''.join([self.grwiz.src_map.split('@')[0],self.extension])) 1048 1049 del busy 1050 1051 if ret1 != 0: 1052 print('ip2i: Error in i.group', file=sys.stderr) 1053 print(self.grwiz.src_map.split('@')[0], file=sys.stderr) 1054 print(self.extension, file=sys.stderr) 1055 print(msg1, file=sys.stderr) 1056 1057 self.grwiz.SwitchEnv('target') 1058 1059 def OnGeorectDone(self, **kargs): 1060 """Print final message""" 1061 global maptype 1062 if maptype == 'raster': 1063 return 1064 1065 def OnSettings(self, event): 1066 """GCP Manager settings""" 1067 dlg = GrSettingsDialog(parent=self, giface=self._giface, 1068 id=wx.ID_ANY, title=_('GCP Manager settings')) 1069 1070 if dlg.ShowModal() == wx.ID_OK: 1071 pass 1072 1073 dlg.Destroy() 1074 1075 def UpdateColours(self, srcrender=False, 1076 tgtrender=False): 1077 """update colours""" 1078 highest_fwd_err = 0.0 1079 self.highest_key = 0 1080 highest_idx = 0 1081 1082 for index in range(self.list.GetItemCount()): 1083 if self.list.IsItemChecked(index): 1084 key = self.list.GetItemData(index) 1085 fwd_err = self.mapcoordlist[key][5] 1086 1087 if self.highest_only == True: 1088 self.list.SetItemTextColour(index, wx.BLACK) 1089 if highest_fwd_err < fwd_err: 1090 highest_fwd_err = fwd_err 1091 self.highest_key = key 1092 highest_idx = index 1093 elif self.rmsthresh > 0: 1094 if (fwd_err > self.rmsthresh): 1095 self.list.SetItemTextColour(index, wx.RED) 1096 else: 1097 self.list.SetItemTextColour(index, wx.BLACK) 1098 else: 1099 self.list.SetItemTextColour(index, wx.BLACK) 1100 1101 if self.highest_only and highest_fwd_err > 0.0: 1102 self.list.SetItemTextColour(highest_idx, wx.RED) 1103 1104 sourceMapWin = self.SrcMapWindow 1105 sourceMapWin.UpdateMap(render=srcrender) 1106 if self.show_target: 1107 targetMapWin = self.TgtMapWindow 1108 targetMapWin.UpdateMap(render=tgtrender) 1109 1110 def OnQuit(self, event): 1111 """Quit georectifier""" 1112 ret = wx.MessageBox( 1113 parent=self, caption=_("Quit GCP Manager"), 1114 message=_('Save ground control points?'), 1115 style=wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.CENTRE) 1116 1117 if ret != wx.CANCEL: 1118 if ret == wx.YES: 1119 self.SaveGCPs(None) 1120 elif ret == wx.NO: 1121 # restore POINTS file from backup 1122 if os.path.exists(self.file['points_bak']): 1123 shutil.copy(self.file['points_bak'], self.file['points']) 1124 shutil.copy(self.file['points_bak'], self.file['ref_points']) 1125 1126 if os.path.exists(self.file['points_bak']): 1127 os.unlink(self.file['points_bak']) 1128 1129 self.SrcMap.Clean() 1130 self.TgtMap.Clean() 1131 1132 self.Destroy() 1133 1134 # event.Skip() 1135 1136 def OnGROrder(self, event): 1137 """ 1138 sets transformation order for georectifying 1139 """ 1140 if event: 1141 self.gr_order = event.GetInt() + 1 1142 1143 numOfItems = self.list.GetItemCount() 1144 minNumOfItems = numOfItems 1145 1146 if self.gr_order == 1: 1147 minNumOfItems = 3 1148 # self.SetStatusText(_('Insufficient points, 3+ points needed for 1st order')) 1149 1150 elif self.gr_order == 2: 1151 minNumOfItems = 6 1152 diff = 6 - numOfItems 1153 # self.SetStatusText(_('Insufficient points, 6+ points needed for 2nd order')) 1154 1155 elif self.gr_order == 3: 1156 minNumOfItems = 10 1157 # self.SetStatusText(_('Insufficient points, 10+ points needed for 3rd order')) 1158 1159 for i in range(minNumOfItems - numOfItems): 1160 self.AddGCP(None) 1161 1162 return minNumOfItems 1163 1164 def RMSError(self, xygroup, order): 1165 """ 1166 Uses m.transform to calculate forward and backward error for each used GCP 1167 in POINTS file and insert error values into GCP list. 1168 Calculates total forward and backward RMS error for all used points 1169 """ 1170 # save GCPs to points file to make sure that all checked GCPs are used 1171 self.SaveGCPs(None) 1172 # self.SetStatusText('') 1173 1174 if self.CheckGCPcount(msg=True) == False: 1175 return 1176 1177 # get list of forward and reverse rms error values for each point 1178 self.grwiz.SwitchEnv('source') 1179 1180 ret = RunCommand('m.transform', 1181 parent=self, 1182 read=True, 1183 group=xygroup, 1184 order=order) 1185 1186 self.grwiz.SwitchEnv('target') 1187 1188 if ret: 1189 errlist = ret.splitlines() 1190 else: 1191 GError(parent=self, 1192 message=_('Could not calculate RMS Error.\n' 1193 'Possible error with m.transform.')) 1194 return 1195 1196 # insert error values into GCP list for checked items 1197 sdfactor = float( 1198 UserSettings.Get( 1199 group='gcpman', 1200 key='rms', 1201 subkey='sdfactor')) 1202 GCPcount = 0 1203 sumsq_fwd_err = 0.0 1204 sumsq_bkw_err = 0.0 1205 sum_fwd_err = 0.0 1206 highest_fwd_err = 0.0 1207 self.highest_key = 0 1208 highest_idx = 0 1209 1210 for index in range(self.list.GetItemCount()): 1211 key = self.list.GetItemData(index) 1212 if self.list.IsItemChecked(index): 1213 fwd_err, bkw_err = errlist[GCPcount].split() 1214 self.list.SetItem(index, 5, fwd_err) 1215 self.list.SetItem(index, 6, bkw_err) 1216 self.mapcoordlist[key][5] = float(fwd_err) 1217 self.mapcoordlist[key][6] = float(bkw_err) 1218 self.list.SetItemTextColour(index, wx.BLACK) 1219 if self.highest_only: 1220 if highest_fwd_err < float(fwd_err): 1221 highest_fwd_err = float(fwd_err) 1222 self.highest_key = key 1223 highest_idx = index 1224 1225 sumsq_fwd_err += float(fwd_err)**2 1226 sumsq_bkw_err += float(bkw_err)**2 1227 sum_fwd_err += float(fwd_err) 1228 GCPcount += 1 1229 else: 1230 self.list.SetItem(index, 5, '') 1231 self.list.SetItem(index, 6, '') 1232 self.mapcoordlist[key][5] = 0.0 1233 self.mapcoordlist[key][6] = 0.0 1234 self.list.SetItemTextColour(index, wx.BLACK) 1235 1236 # SD 1237 if GCPcount > 0: 1238 self.rmsmean = sum_fwd_err / GCPcount 1239 self.rmssd = ((sumsq_fwd_err - self.rmsmean**2)**0.5) 1240 self.rmsthresh = self.rmsmean + sdfactor * self.rmssd 1241 else: 1242 self.rmsthresh = 0 1243 self.rmsmean = 0 1244 self.rmssd = 0 1245 1246 if self.highest_only and highest_fwd_err > 0.0: 1247 self.list.SetItemTextColour(highest_idx, wx.RED) 1248 elif GCPcount > 0 and self.rmsthresh > 0 and not self.highest_only: 1249 for index in range(self.list.GetItemCount()): 1250 if self.list.IsItemChecked(index): 1251 key = self.list.GetItemData(index) 1252 if (self.mapcoordlist[key][5] > self.rmsthresh): 1253 self.list.SetItemTextColour(index, wx.RED) 1254 1255 # calculate global RMS error (geometric mean) 1256 self.fwd_rmserror = round((sumsq_fwd_err / GCPcount)**0.5, 4) 1257 self.bkw_rmserror = round((sumsq_bkw_err / GCPcount)**0.5, 4) 1258 self.list.ResizeColumns() 1259 1260 def GetNewExtent(self, region, map=None): 1261 1262 coord_file = utils.GetTempfile() 1263 newreg = {'n': 0.0, 's': 0.0, 'e': 0.0, 'w': 0.0, } 1264 1265 try: 1266 f = open(coord_file, mode='w') 1267 # NW corner 1268 f.write(str(region['e']) + " " + str(region['n']) + "\n") 1269 # NE corner 1270 f.write(str(region['e']) + " " + str(region['s']) + "\n") 1271 # SW corner 1272 f.write(str(region['w']) + " " + str(region['n']) + "\n") 1273 # SE corner 1274 f.write(str(region['w']) + " " + str(region['s']) + "\n") 1275 finally: 1276 f.close() 1277 1278 # save GCPs to points file to make sure that all checked GCPs are used 1279 self.SaveGCPs(None) 1280 1281 order = self.gr_order 1282 self.gr_order = 1 1283 1284 if self.CheckGCPcount(msg=True) == False: 1285 self.gr_order = order 1286 return 1287 1288 self.gr_order = order 1289 1290 # get list of forward and reverse rms error values for each point 1291 self.grwiz.SwitchEnv('source') 1292 1293 if map == 'source': 1294 ret = RunCommand('m.transform', 1295 parent=self, 1296 read=True, 1297 group=self.xygroup, 1298 order=1, 1299 format='dst', 1300 coords=coord_file) 1301 1302 elif map == 'target': 1303 ret = RunCommand('m.transform', 1304 parent=self, 1305 read=True, 1306 group=self.xygroup, 1307 order=1, 1308 flags='r', 1309 format='src', 1310 coords=coord_file) 1311 1312 os.unlink(coord_file) 1313 1314 self.grwiz.SwitchEnv('target') 1315 1316 if ret: 1317 errlist = ret.splitlines() 1318 else: 1319 GError(parent=self, 1320 message=_('Could not calculate new extends.\n' 1321 'Possible error with m.transform.')) 1322 return 1323 1324 # fist corner 1325 e, n = errlist[0].split() 1326 fe = float(e) 1327 fn = float(n) 1328 newreg['n'] = fn 1329 newreg['s'] = fn 1330 newreg['e'] = fe 1331 newreg['w'] = fe 1332 # other three corners 1333 for i in range(1, 4): 1334 e, n = errlist[i].split() 1335 fe = float(e) 1336 fn = float(n) 1337 if fe < newreg['w']: 1338 newreg['w'] = fe 1339 if fe > newreg['e']: 1340 newreg['e'] = fe 1341 if fn < newreg['s']: 1342 newreg['s'] = fn 1343 if fn > newreg['n']: 1344 newreg['n'] = fn 1345 1346 return newreg 1347 1348 def OnHelp(self, event): 1349 """Show GCP Manager manual page""" 1350 self._giface.Help(entry='wxGUI.gcp') 1351 1352 def OnUpdateActive(self, event): 1353 1354 if self.activemap.GetSelection() == 0: 1355 self.MapWindow = self.SrcMapWindow 1356 self.Map = self.SrcMap 1357 else: 1358 self.MapWindow = self.TgtMapWindow 1359 self.Map = self.TgtMap 1360 1361 self.UpdateActive(self.MapWindow) 1362 # for wingrass 1363 if os.name == 'nt': 1364 self.MapWindow.SetFocus() 1365 1366 def UpdateActive(self, win): 1367 1368 # optionally disable tool zoomback tool 1369 self.GetMapToolbar().Enable('zoomback', 1370 enable=(len(self.MapWindow.zoomhistory) > 1)) 1371 1372 if self.activemap.GetSelection() != (win == self.TgtMapWindow): 1373 self.activemap.SetSelection(win == self.TgtMapWindow) 1374 self.StatusbarUpdate() 1375 1376 def AdjustMap(self, newreg): 1377 """Adjust map window to new extents 1378 """ 1379 1380 # adjust map window 1381 self.Map.region['n'] = newreg['n'] 1382 self.Map.region['s'] = newreg['s'] 1383 self.Map.region['e'] = newreg['e'] 1384 self.Map.region['w'] = newreg['w'] 1385 1386 self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'], 1387 self.Map.region['e'], self.Map.region['w']) 1388 1389 # LL locations 1390 if self.Map.projinfo['proj'] == 'll': 1391 if newreg['n'] > 90.0: 1392 newreg['n'] = 90.0 1393 if newreg['s'] < -90.0: 1394 newreg['s'] = -90.0 1395 1396 ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2 1397 cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2 1398 1399 # calculate new center point and display resolution 1400 self.Map.region['center_easting'] = ce 1401 self.Map.region['center_northing'] = cn 1402 self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width 1403 self.Map.region["nsres"] = ( 1404 newreg['n'] - newreg['s']) / self.Map.height 1405 self.Map.AlignExtentFromDisplay() 1406 1407 self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'], 1408 self.Map.region['e'], self.Map.region['w']) 1409 1410 if self.MapWindow.redrawAll is False: 1411 self.MapWindow.redrawAll = True 1412 1413 self.MapWindow.UpdateMap() 1414 self.StatusbarUpdate() 1415 1416 def OnZoomToSource(self, event): 1417 """Set target map window to match extents of source map window 1418 """ 1419 1420 if not self.MapWindow == self.TgtMapWindow: 1421 self.MapWindow = self.TgtMapWindow 1422 self.Map = self.TgtMap 1423 self.UpdateActive(self.TgtMapWindow) 1424 1425 # get new N, S, E, W for target 1426 newreg = self.GetNewExtent(self.SrcMap.region, 'source') 1427 if newreg: 1428 self.AdjustMap(newreg) 1429 1430 def OnZoomToTarget(self, event): 1431 """Set source map window to match extents of target map window 1432 """ 1433 1434 if not self.MapWindow == self.SrcMapWindow: 1435 self.MapWindow = self.SrcMapWindow 1436 self.Map = self.SrcMap 1437 self.UpdateActive(self.SrcMapWindow) 1438 1439 # get new N, S, E, W for target 1440 newreg = self.GetNewExtent(self.TgtMap.region, 'target') 1441 if newreg: 1442 self.AdjustMap(newreg) 1443 1444 def OnZoomMenuGCP(self, event): 1445 """Popup Zoom menu 1446 """ 1447 point = wx.GetMousePosition() 1448 zoommenu = Menu() 1449 # Add items to the menu 1450 1451 zoomsource = wx.MenuItem(zoommenu, wx.ID_ANY, _( 1452 'Adjust source display to target display')) 1453 zoommenu.AppendItem(zoomsource) 1454 self.Bind(wx.EVT_MENU, self.OnZoomToTarget, zoomsource) 1455 1456 zoomtarget = wx.MenuItem(zoommenu, wx.ID_ANY, _( 1457 'Adjust target display to source display')) 1458 zoommenu.AppendItem(zoomtarget) 1459 self.Bind(wx.EVT_MENU, self.OnZoomToSource, zoomtarget) 1460 1461 # Popup the menu. If an item is selected then its handler 1462 # will be called before PopupMenu returns. 1463 self.PopupMenu(zoommenu) 1464 zoommenu.Destroy() 1465 1466 def OnSize(self, event): 1467 """Adjust Map Windows after GCP Map Display has been resized 1468 """ 1469 # re-render image on idle 1470 self.resize = grass.clock() 1471 super(MapFrame, self).OnSize(event) 1472 1473 def OnIdle(self, event): 1474 """GCP Map Display resized, adjust Map Windows 1475 """ 1476 if self.GetMapToolbar(): 1477 if self.resize and self.resize + 0.2 < grass.clock(): 1478 srcwidth, srcheight = self.SrcMapWindow.GetSize() 1479 tgtwidth, tgtheight = self.TgtMapWindow.GetSize() 1480 srcwidth = (srcwidth + tgtwidth) / 2 1481 if self.show_target: 1482 self._mgr.GetPane("target").Hide() 1483 self._mgr.Update() 1484 self._mgr.GetPane("source").BestSize((srcwidth, srcheight)) 1485 self._mgr.GetPane("target").BestSize((srcwidth, tgtheight)) 1486 if self.show_target: 1487 self._mgr.GetPane("target").Show() 1488 self._mgr.Update() 1489 self.resize = False 1490 elif self.resize: 1491 event.RequestMore() 1492 pass 1493 1494 1495class GCPList(ListCtrl, 1496 CheckListCtrlMixin, 1497 ListCtrlAutoWidthMixin): 1498 1499 def __init__(self, parent, gcp, id=wx.ID_ANY, 1500 pos=wx.DefaultPosition, size=wx.DefaultSize, 1501 style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES | 1502 wx.LC_SINGLE_SEL): 1503 1504 ListCtrl.__init__(self, parent, id, pos, size, style) 1505 1506 self.gcp = gcp # GCP class 1507 self.render = True 1508 1509 # Mixin settings 1510 CheckListCtrlMixin.__init__(self) 1511 ListCtrlAutoWidthMixin.__init__(self) 1512 # TextEditMixin.__init__(self) 1513 1514 # tracks whether list items are checked or not 1515 self.CheckList = [] 1516 1517 self._Create() 1518 1519 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) 1520 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) 1521 self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick) 1522 1523 self.selected = wx.NOT_FOUND 1524 self.selectedkey = -1 1525 1526 def _Create(self): 1527 1528 if 0: 1529 # normal, simple columns 1530 idx_col = 0 1531 for col in (_('use'), 1532 _('source X'), 1533 _('source Y'), 1534 _('target X'), 1535 _('target Y'), 1536 _('Forward error'), 1537 _('Backward error')): 1538 self.InsertColumn(idx_col, col) 1539 idx_col += 1 1540 else: 1541 # the hard way: we want images on the column header 1542 info = wx.ListItem() 1543 info.SetMask( 1544 wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT) 1545 info.SetImage(-1) 1546 info.m_format = wx.LIST_FORMAT_LEFT 1547 1548 idx_col = 0 1549 for lbl in (_('use'), 1550 _('source X'), 1551 _('source Y'), 1552 _('target X'), 1553 _('target Y'), 1554 _('Forward error'), 1555 _('Backward error')): 1556 info.SetText(lbl) 1557 self.InsertColumn(idx_col, info) 1558 idx_col += 1 1559 1560 def LoadData(self): 1561 """Load data into list""" 1562 self.DeleteAllItems() 1563 1564 self.render = False 1565 if os.path.isfile(self.gcp.file['points']): 1566 self.gcp.ReadGCPs() 1567 else: 1568 # 3 gcp is minimum 1569 for i in range(3): 1570 self.gcp.AddGCP(None) 1571 1572 # select first point by default 1573 self.selected = 0 1574 self.selectedkey = self.GetItemData(self.selected) 1575 self.SetItemState(self.selected, 1576 wx.LIST_STATE_SELECTED, 1577 wx.LIST_STATE_SELECTED) 1578 1579 self.ResizeColumns() 1580 self.render = True 1581 1582 self.EnsureVisible(self.selected) 1583 1584 def OnCheckItem(self, index, flag): 1585 """Item is checked/unchecked""" 1586 1587 if self.render: 1588 # redraw points 1589 sourceMapWin = self.gcp.SrcMapWindow 1590 sourceMapWin.UpdateMap(render=False) 1591 if self.gcp.show_target: 1592 targetMapWin = self.gcp.TgtMapWindow 1593 targetMapWin.UpdateMap(render=False) 1594 1595 def AddGCPItem(self): 1596 """ 1597 Appends an item to GCP list 1598 """ 1599 self.selectedkey = self.GetItemCount() + 1 1600 1601 self.Append([str(self.selectedkey), # GCP number 1602 '0.0', # source E 1603 '0.0', # source N 1604 '0.0', # target E 1605 '0.0', # target N 1606 '', # forward error 1607 '']) # backward error 1608 1609 self.selected = self.GetItemCount() - 1 1610 self.SetItemData(self.selected, self.selectedkey) 1611 1612 self.SetItemState(self.selected, 1613 wx.LIST_STATE_SELECTED, 1614 wx.LIST_STATE_SELECTED) 1615 1616 self.ResizeColumns() 1617 1618 self.gcp.pointsToDrawSrc.AddItem( 1619 coords=[0, 0], label=str(self.selectedkey)) 1620 self.gcp.pointsToDrawTgt.AddItem( 1621 coords=[0, 0], label=str(self.selectedkey)) 1622 1623 self.EnsureVisible(self.selected) 1624 1625 return self.selected 1626 1627 def DeleteGCPItem(self): 1628 """Deletes selected item in GCP list. 1629 """ 1630 if self.selected == wx.NOT_FOUND: 1631 return 1632 1633 key = self.GetItemData(self.selected) 1634 self.DeleteItem(self.selected) 1635 1636 if self.selected != wx.NOT_FOUND: 1637 item = self.gcp.pointsToDrawSrc.GetItem(key - 1) 1638 self.gcp.pointsToDrawSrc.DeleteItem(item) 1639 1640 item = self.gcp.pointsToDrawTgt.GetItem(key - 1) 1641 self.gcp.pointsToDrawTgt.DeleteItem(item) 1642 1643 return key 1644 1645 def ResizeColumns(self): 1646 """Resize columns""" 1647 minWidth = [90, 120] 1648 for i in range(self.GetColumnCount()): 1649 self.SetColumnWidth(i, wx.LIST_AUTOSIZE) 1650 # first column is checkbox, don't set to minWidth 1651 if i > 0 and self.GetColumnWidth(i) < minWidth[i > 4]: 1652 self.SetColumnWidth(i, minWidth[i > 4]) 1653 1654 self.SendSizeEvent() 1655 1656 def GetSelected(self): 1657 """Get index of selected item""" 1658 return self.selected 1659 1660 def OnItemSelected(self, event): 1661 """Item selected 1662 """ 1663 if self.render and self.selected != event.GetIndex(): 1664 self.selected = event.GetIndex() 1665 self.selectedkey = self.GetItemData(self.selected) 1666 sourceMapWin = self.gcp.SrcMapWindow 1667 sourceMapWin.UpdateMap(render=False) 1668 if self.gcp.show_target: 1669 targetMapWin = self.gcp.TgtMapWindow 1670 targetMapWin.UpdateMap(render=False) 1671 event.Skip() 1672 1673 def OnItemActivated(self, event): 1674 """ 1675 When item double clicked, open editor to update coordinate values 1676 """ 1677 coords = [] 1678 index = event.GetIndex() 1679 key = self.GetItemData(index) 1680 changed = False 1681 1682 for i in range(1, 5): 1683 coords.append(self.GetItem(index, i).GetText()) 1684 1685 dlg = EditGCP(parent=self, id=wx.ID_ANY, data=coords, gcpno=key) 1686 1687 if dlg.ShowModal() == wx.ID_OK: 1688 values = dlg.GetValues() # string 1689 1690 if len(values) == 0: 1691 GError(parent=self, message=_( 1692 "Invalid coordinate value. Operation canceled.")) 1693 else: 1694 for i in range(len(values)): 1695 if values[i] != coords[i]: 1696 self.SetItem(index, i + 1, values[i]) 1697 changed = True 1698 1699 if changed: 1700 # reset RMS and update mapcoordlist 1701 self.SetItem(index, 5, '') 1702 self.SetItem(index, 6, '') 1703 key = self.GetItemData(index) 1704 self.gcp.mapcoordlist[key] = [key, 1705 float(values[0]), 1706 float(values[1]), 1707 float(values[2]), 1708 float(values[3]), 1709 0.0, 1710 0.0] 1711 1712 self.gcp.pointsToDrawSrc.GetItem( 1713 key - 1).SetCoords([float(values[0]), float(values[1])]) 1714 self.gcp.pointsToDrawTgt.GetItem( 1715 key - 1).SetCoords([float(values[2]), float(values[3])]) 1716 self.gcp.UpdateColours() 1717 1718 def OnColClick(self, event): 1719 """ListCtrl forgets selected item...""" 1720 self.selected = self.FindItem(-1, self.selectedkey) 1721 self.SetItemState(self.selected, 1722 wx.LIST_STATE_SELECTED, 1723 wx.LIST_STATE_SELECTED) 1724 1725 event.Skip() 1726 1727class EditGCP(wx.Dialog): 1728 1729 def __init__(self, parent, data, gcpno, id=wx.ID_ANY, 1730 title=_("Edit GCP"), 1731 style=wx.DEFAULT_DIALOG_STYLE): 1732 """Dialog for editing GPC and map coordinates in list control""" 1733 1734 wx.Dialog.__init__(self, parent, id, title=title, style=style) 1735 1736 panel = wx.Panel(parent=self) 1737 1738 sizer = wx.BoxSizer(wx.VERTICAL) 1739 1740 box = StaticBox( 1741 parent=panel, id=wx.ID_ANY, label=" %s %s " % 1742 (_("Ground Control Point No."), str(gcpno))) 1743 boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) 1744 1745 # source coordinates 1746 gridSizer = wx.GridBagSizer(vgap=5, hgap=5) 1747 1748 self.xcoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) 1749 self.ycoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) 1750 self.ecoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) 1751 self.ncoord = TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1)) 1752 1753 # swap source N, target E 1754 tmp_coord = data[1] 1755 data[1] = data[2] 1756 data[2] = tmp_coord 1757 1758 row = 0 1759 col = 0 1760 idx = 0 1761 for label, win in ((_("source X:"), self.xcoord), 1762 (_("target X:"), self.ecoord), 1763 (_("source Y:"), self.ycoord), 1764 (_("target Y:"), self.ncoord)): 1765 label = StaticText(parent=panel, id=wx.ID_ANY, 1766 label=label) 1767 gridSizer.Add(label, 1768 flag=wx.ALIGN_CENTER_VERTICAL, 1769 pos=(row, col)) 1770 1771 col += 1 1772 win.SetValue(str(data[idx])) 1773 1774 gridSizer.Add(win, 1775 pos=(row, col)) 1776 1777 col += 1 1778 idx += 1 1779 1780 if col > 3: 1781 row += 1 1782 col = 0 1783 1784 boxSizer.Add(gridSizer, proportion=1, 1785 flag=wx.EXPAND | wx.ALL, border=5) 1786 1787 sizer.Add(boxSizer, proportion=1, 1788 flag=wx.EXPAND | wx.ALL, border=5) 1789 1790 # 1791 # buttons 1792 # 1793 self.btnCancel = Button(panel, wx.ID_CANCEL) 1794 self.btnOk = Button(panel, wx.ID_OK) 1795 self.btnOk.SetDefault() 1796 1797 btnSizer = wx.StdDialogButtonSizer() 1798 btnSizer.AddButton(self.btnCancel) 1799 btnSizer.AddButton(self.btnOk) 1800 btnSizer.Realize() 1801 1802 sizer.Add(btnSizer, proportion=0, 1803 flag=wx.ALIGN_RIGHT | wx.ALL, border=5) 1804 1805 panel.SetSizer(sizer) 1806 sizer.Fit(self) 1807 1808 def GetValues(self, columns=None): 1809 """Return list of values (as strings). 1810 """ 1811 valuelist = [] 1812 try: 1813 float(self.xcoord.GetValue()) 1814 float(self.ycoord.GetValue()) 1815 float(self.ecoord.GetValue()) 1816 float(self.ncoord.GetValue()) 1817 except ValueError: 1818 return valuelist 1819 1820 valuelist.append(self.xcoord.GetValue()) 1821 valuelist.append(self.ycoord.GetValue()) 1822 valuelist.append(self.ecoord.GetValue()) 1823 valuelist.append(self.ncoord.GetValue()) 1824 1825 return valuelist 1826 1827 1828class GrSettingsDialog(wx.Dialog): 1829 1830 def __init__( 1831 self, parent, id, giface, title, pos=wx.DefaultPosition, 1832 size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE): 1833 wx.Dialog.__init__(self, parent, id, title, pos, size, style) 1834 """ 1835 Dialog to set profile text options: font, title 1836 and font size, axis labels and font size 1837 """ 1838 # 1839 # initialize variables 1840 # 1841 self.parent = parent 1842 self.new_src_map = src_map 1843 self.new_tgt_map = {'raster': tgt_map['raster']} 1844 self.sdfactor = 0 1845 1846 self.symbol = {} 1847 1848 self.methods = ["nearest", 1849 "linear", 1850 "linear_f", 1851 "cubic", 1852 "cubic_f", 1853 "lanczos", 1854 "lanczos_f"] 1855 1856 # notebook 1857 notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT) 1858 self.__CreateSymbologyPage(notebook) 1859 self.__CreateRectificationPage(notebook) 1860 1861 # buttons 1862 btnSave = Button(self, wx.ID_SAVE) 1863 btnApply = Button(self, wx.ID_APPLY) 1864 btnClose = Button(self, wx.ID_CLOSE) 1865 btnApply.SetDefault() 1866 1867 # bindings 1868 btnApply.Bind(wx.EVT_BUTTON, self.OnApply) 1869 btnApply.SetToolTip(_("Apply changes for the current session")) 1870 btnSave.Bind(wx.EVT_BUTTON, self.OnSave) 1871 btnSave.SetToolTip( 1872 _("Apply and save changes to user settings file (default for next sessions)")) 1873 btnClose.Bind(wx.EVT_BUTTON, self.OnClose) 1874 btnClose.SetToolTip(_("Close dialog")) 1875 1876 # sizers 1877 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 1878 btnSizer.Add(btnApply, flag=wx.LEFT | wx.RIGHT, border=5) 1879 btnSizer.Add(btnSave, flag=wx.LEFT | wx.RIGHT, border=5) 1880 btnSizer.Add(btnClose, flag=wx.LEFT | wx.RIGHT, border=5) 1881 1882 # sizers 1883 mainSizer = wx.BoxSizer(wx.VERTICAL) 1884 mainSizer.Add( 1885 notebook, 1886 proportion=1, 1887 flag=wx.EXPAND | wx.ALL, 1888 border=5) 1889 mainSizer.Add(btnSizer, proportion=0, 1890 flag=wx.ALIGN_RIGHT | wx.ALL, border=5) 1891 # flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5) 1892 1893 self.SetSizer(mainSizer) 1894 mainSizer.Fit(self) 1895 1896 def __CreateSymbologyPage(self, notebook): 1897 """Create notebook page with symbology settings""" 1898 1899 panel = wx.Panel(parent=notebook, id=wx.ID_ANY) 1900 notebook.AddPage(page=panel, text=_("Symbology")) 1901 1902 sizer = wx.BoxSizer(wx.VERTICAL) 1903 1904 rmsgridSizer = wx.GridBagSizer(vgap=5, hgap=5) 1905 1906 # highlight only highest forward RMS error 1907 self.highlighthighest = wx.CheckBox( 1908 parent=panel, id=wx.ID_ANY, 1909 label=_("Highlight highest RMS error only")) 1910 hh = UserSettings.Get(group='gcpman', key='rms', subkey='highestonly') 1911 self.highlighthighest.SetValue(hh) 1912 rmsgridSizer.Add( 1913 self.highlighthighest, 1914 flag=wx.ALIGN_CENTER_VERTICAL, 1915 pos=( 1916 0, 1917 0)) 1918 1919 # RMS forward error threshold 1920 rmslabel = StaticText( 1921 parent=panel, id=wx.ID_ANY, 1922 label=_("Highlight RMS error > M + SD * factor:")) 1923 rmslabel.SetToolTip( 1924 wx.ToolTip( 1925 _( 1926 "Highlight GCPs with an RMS error larger than \n" 1927 "mean + standard deviation * given factor. \n" 1928 "Recommended values for this factor are between 1 and 2."))) 1929 rmsgridSizer.Add( 1930 rmslabel, 1931 flag=wx.ALIGN_CENTER_VERTICAL, 1932 pos=( 1933 1, 1934 0)) 1935 sdfactor = UserSettings.Get( 1936 group='gcpman', key='rms', subkey='sdfactor') 1937 self.rmsWin = TextCtrl(parent=panel, id=wx.ID_ANY, 1938 size=(70, -1), style=wx.TE_NOHIDESEL) 1939 self.rmsWin.SetValue("%s" % str(sdfactor)) 1940 if (self.parent.highest_only == True): 1941 self.rmsWin.Disable() 1942 1943 self.symbol['sdfactor'] = self.rmsWin.GetId() 1944 rmsgridSizer.Add(self.rmsWin, flag=wx.ALIGN_RIGHT, pos=(1, 1)) 1945 rmsgridSizer.AddGrowableCol(1) 1946 sizer.Add(rmsgridSizer, flag=wx.EXPAND | wx.ALL, border=5) 1947 1948 box = StaticBox(parent=panel, id=wx.ID_ANY, 1949 label=" %s " % _("Symbol settings")) 1950 boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) 1951 gridSizer = wx.GridBagSizer(vgap=5, hgap=5) 1952 1953 # 1954 # general symbol color 1955 # 1956 row = 0 1957 label = StaticText(parent=panel, id=wx.ID_ANY, label=_("Color:")) 1958 gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) 1959 col = UserSettings.Get(group='gcpman', key='symbol', subkey='color') 1960 colWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY, 1961 colour=wx.Colour(col[0], 1962 col[1], 1963 col[2], 1964 255)) 1965 self.symbol['color'] = colWin.GetId() 1966 gridSizer.Add(colWin, 1967 flag=wx.ALIGN_RIGHT, 1968 pos=(row, 1)) 1969 1970 # 1971 # symbol color for high forward RMS error 1972 # 1973 row += 1 1974 label = StaticText( 1975 parent=panel, 1976 id=wx.ID_ANY, 1977 label=_("Color for high RMS error:")) 1978 gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) 1979 hcol = UserSettings.Get(group='gcpman', key='symbol', subkey='hcolor') 1980 hcolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY, 1981 colour=wx.Colour(hcol[0], 1982 hcol[1], 1983 hcol[2], 1984 255)) 1985 self.symbol['hcolor'] = hcolWin.GetId() 1986 gridSizer.Add(hcolWin, 1987 flag=wx.ALIGN_RIGHT, 1988 pos=(row, 1)) 1989 1990 # 1991 # symbol color for selected GCP 1992 # 1993 row += 1 1994 label = StaticText( 1995 parent=panel, 1996 id=wx.ID_ANY, 1997 label=_("Color for selected GCP:")) 1998 gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) 1999 scol = UserSettings.Get(group='gcpman', key='symbol', subkey='scolor') 2000 scolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY, 2001 colour=wx.Colour(scol[0], 2002 scol[1], 2003 scol[2], 2004 255)) 2005 self.symbol['scolor'] = scolWin.GetId() 2006 gridSizer.Add(scolWin, 2007 flag=wx.ALIGN_RIGHT, 2008 pos=(row, 1)) 2009 2010 # 2011 # symbol color for unused GCP 2012 # 2013 row += 1 2014 label = StaticText( 2015 parent=panel, 2016 id=wx.ID_ANY, 2017 label=_("Color for unused GCPs:")) 2018 gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) 2019 ucol = UserSettings.Get(group='gcpman', key='symbol', subkey='ucolor') 2020 ucolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY, 2021 colour=wx.Colour(ucol[0], 2022 ucol[1], 2023 ucol[2], 2024 255)) 2025 self.symbol['ucolor'] = ucolWin.GetId() 2026 gridSizer.Add(ucolWin, 2027 flag=wx.ALIGN_RIGHT, 2028 pos=(row, 1)) 2029 2030 # show unused GCPs 2031 row += 1 2032 self.showunused = wx.CheckBox(parent=panel, id=wx.ID_ANY, 2033 label=_("Show unused GCPs")) 2034 shuu = UserSettings.Get(group='gcpman', key='symbol', subkey='unused') 2035 self.showunused.SetValue(shuu) 2036 gridSizer.Add( 2037 self.showunused, 2038 flag=wx.ALIGN_CENTER_VERTICAL, 2039 pos=( 2040 row, 2041 0)) 2042 2043 # 2044 # symbol size 2045 # 2046 row += 1 2047 label = StaticText( 2048 parent=panel, 2049 id=wx.ID_ANY, 2050 label=_("Symbol size:")) 2051 gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) 2052 symsize = int( 2053 UserSettings.Get( 2054 group='gcpman', 2055 key='symbol', 2056 subkey='size')) 2057 sizeWin = SpinCtrl(parent=panel, id=wx.ID_ANY, 2058 min=1, max=20) 2059 sizeWin.SetValue(symsize) 2060 self.symbol['size'] = sizeWin.GetId() 2061 gridSizer.Add(sizeWin, 2062 flag=wx.ALIGN_RIGHT, 2063 pos=(row, 1)) 2064 2065 # 2066 # symbol width 2067 # 2068 row += 1 2069 label = StaticText( 2070 parent=panel, 2071 id=wx.ID_ANY, 2072 label=_("Line width:")) 2073 gridSizer.Add(label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) 2074 width = int( 2075 UserSettings.Get( 2076 group='gcpman', 2077 key='symbol', 2078 subkey='width')) 2079 widWin = SpinCtrl(parent=panel, id=wx.ID_ANY, 2080 min=1, max=10) 2081 widWin.SetValue(width) 2082 self.symbol['width'] = widWin.GetId() 2083 gridSizer.Add(widWin, 2084 flag=wx.ALIGN_RIGHT, 2085 pos=(row, 1)) 2086 gridSizer.AddGrowableCol(1) 2087 2088 boxSizer.Add(gridSizer, flag=wx.EXPAND) 2089 sizer.Add(boxSizer, flag=wx.EXPAND | wx.ALL, border=5) 2090 2091 # 2092 # maps to display 2093 # 2094 # source map to display 2095 self.srcselection = Select( 2096 panel, 2097 id=wx.ID_ANY, 2098 size=globalvar.DIALOG_GSELECT_SIZE, 2099 type='maptype', 2100 updateOnPopup=False) 2101 self.parent.grwiz.SwitchEnv('source') 2102 self.srcselection.SetElementList(maptype) 2103 # filter out all maps not in group 2104 self.srcselection.tcp.GetElementList(elements=self.parent.src_maps) 2105 2106 # target map(s) to display 2107 self.parent.grwiz.SwitchEnv('target') 2108 self.tgtrastselection = Select( 2109 panel, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE, 2110 type='raster', updateOnPopup=False) 2111 self.tgtrastselection.SetElementList('cell') 2112 self.tgtrastselection.GetElementList() 2113 2114 sizer.Add( 2115 StaticText( 2116 parent=panel, 2117 id=wx.ID_ANY, 2118 label=_('Select source map to display:')), 2119 proportion=0, 2120 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2121 border=5) 2122 sizer.Add( 2123 self.srcselection, 2124 proportion=0, 2125 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2126 border=5) 2127 self.srcselection.SetValue(src_map) 2128 sizer.Add( 2129 StaticText( 2130 parent=panel, 2131 id=wx.ID_ANY, 2132 label=_('Select target raster map to display:')), 2133 proportion=0, 2134 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2135 border=5) 2136 sizer.Add( 2137 self.tgtrastselection, 2138 proportion=0, 2139 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2140 border=5) 2141 self.tgtrastselection.SetValue(tgt_map['raster']) 2142 2143 # bindings 2144 self.highlighthighest.Bind(wx.EVT_CHECKBOX, self.OnHighlight) 2145 self.rmsWin.Bind(wx.EVT_TEXT, self.OnSDFactor) 2146 self.srcselection.Bind(wx.EVT_TEXT, self.OnSrcSelection) 2147 self.tgtrastselection.Bind(wx.EVT_TEXT, self.OnTgtRastSelection) 2148 2149 panel.SetSizer(sizer) 2150 2151 return panel 2152 2153 def __CreateRectificationPage(self, notebook): 2154 """Create notebook page with symbology settings""" 2155 2156 panel = wx.Panel(parent=notebook, id=wx.ID_ANY) 2157 notebook.AddPage(page=panel, text=_("Rectification")) 2158 2159 sizer = wx.BoxSizer(wx.VERTICAL) 2160 2161 # transformation order 2162 self.rb_grorder = wx.RadioBox( 2163 parent=panel, 2164 id=wx.ID_ANY, 2165 label=" %s " % 2166 _("Select rectification order"), 2167 choices=[ 2168 _('1st order'), 2169 _('2nd order'), 2170 _('3rd order')], 2171 majorDimension=wx.RA_SPECIFY_COLS) 2172 sizer.Add(self.rb_grorder, proportion=0, 2173 flag=wx.EXPAND | wx.ALL, border=5) 2174 self.rb_grorder.SetSelection(self.parent.gr_order - 1) 2175 2176 # interpolation method 2177 gridSizer = wx.GridBagSizer(vgap=5, hgap=5) 2178 gridSizer.Add( 2179 StaticText( 2180 parent=panel, 2181 id=wx.ID_ANY, 2182 label=_('Select interpolation method:')), 2183 pos=( 2184 0, 2185 0), 2186 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2187 border=5) 2188 self.grmethod = wx.Choice(parent=panel, id=wx.ID_ANY, 2189 choices=self.methods) 2190 gridSizer.Add(self.grmethod, pos=(0, 1), 2191 flag=wx.ALIGN_RIGHT, border=5) 2192 self.grmethod.SetStringSelection(self.parent.gr_method) 2193 gridSizer.AddGrowableCol(1) 2194 sizer.Add(gridSizer, flag=wx.EXPAND | wx.ALL, border=5) 2195 2196 # clip to region 2197 self.check = wx.CheckBox(parent=panel, id=wx.ID_ANY, label=_( 2198 "clip to computational region in target location")) 2199 sizer.Add(self.check, proportion=0, 2200 flag=wx.EXPAND | wx.ALL, border=5) 2201 self.check.SetValue(self.parent.clip_to_region) 2202 2203 # extension 2204 sizer.Add( 2205 StaticText( 2206 parent=panel, 2207 id=wx.ID_ANY, 2208 label=_('Extension for output maps:')), 2209 proportion=0, 2210 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2211 border=5) 2212 self.ext_txt = TextCtrl( 2213 parent=panel, id=wx.ID_ANY, value="", size=( 2214 350, -1)) 2215 self.ext_txt.SetValue(self.parent.extension) 2216 sizer.Add( 2217 self.ext_txt, 2218 proportion=0, 2219 flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2220 border=5) 2221 2222 # bindings 2223 self.ext_txt.Bind(wx.EVT_TEXT, self.OnExtension) 2224 self.Bind(wx.EVT_RADIOBOX, self.parent.OnGROrder, self.rb_grorder) 2225 self.Bind(wx.EVT_CHOICE, self.OnMethod, self.grmethod) 2226 self.Bind(wx.EVT_CHECKBOX, self.OnClipRegion, self.check) 2227 2228 panel.SetSizer(sizer) 2229 2230 return panel 2231 2232 def OnHighlight(self, event): 2233 """Checkbox 'highlighthighest' checked/unchecked""" 2234 if self.highlighthighest.IsChecked(): 2235 self.parent.highest_only = True 2236 self.rmsWin.Disable() 2237 else: 2238 self.parent.highest_only = False 2239 self.rmsWin.Enable() 2240 2241 def OnSDFactor(self, event): 2242 """New factor for RMS threshold = M + SD * factor""" 2243 try: 2244 self.sdfactor = float(self.rmsWin.GetValue()) 2245 except ValueError: 2246 return 2247 if self.sdfactor <= 0: 2248 GError(parent=self, 2249 message=_('RMS threshold factor must be > 0')) 2250 elif self.sdfactor < 1: 2251 GError(parent=self, 2252 message=_('RMS threshold factor is < 1\n' 2253 'Too many points might be highlighted')) 2254 2255 def OnSrcSelection(self, event): 2256 """Source map to display selected""" 2257 global src_map 2258 2259 tmp_map = self.srcselection.GetValue() 2260 2261 if not tmp_map == '' and not tmp_map == src_map: 2262 self.new_src_map = tmp_map 2263 2264 def OnTgtRastSelection(self, event): 2265 """Target map to display selected""" 2266 global tgt_map 2267 2268 self.new_tgt_map['raster'] = self.tgtrastselection.GetValue() 2269 2270 def OnMethod(self, event): 2271 self.parent.gr_method = self.methods[event.GetSelection()] 2272 2273 def OnClipRegion(self, event): 2274 self.parent.clip_to_region = event.IsChecked() 2275 2276 def OnExtension(self, event): 2277 self.parent.extension = self.ext_txt.GetValue() 2278 2279 def UpdateSettings(self): 2280 global src_map 2281 global tgt_map 2282 global maptype 2283 2284 layers = None 2285 2286 UserSettings.Set(group='gcpman', key='rms', subkey='highestonly', 2287 value=self.highlighthighest.GetValue()) 2288 2289 if self.sdfactor > 0: 2290 UserSettings.Set(group='gcpman', key='rms', subkey='sdfactor', 2291 value=self.sdfactor) 2292 2293 self.parent.sdfactor = self.sdfactor 2294 if self.parent.rmsthresh > 0: 2295 self.parent.rmsthresh = self.parent.rmsmean + self.parent.sdfactor * self.parent.rmssd 2296 2297 UserSettings.Set( 2298 group='gcpman', 2299 key='symbol', 2300 subkey='color', 2301 value=tuple( 2302 wx.FindWindowById( 2303 self.symbol['color']).GetColour())) 2304 UserSettings.Set( 2305 group='gcpman', 2306 key='symbol', 2307 subkey='hcolor', 2308 value=tuple( 2309 wx.FindWindowById( 2310 self.symbol['hcolor']).GetColour())) 2311 UserSettings.Set( 2312 group='gcpman', 2313 key='symbol', 2314 subkey='scolor', 2315 value=tuple( 2316 wx.FindWindowById( 2317 self.symbol['scolor']).GetColour())) 2318 UserSettings.Set( 2319 group='gcpman', 2320 key='symbol', 2321 subkey='ucolor', 2322 value=tuple( 2323 wx.FindWindowById( 2324 self.symbol['ucolor']).GetColour())) 2325 UserSettings.Set(group='gcpman', key='symbol', subkey='unused', 2326 value=self.showunused.GetValue()) 2327 UserSettings.Set( 2328 group='gcpman', 2329 key='symbol', 2330 subkey='size', 2331 value=wx.FindWindowById( 2332 self.symbol['size']).GetValue()) 2333 UserSettings.Set( 2334 group='gcpman', 2335 key='symbol', 2336 subkey='width', 2337 value=wx.FindWindowById( 2338 self.symbol['width']).GetValue()) 2339 2340 srcrender = False 2341 tgtrender = False 2342 reload_target = False 2343 if self.new_src_map != src_map: 2344 # remove old layer 2345 layers = self.parent.grwiz.SrcMap.GetListOfLayers() 2346 self.parent.grwiz.SrcMap.DeleteLayer(layers[0]) 2347 2348 src_map = self.new_src_map 2349 if maptype == 'raster': 2350 cmdlist = ['d.rast', 'map=%s' % src_map] 2351 srcrender = True 2352 self.parent.grwiz.SwitchEnv('source') 2353 name, found = utils.GetLayerNameFromCmd(cmdlist) 2354 self.parent.grwiz.SrcMap.AddLayer( 2355 ltype=maptype, command=cmdlist, active=True, name=name, 2356 hidden=False, opacity=1.0, render=False) 2357 2358 self.parent.grwiz.SwitchEnv('target') 2359 2360 if self.new_tgt_map['raster'] != tgt_map['raster']: 2361 # remove all layers 2362 layers = self.parent.grwiz.TgtMap.GetListOfLayers() 2363 while layers: 2364 self.parent.grwiz.TgtMap.DeleteLayer(layers[0]) 2365 del layers[0] 2366 layers = self.parent.grwiz.TgtMap.GetListOfLayers() 2367 # self.parent.grwiz.TgtMap.DeleteAllLayers() 2368 reload_target = True 2369 tgt_map['raster'] = self.new_tgt_map['raster'] 2370 2371 if tgt_map['raster'] != '': 2372 cmdlist = ['d.rast', 'map=%s' % tgt_map['raster']] 2373 name, found = utils.GetLayerNameFromCmd(cmdlist) 2374 self.parent.grwiz.TgtMap.AddLayer( 2375 ltype='raster', command=cmdlist, active=True, name=name, 2376 hidden=False, opacity=1.0, render=False) 2377 2378 tgtrender = True 2379 2380 if tgt_map['raster'] == '': 2381 if self.parent.show_target == True: 2382 self.parent.show_target = False 2383 self.parent._mgr.GetPane("target").Hide() 2384 self.parent._mgr.Update() 2385 self.parent.activemap.SetSelection(0) 2386 self.parent.activemap.Enable(False) 2387 self.parent.GetMapToolbar().Enable('zoommenu', enable=False) 2388 else: 2389 if self.parent.show_target == False: 2390 self.parent.show_target = True 2391 self.parent._mgr.GetPane("target").Show() 2392 self.parent._mgr.Update() 2393 self.parent.activemap.SetSelection(0) 2394 self.parent.activemap.Enable(True) 2395 self.parent.GetMapToolbar().Enable('zoommenu', enable=True) 2396 self.parent.TgtMapWindow.ZoomToMap( 2397 layers=self.parent.TgtMap.GetListOfLayers()) 2398 2399 self.parent.UpdateColours( 2400 srcrender, 2401 tgtrender) 2402 self.parent.SetSettings() 2403 2404 def OnSave(self, event): 2405 """Button 'Save' pressed""" 2406 self.UpdateSettings() 2407 fileSettings = {} 2408 UserSettings.ReadSettingsFile(settings=fileSettings) 2409 fileSettings['gcpman'] = UserSettings.Get(group='gcpman') 2410 file = UserSettings.SaveToFile(fileSettings) 2411 self.parent._giface.WriteLog( 2412 _('GCP Manager settings saved to file \'%s\'.') % 2413 file) 2414 # self.Close() 2415 2416 def OnApply(self, event): 2417 """Button 'Apply' pressed""" 2418 self.UpdateSettings() 2419 # self.Close() 2420 2421 def OnClose(self, event): 2422 """Button 'Cancel' pressed""" 2423 self.Close() 2424