ip2i_manager.py 80 KB

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