ii2t_manager.py 110 KB

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