ip2i_manager.py 82 KB

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