ip2i_manager.py 82 KB

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