ip2i_manager.py 81 KB

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