ip2i_manager.py 83 KB

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