georect.py 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605
  1. """
  2. @package georect.py
  3. Georectification module for GRASS GIS. Includes ground control
  4. point management and interactive point and click GCP creation
  5. Classes:
  6. - Georectify
  7. - GCP
  8. - GRMap
  9. - GrSettingsDialog
  10. COPYRIGHT: (C) 2006-2008 by the GRASS Development Team
  11. This program is free software under the GNU General Public
  12. License (>=v2). Read the file COPYING that comes with GRASS
  13. for details.
  14. @author Michael Barton
  15. Updated by Martin Landa <landa.martin gmail.com>
  16. """
  17. # recheck once completed to see how many of these are still needed
  18. import os
  19. import sys
  20. import tempfile
  21. import shutil
  22. import time
  23. import wx
  24. from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin, TextEditMixin
  25. import wx.lib.colourselect as csel
  26. import wx.wizard as wiz
  27. import grass
  28. import globalvar
  29. import mapdisp
  30. import render
  31. import toolbars
  32. import menuform
  33. import gselect
  34. import gcmd
  35. import utils
  36. from debug import Debug as Debug
  37. from icon import Icons as Icons
  38. from location_wizard import TitledPage as TitledPage
  39. from preferences import globalSettings as UserSettings
  40. try:
  41. import subprocess # Not needed if GRASS commands could actually be quiet
  42. except:
  43. CompatPath = globalvar.ETCWXDIR
  44. sys.path.append(CompatPath)
  45. from compat import subprocess
  46. gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
  47. sys.path.append(gmpath)
  48. #
  49. # global variables
  50. #
  51. global xy_map
  52. global maptype
  53. xy_map = ''
  54. maptype = 'cell'
  55. class GeorectWizard(object):
  56. """
  57. Start wizard here and finish wizard here
  58. """
  59. def __init__(self, parent):
  60. self.parent = parent # GMFrame
  61. #
  62. # get environmental variables
  63. #
  64. self.grassdatabase = grass.gisenv()['GISDBASE']
  65. #
  66. # read original environment settings
  67. #
  68. self.orig_gisrc = os.environ['GISRC']
  69. self.gisrc_dict = {}
  70. try:
  71. f = open(self.orig_gisrc, 'r')
  72. for line in f.readlines():
  73. if line != '':
  74. line = line.replace('\n', '').strip()
  75. key, value = line.split(':')
  76. self.gisrc_dict[key.strip()] = value.strip()
  77. finally:
  78. f.close()
  79. self.currentlocation = self.gisrc_dict['LOCATION_NAME']
  80. self.currentmapset = self.gisrc_dict['MAPSET']
  81. # location for xy map to georectify
  82. self.newlocation = ''
  83. # mapset for xy map to georectify
  84. self.newmapset = ''
  85. # GISRC file for source location/mapset of map(s) to georectify
  86. self.new_gisrc = ''
  87. #
  88. # define wizard pages
  89. #
  90. self.wizard = wiz.Wizard(parent=parent, id=wx.ID_ANY, title=_("Setup for georectification"))
  91. self.startpage = LocationPage(self.wizard, self)
  92. self.grouppage = GroupPage(self.wizard, self)
  93. self.mappage = DispMapPage(self.wizard, self)
  94. # Set the initial order of the pages
  95. self.startpage.SetNext(self.grouppage)
  96. self.grouppage.SetPrev(self.startpage)
  97. self.grouppage.SetNext(self.mappage)
  98. self.mappage.SetPrev(self.grouppage)
  99. #
  100. # do pages layout
  101. #
  102. self.startpage.DoLayout()
  103. self.grouppage.DoLayout()
  104. self.mappage.DoLayout()
  105. self.wizard.FitToPage(self.startpage)
  106. # self.Bind(wx.EVT_CLOSE, self.Cleanup)
  107. # self.parent.Bind(wx.EVT_ACTIVATE, self.OnGLMFocus)
  108. success = False
  109. #
  110. # run wizard
  111. #
  112. if self.wizard.RunWizard(self.startpage):
  113. success = self.OnWizFinished()
  114. if success == False:
  115. wx.MessageBox(parent=self.parent,
  116. message=_("Georectifying setup canceled."),
  117. caption=_("Georectify"),
  118. style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
  119. self.Cleanup()
  120. else:
  121. wx.MessageBox(parent=self.parent,
  122. message=_("Georectifying setup canceled."),
  123. caption=_("Georectify"),
  124. style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
  125. self.Cleanup()
  126. #
  127. # start display showing xymap
  128. #
  129. if success != False:
  130. # instance of render.Map to be associated with display
  131. self.Map = render.Map(gisrc=self.new_gisrc)
  132. global maptype
  133. global xy_map
  134. #
  135. # add layer to map
  136. #
  137. if maptype == 'cell':
  138. rendertype = 'raster'
  139. cmdlist = ['d.rast', 'map=%s' % xy_map]
  140. elif maptype == 'vector':
  141. rendertype = 'vector'
  142. cmdlist = ['d.vect', 'map=%s' % xy_map]
  143. self.Map.AddLayer(type=rendertype, command=cmdlist, l_active=True,
  144. name=utils.GetLayerNameFromCmd(cmdlist),
  145. l_hidden=False, l_opacity=1.0, l_render=False)
  146. #
  147. # start GCP form
  148. #
  149. self.gcpmgr = GCP(self.parent, grwiz=self)
  150. self.gcpmgr.Show()
  151. #
  152. # start map display
  153. #
  154. self.xy_mapdisp = mapdisp.MapFrame(self.gcpmgr, title=_("Set ground control points (GCPs)"),
  155. size=globalvar.MAP_WINDOW_SIZE,
  156. toolbars=["georect"],
  157. Map=self.Map, gismgr=self.parent)
  158. self.gcpmgr.SetMapDisplay(self.xy_mapdisp)
  159. self.mapwin = self.xy_mapdisp.MapWindow
  160. # set mouse characteristics
  161. self.mapwin.mouse['box'] = 'point'
  162. self.mapwin.mouse["use"] == "pointer"
  163. self.mapwin.zoomtype = 0
  164. self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID)
  165. self.mapwin.SetCursor(self.xy_mapdisp.cursors["cross"])
  166. #
  167. # show new display & draw map
  168. #
  169. self.xy_mapdisp.Show()
  170. else:
  171. self.Cleanup()
  172. def SetSrcEnv(self, location, mapset):
  173. """Create environment to use for location and mapset
  174. that are the source of the file(s) to georectify
  175. @param location source location
  176. @param mapset source mapset
  177. @return False on error
  178. @return True on success
  179. """
  180. self.newlocation = location
  181. self.newmapset = mapset
  182. # check to see if we are georectifying map in current working location/mapset
  183. if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
  184. return False
  185. self.gisrc_dict['LOCATION_NAME'] = location
  186. self.gisrc_dict['MAPSET'] = mapset
  187. self.new_gisrc = utils.GetTempfile()
  188. try:
  189. f = open(self.new_gisrc, mode='w')
  190. for line in self.gisrc_dict.items():
  191. f.write(line[0] + ": " + line[1] + "\n")
  192. finally:
  193. f.close()
  194. return True
  195. def SwitchEnv(self, grc):
  196. """
  197. Switches between original working location/mapset and
  198. location/mapset that is source of file(s) to georectify
  199. """
  200. # check to see if we are georectifying map in current working location/mapset
  201. if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
  202. return False
  203. if grc == 'original':
  204. os.environ["GISRC"] = str(self.orig_gisrc)
  205. elif grc == 'new':
  206. os.environ["GISRC"] = str(self.new_gisrc)
  207. return True
  208. def OnWizFinished(self):
  209. # self.Cleanup()
  210. return True
  211. def OnGLMFocus(self, event):
  212. """Layer Manager focus"""
  213. # self.SwitchEnv('original')
  214. event.Skip()
  215. def Cleanup(self):
  216. """Return to current location and mapset"""
  217. self.SwitchEnv('original')
  218. self.parent.georectifying = None
  219. if hasattr(self, "xy_mapdisp"):
  220. self.xy_mapdisp.Close()
  221. self.xy_mapdisp = None
  222. self.wizard.Destroy()
  223. class LocationPage(TitledPage):
  224. """
  225. Set map type (raster or vector) to georectify and
  226. select location/mapset of map(s) to georectify.
  227. """
  228. def __init__(self, wizard, parent):
  229. TitledPage.__init__(self, wizard, _("Select map type and location/mapset"))
  230. self.parent = parent
  231. self.grassdatabase = self.parent.grassdatabase
  232. self.xylocation = ''
  233. self.xymapset = ''
  234. tmplist = os.listdir(self.grassdatabase)
  235. self.locList = []
  236. self.mapsetList = []
  237. #
  238. # create a list of valid locations
  239. #
  240. for item in tmplist:
  241. if os.path.isdir(os.path.join(self.grassdatabase, item)) and \
  242. os.path.exists(os.path.join(self.grassdatabase, item, 'PERMANENT')):
  243. self.locList.append(item)
  244. utils.ListSortLower(self.locList)
  245. #
  246. # layout
  247. #
  248. self.sizer.AddGrowableCol(2)
  249. # map type
  250. self.rb_maptype = wx.RadioBox(parent=self, id=wx.ID_ANY, label=' %s ' % _("Map type to georectify"),
  251. choices=[_('raster'), _('vector')], majorDimension=wx.RA_SPECIFY_COLS)
  252. self.sizer.Add(item=self.rb_maptype,
  253. flag=wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, border=5,
  254. pos=(1, 1), span=(1, 2))
  255. # location
  256. self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source location:')),
  257. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  258. pos=(2, 1))
  259. self.cb_location = wx.ComboBox(parent=self, id=wx.ID_ANY,
  260. choices = self.locList, size=(300, -1),
  261. style=wx.CB_DROPDOWN | wx.CB_READONLY)
  262. self.sizer.Add(item=self.cb_location,
  263. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  264. pos=(2, 2))
  265. # mapset
  266. self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source mapset:')),
  267. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  268. pos=(3, 1))
  269. self.cb_mapset = wx.ComboBox(parent=self, id=wx.ID_ANY,
  270. choices = self.mapsetList, size=(300, -1),
  271. style=wx.CB_DROPDOWN | wx.CB_READONLY)
  272. self.sizer.Add(item=self.cb_mapset,
  273. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  274. pos=(3,2))
  275. #
  276. # bindings
  277. #
  278. self.Bind(wx.EVT_RADIOBOX, self.OnMaptype, self.rb_maptype)
  279. self.Bind(wx.EVT_COMBOBOX, self.OnLocation, self.cb_location)
  280. self.Bind(wx.EVT_COMBOBOX, self.OnMapset, self.cb_mapset)
  281. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  282. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  283. # self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
  284. def OnMaptype(self,event):
  285. """Change map type"""
  286. global maptype
  287. if event.GetInt() == 0:
  288. maptype = 'cell'
  289. else:
  290. maptype = 'vector'
  291. def OnLocation(self, event):
  292. """Sets source location for map(s) to georectify"""
  293. self.xylocation = event.GetString()
  294. #create a list of valid mapsets
  295. tmplist = os.listdir(os.path.join(self.grassdatabase, self.xylocation))
  296. self.mapsetList = []
  297. for item in tmplist:
  298. if os.path.isdir(os.path.join(self.grassdatabase, self.xylocation, item)) and \
  299. os.path.exists(os.path.join(self.grassdatabase, self.xylocation, item, 'WIND')):
  300. if item != 'PERMANENT':
  301. self.mapsetList.append(item)
  302. self.xymapset = 'PERMANENT'
  303. utils.ListSortLower(self.mapsetList)
  304. self.mapsetList.insert(0, 'PERMANENT')
  305. self.cb_mapset.SetItems(self.mapsetList)
  306. self.cb_mapset.SetStringSelection(self.xymapset)
  307. if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
  308. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  309. def OnMapset(self, event):
  310. """Sets source mapset for map(s) to georectify"""
  311. if self.xylocation == '':
  312. wx.MessageBox(_('You must select a valid location before selecting a mapset'))
  313. return
  314. self.xymapset = event.GetString()
  315. if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
  316. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  317. def OnPageChanging(self, event=None):
  318. if event.GetDirection() and \
  319. (self.xylocation == '' or self.xymapset == ''):
  320. wx.MessageBox(_('You must select a valid location and mapset in order to continue'))
  321. event.Veto()
  322. return
  323. else:
  324. self.parent.SetSrcEnv(self.xylocation, self.xymapset)
  325. def OnEnterPage(self, event=None):
  326. if self.xylocation == '' or self.xymapset == '':
  327. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  328. else:
  329. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  330. class GroupPage(TitledPage):
  331. """
  332. Set group to georectify. Create group if desired.
  333. """
  334. def __init__(self, wizard, parent):
  335. TitledPage.__init__(self, wizard, _("Select image/map group to georectify"))
  336. self.parent = parent
  337. self.grassdatabase = self.parent.grassdatabase
  338. self.groupList = []
  339. self.xylocation = ''
  340. self.xymapset = ''
  341. self.xygroup = ''
  342. # default extension
  343. self.extension = 'georect' + str(os.getpid())
  344. #
  345. # layout
  346. #
  347. self.sizer.AddGrowableCol(2)
  348. # group
  349. self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select group:')),
  350. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  351. pos=(1, 1))
  352. self.cb_group = wx.ComboBox(parent=self, id=wx.ID_ANY,
  353. choices=self.groupList, size=(350, -1),
  354. style=wx.CB_DROPDOWN | wx.CB_READONLY)
  355. self.sizer.Add(item=self.cb_group,
  356. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  357. pos=(1, 2))
  358. # create group
  359. self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Create group if none exists')),
  360. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  361. pos=(2, 1))
  362. self.btn_mkgroup = wx.Button(parent=self, id=wx.ID_ANY, label=_("Create/edit group..."))
  363. self.sizer.Add(item=self.btn_mkgroup,
  364. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  365. pos=(2, 2))
  366. # extension
  367. self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Extension for output maps:')),
  368. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  369. pos=(3, 1))
  370. self.ext_txt = wx.TextCtrl(parent=self, id=wx.ID_ANY, value="", size=(350,-1))
  371. self.ext_txt.SetValue(self.extension)
  372. self.sizer.Add(item=self.ext_txt,
  373. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  374. pos=(3, 2))
  375. #
  376. # bindings
  377. #
  378. self.Bind(wx.EVT_COMBOBOX, self.OnGroup, self.cb_group)
  379. self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup)
  380. self.Bind(wx.EVT_TEXT, self.OnExtension, self.ext_txt)
  381. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  382. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  383. self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
  384. def OnGroup(self, event):
  385. self.xygroup = event.GetString()
  386. def OnMkGroup(self, event):
  387. """Create new group in source location/mapset"""
  388. global maptype
  389. # open dialog
  390. if maptype == 'cell':
  391. menuform.GUI().ParseCommand(['i.group'],
  392. completed=(self.GetOptData, None, ''),
  393. parentframe=self.parent.parent, modal=True)
  394. elif maptype == 'vector':
  395. dlg = VectGroup(self, wx.ID_ANY, self.grassdatabase, self.xylocation, self.xymapset, self.xygroup)
  396. if dlg.ShowModal() == wx.ID_OK:
  397. dlg.MakeVGroup()
  398. self.OnEnterPage()
  399. def GetOptData(self, dcmd, layer, params, propwin):
  400. """Process i.group"""
  401. # update the page
  402. if dcmd:
  403. gcmd.Command(dcmd, stderr=None)
  404. self.OnEnterPage()
  405. self.Update()
  406. def OnExtension(self, event):
  407. self.extension = event.GetString()
  408. def OnPageChanging(self, event=None):
  409. if event.GetDirection() and self.xygroup == '':
  410. wx.MessageBox(_('You must select a valid image/map group in order to continue'))
  411. event.Veto()
  412. return
  413. if event.GetDirection() and self.extension == '':
  414. wx.MessageBox(_('You must enter an map name extension in order to continue'))
  415. event.Veto()
  416. return
  417. def OnEnterPage(self, event=None):
  418. self.groupList = []
  419. tmplist = []
  420. self.xylocation = self.parent.gisrc_dict['LOCATION_NAME']
  421. self.xymapset = self.parent.gisrc_dict['MAPSET']
  422. # create a list of groups in selected mapset
  423. if os.path.isdir(os.path.join(self.grassdatabase,self.xylocation,self.xymapset,'group')):
  424. tmplist = os.listdir(os.path.join(self.grassdatabase, self.xylocation, self.xymapset, 'group'))
  425. else:
  426. tmplist = []
  427. # if (event and event.GetDirection()) and self.xygroup == '':
  428. # if tmplist == []:
  429. # wx.MessageBox(_('No map/imagery groups exist to georectify. '
  430. # 'You will need to create one.'))
  431. # else:
  432. for item in tmplist:
  433. if os.path.isdir(os.path.join(self.grassdatabase, self.xylocation, self.xymapset, 'group', item)):
  434. self.groupList.append(item)
  435. utils.ListSortLower(self.groupList)
  436. self.cb_group.SetItems(self.groupList)
  437. if len(self.groupList) > 0:
  438. self.cb_group.SetSelection(0)
  439. self.xygroup = self.groupList[0]
  440. if self.xygroup == '' or self.extension == '':
  441. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  442. else:
  443. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  444. # switch to source
  445. self.parent.SwitchEnv('new')
  446. class DispMapPage(TitledPage):
  447. """
  448. Select ungeoreferenced map to display for interactively
  449. setting ground control points (GCPs).
  450. """
  451. def __init__(self, wizard, parent):
  452. TitledPage.__init__(self, wizard, _("Select image/map to display for ground control point (GCP) creation"))
  453. self.parent = parent
  454. global maptype
  455. #
  456. # layout
  457. #
  458. self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select display image/map:')),
  459. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  460. pos=(1, 1))
  461. self.selection = gselect.Select(self, id=wx.ID_ANY, size=globalvar.DIALOG_GSELECT_SIZE,
  462. type=maptype)
  463. self.sizer.Add(item=self.selection,
  464. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
  465. pos=(1, 2))
  466. #
  467. # bindings
  468. #
  469. self.selection.Bind(wx.EVT_TEXT, self.OnSelection)
  470. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  471. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  472. self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
  473. def OnSelection(self,event):
  474. """Map to display selected"""
  475. global xy_map
  476. xy_map = event.GetString()
  477. if xy_map == '':
  478. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  479. else:
  480. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  481. def OnPageChanging(self, event=None):
  482. global xy_map
  483. if event.GetDirection() and xy_map == '':
  484. wx.MessageBox(_('You must select a valid image/map in order to continue'))
  485. event.Veto()
  486. return
  487. self.parent.SwitchEnv('original')
  488. def OnEnterPage(self, event=None):
  489. global maptype
  490. global xy_map
  491. self.selection.SetElementList(maptype)
  492. if xy_map == '':
  493. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  494. else:
  495. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  496. class GCP(wx.Frame):
  497. """
  498. Manages ground control points for georectifying. Calculates RMS statics.
  499. Calls i.rectify or v.transform to georectify map.
  500. """
  501. def __init__(self, parent, grwiz, mapdisp=None, id=wx.ID_ANY,
  502. title=_("Create & manage ground control points"),
  503. size=wx.DefaultSize):
  504. wx.Frame.__init__(self, parent, id, title, size=(625, 300))
  505. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
  506. #
  507. # init variables
  508. #
  509. self.parent = parent # GMFrame
  510. self.parent.georectifying = self
  511. self.mapdisp = mapdisp # XY-location Map Display
  512. self.grwiz = grwiz # GR Wizard
  513. self.grassdatabase = self.grwiz.grassdatabase
  514. self.currentlocation = self.grwiz.currentlocation
  515. self.currentmapset = self.grwiz.currentmapset
  516. self.newlocation = self.grwiz.newlocation
  517. self.newmapset = self.grwiz.newmapset
  518. self.xylocation = self.grwiz.gisrc_dict['LOCATION_NAME']
  519. self.xymapset = self.grwiz.gisrc_dict['MAPSET']
  520. self.xygroup = self.grwiz.grouppage.xygroup
  521. self.extension = self.grwiz.grouppage.extension
  522. self.file = {
  523. 'points' : os.path.join(self.grassdatabase,
  524. self.xylocation,
  525. self.xymapset,
  526. 'group',
  527. self.xygroup,
  528. 'POINTS'),
  529. 'rgrp' : os.path.join(self.grassdatabase,
  530. self.xylocation,
  531. self.xymapset,
  532. 'group',
  533. self.xygroup,
  534. 'REF'),
  535. 'vgrp' : os.path.join(self.grassdatabase,
  536. self.xylocation,
  537. self.xymapset,
  538. 'group',
  539. self.xygroup,
  540. 'VREF'),
  541. 'target' : os.path.join(self.grassdatabase,
  542. self.xylocation,
  543. self.xymapset,
  544. 'group',
  545. self.xygroup,
  546. 'TARGET'),
  547. }
  548. # polynomial order transformation for georectification
  549. self.gr_order = 1
  550. # number of GCPs selected to be used for georectification (checked)
  551. self.GCPcount = 0
  552. # forward RMS error
  553. self.fwd_rmserror = 0.0
  554. # backward RMS error
  555. self.bkw_rmserror = 0.0
  556. # list map coords and ID of map display they came from
  557. self.mapcoordlist = []
  558. self.SetTarget(self.xygroup, self.currentlocation, self.currentmapset)
  559. #
  560. # toolbar and display for xy map
  561. #
  562. self.toolbar = toolbars.GCPToolbar(parent=self, tbframe=self).GetToolbar()
  563. self.SetToolBar(self.toolbar)
  564. self.SetMapDisplay(self.mapdisp)
  565. #
  566. # statusbar
  567. #
  568. self.CreateStatusBar(number=1)
  569. # can put guage into custom statusbar for progress if can figure out how to get progress text from i.rectify
  570. # self.gr_gauge = wx.Gauge(self, -1, 100, (-1,-1), (100, 25))
  571. # self.gr_guage.Pulse()
  572. panel = wx.Panel(parent=self)
  573. #
  574. # do layout
  575. #
  576. sizer = wx.BoxSizer(wx.VERTICAL)
  577. self.rb_grmethod = wx.RadioBox(parent=panel, id=wx.ID_ANY,
  578. label=" %s " % _("Select rectification method for rasters"),
  579. choices=[_('1st order'), _('2nd order'), _('3rd order')],
  580. majorDimension=wx.RA_SPECIFY_COLS)
  581. sizer.Add(item=self.rb_grmethod, proportion=0,
  582. flag=wx.EXPAND | wx.ALL, border=5)
  583. self.clip_to_region = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  584. label=_("clip to computational region in target location"))
  585. sizer.Add(item=self.clip_to_region, proportion=0,
  586. flag=wx.EXPAND | wx.ALL, border=5)
  587. box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
  588. label=" %s " % _("Ground Control Points"))
  589. boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  590. # initialize list control for GCP management
  591. self.list = GCPList(parent=panel, gcp=self)
  592. boxSizer.Add(item=self.list, proportion=1,
  593. flag=wx.EXPAND | wx.ALL, border=3)
  594. sizer.Add(item=boxSizer, proportion=1,
  595. flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  596. #
  597. # bindigs
  598. #
  599. self.Bind(wx.EVT_RADIOBOX, self.OnGRMethod, self.rb_grmethod)
  600. self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
  601. self.Bind(wx.EVT_CLOSE, self.OnQuit)
  602. panel.SetSizer(sizer)
  603. # sizer.Fit(self)
  604. def __del__(self):
  605. """Disable georectification mode"""
  606. self.parent.georectifying = None
  607. def SetMapDisplay(self, win):
  608. self.mapdisp = win
  609. if self.mapdisp:
  610. self.list.LoadData()
  611. def SetTarget(self, tgroup, tlocation, tmapset):
  612. """
  613. Sets rectification target to current location and mapset
  614. """
  615. # check to see if we are georectifying map in current working location/mapset
  616. if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
  617. gcmd.RunCommand('i.target',
  618. parent = self,
  619. flags = 'c',
  620. group = tgroup)
  621. else:
  622. self.grwiz.SwitchEnv('new')
  623. gcmd.RunCommand('i.target',
  624. parent = self,
  625. group = tgroup,
  626. location = tlocation,
  627. mapset = tmapset)
  628. self.grwiz.SwitchEnv('original')
  629. def AddGCP(self, event):
  630. """
  631. Appends an item to GCP list
  632. """
  633. self.list.AddGCPItem()
  634. # x, y, MapWindow instance
  635. self.mapcoordlist.append({ 'gcpcoord' : (0.0, 0.0, None),
  636. 'mapcoord' : (0.0, 0.0, None) })
  637. def DeleteGCP(self, event):
  638. """
  639. Deletes selected item in GCP list
  640. """
  641. minNumOfItems = self.OnGRMethod(None)
  642. if self.list.GetItemCount() <= minNumOfItems:
  643. wx.MessageBox(parent=self, message=_("At least %d GCPs required. Operation cancelled.") % minNumOfItems,
  644. caption=_("Delete GCP"), style=wx.OK | wx.ICON_INFORMATION)
  645. return
  646. item = self.list.DeleteGCPItem()
  647. del self.mapcoordlist[item]
  648. def ClearGCP(self, event):
  649. """
  650. Clears all values in selected item of GCP list and unchecks it
  651. """
  652. index = self.list.GetSelected()
  653. for i in range(4):
  654. self.list.SetStringItem(index, i, '0.0')
  655. self.list.SetStringItem(index, 4, '')
  656. self.list.SetStringItem(index, 5, '')
  657. self.list.CheckItem(index, False)
  658. self.mapcoordlist[index] = { 'gcpcoord' : (0.0, 0.0, None),
  659. 'mapcoord' : (0.0, 0.0, None) }
  660. def DrawGCP(self, coordtype):
  661. """
  662. Updates GCP and map coord maps and redraws
  663. active (checked) GCP markers
  664. """
  665. col = UserSettings.Get(group='georect', key='symbol', subkey='color')
  666. wxCol = wx.Colour(col[0], col[1], col[2], 255)
  667. wpx = UserSettings.Get(group='georect', key='symbol', subkey='width')
  668. font = self.GetFont()
  669. idx = 0
  670. for gcp in self.mapcoordlist:
  671. mapWin = gcp[coordtype][2]
  672. if not self.list.IsChecked(idx) or not mapWin:
  673. idx += 1
  674. continue
  675. mapWin.pen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID)
  676. mapWin.polypen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID) # ?
  677. coord = mapWin.Cell2Pixel((gcp[coordtype][0], gcp[coordtype][1]))
  678. mapWin.DrawCross(pdc=mapWin.pdcTmp, coords=coord,
  679. size=5, text={ 'text' : '%s' % str(idx + 1),
  680. 'active' : True,
  681. 'font' : font,
  682. 'color': wxCol,
  683. 'coords': [coord[0] + 5,
  684. coord[1] + 5,
  685. 5,
  686. 5]})
  687. idx += 1
  688. def SetGCPData(self, coordtype, coord, mapdisp=None, check=True):
  689. """
  690. Inserts coordinates from mouse click on map
  691. into selected item of GCP list and checks it for use
  692. """
  693. index = self.list.GetSelected()
  694. if index == wx.NOT_FOUND:
  695. return
  696. coord0 = str(coord[0])
  697. coord1 = str(coord[1])
  698. if coordtype == 'gcpcoord':
  699. self.list.SetStringItem(index, 0, coord0)
  700. self.list.SetStringItem(index, 1, coord1)
  701. self.mapcoordlist[index]['gcpcoord'] = (coord[0], coord[1], mapdisp)
  702. elif coordtype == 'mapcoord':
  703. self.list.SetStringItem(index, 2, coord0)
  704. self.list.SetStringItem(index, 3, coord1)
  705. self.mapcoordlist[index][coordtype] = (coord[0], coord[1], mapdisp)
  706. self.list.CheckItem(index, check)
  707. # self.list.ResizeColumns()
  708. def SaveGCPs(self, event):
  709. """
  710. Make a POINTS file or save GCP coordinates to existing POINTS file
  711. """
  712. self.GCPcount = 0
  713. try:
  714. f = open(self.file['points'], mode='w')
  715. # use os.linesep or '\n' here ???
  716. f.write('# Ground Control Points File\n')
  717. f.write("# \n")
  718. f.write("# target location: " + self.currentlocation + '\n')
  719. f.write("# target mapset: " + self.currentmapset + '\n')
  720. f.write("#unrectified xy georectified east north 1=use gcp point\n")
  721. f.write("#-------------- ----------------------- ---------------\n")
  722. for index in range(self.list.GetItemCount()):
  723. if self.list.IsChecked(index) == True:
  724. check = "1"
  725. self.GCPcount += 1
  726. else:
  727. check = "0"
  728. coord0 = self.list.GetItem(index, 0).GetText()
  729. coord1 = self.list.GetItem(index, 1).GetText()
  730. coord2 = self.list.GetItem(index, 2).GetText()
  731. coord3 = self.list.GetItem(index, 3).GetText()
  732. f.write(coord0 + ' ' + coord1 + ' ' + coord2 + ' ' + coord3 + ' ' + check + '\n')
  733. self.parent.goutput.WriteLog(_('POINTS file <%s> saved') % self.file['points'])
  734. self.SetStatusText(_('POINTS file saved'))
  735. except IOError, err:
  736. wx.MessageBox(parent=self,
  737. message="%s <%s>. %s%s" % (_("Writing POINTS file failed"),
  738. self.file['points'], os.linesep, err),
  739. caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
  740. return
  741. f.close()
  742. def ReadGCPs(self):
  743. """
  744. Reads GCPs and georectified coordinates from POINTS file
  745. """
  746. self.GCPcount = 0
  747. sourceMapWin = self.mapdisp.MapWindow
  748. targetMapWin = self.parent.curr_page.maptree.mapdisplay.MapWindow
  749. try:
  750. f = open(self.file['points'], 'r')
  751. GCPcnt = 0
  752. for line in f.readlines():
  753. if line[0] == '#' or line =='':
  754. continue
  755. line = line.replace('\n', '').strip()
  756. coords = map(float, line.split())
  757. if coords[4] == 1:
  758. check = True
  759. self.GCPcount +=1
  760. else:
  761. check = False
  762. index = self.AddGCP(event=None)
  763. self.SetGCPData('gcpcoord', (coords[0], coords[1]), sourceMapWin, check)
  764. self.SetGCPData('mapcoord', (coords[2], coords[3]), targetMapWin, check)
  765. except IOError, err:
  766. wx.MessageBox(parent=self,
  767. message="%s <%s>. %s%s" % (_("Reading POINTS file failed"),
  768. self.file['points'], os.linesep, err),
  769. caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
  770. return
  771. f.close()
  772. #
  773. # draw GCPs (source and target)
  774. #
  775. sourceMapWin.UpdateMap(render=False, renderVector=False)
  776. if targetMapWin:
  777. targetMapWin.UpdateMap(render=False, renderVector=False)
  778. #
  779. # calculate RMS
  780. #
  781. # FIXME auto calculation on load is not working
  782. #if self.CheckGCPcount():
  783. # self.RMSError(self.xygroup, self.gr_order)
  784. def ReloadGCPs(self, event):
  785. """Reload data from file"""
  786. self.list.LoadData()
  787. def OnFocus(self, event):
  788. # self.grwiz.SwitchEnv('new')
  789. pass
  790. def OnRMS(self, event):
  791. """
  792. RMS button handler
  793. """
  794. self.RMSError(self.xygroup,self.gr_order)
  795. def CheckGCPcount(self, msg=False):
  796. """
  797. Checks to make sure that the minimum number of GCPs have been defined and
  798. are active for the selected transformation order
  799. """
  800. if (self.GCPcount < 3 and self.gr_order == 1) or \
  801. (self.GCPcount < 6 and self.gr_order == 2) or \
  802. (self.GCPcount < 10 and self.gr_order == 3):
  803. if msg:
  804. wx.MessageBox(parent=self,
  805. caption=_("RMS Error"),
  806. message=_('Insufficient points defined and active (checked) '
  807. 'for selected rectification method.\n'
  808. '3+ points needed for 1st order,\n'
  809. '6+ points for 2nd order, and\n'
  810. '10+ points for 3rd order.'),
  811. style=wx.ICON_INFORMATION | wx.ID_OK | wx.CENTRE)
  812. return False
  813. else:
  814. return True
  815. def OnGeorect(self, event):
  816. """
  817. Georectifies map(s) in group using i.rectify or v.transform
  818. """
  819. global maptype
  820. self.SaveGCPs(None)
  821. if self.CheckGCPcount(msg=True) == False:
  822. return
  823. if maptype == 'cell':
  824. self.grwiz.SwitchEnv('new')
  825. cmdlist = ['i.rectify','-a','group=%s' % self.xygroup,
  826. 'extension=%s' % self.extension,'order=%s' % self.gr_order]
  827. if self.clip_to_region:
  828. cmdlist.append('-c')
  829. self.parent.goutput.RunCmd(cmdlist, compReg=False,
  830. switchPage=True)
  831. time.sleep(.1)
  832. self.grwiz.SwitchEnv('original')
  833. elif maptype == 'vector':
  834. # loop through all vectors in VREF and move resulting vector to target location
  835. f = open(self.vgrpfile)
  836. vectlist = []
  837. try:
  838. for vect in f:
  839. vect = vect.strip(' \n')
  840. vectlist.append(vect)
  841. finally:
  842. f.close()
  843. for vect in vectlist:
  844. outname = vect+'_'+self.extension
  845. p = gcmd.RunCommand('v.transform',
  846. parent = self,
  847. quiet = True,
  848. input = vect,
  849. output = outname,
  850. pointsfile = self.pointsfile)
  851. if p == 0:
  852. wx.MessageBox("All maps were georectified successfully")
  853. for vect in vectlist:
  854. outname = vect+'_'+self.extension
  855. xyvpath = os.path.join(self.grassdatabase,self.xylocation,self.xymapset,'vector',outname)
  856. vpath = os.path.join(self.grassdatabase,self.currentlocation,self.currentmapset,'vector',outname)
  857. if os.path.isfile(vpath):
  858. wx.MessageBox("%s already exists. Change extension name and georectify again" % outname)
  859. else:
  860. shutil.move(xyvpath, vpath)
  861. wx.MessageBox('For vector files with attribute tables, you will need to manually copy the tables to the new location')
  862. else:
  863. wx.MessageBox('Some maps were not georectified successfully')
  864. else:
  865. return
  866. def OnSettings(self, event):
  867. """Georectifier settings"""
  868. dlg = GrSettingsDialog(parent=self, id=wx.ID_ANY, title=_('Georectifier settings'))
  869. if dlg.ShowModal() == wx.ID_OK:
  870. pass
  871. dlg.Destroy()
  872. def OnQuit(self, event):
  873. """Quit georectifier"""
  874. self.grwiz.Cleanup()
  875. self.Destroy()
  876. event.Skip()
  877. def OnGRMethod(self, event):
  878. """
  879. sets transformation order for georectifying
  880. """
  881. if event:
  882. self.gr_order = event.GetInt() + 1
  883. numOfItems = self.list.GetItemCount()
  884. minNumOfItems = numOfItems
  885. if self.gr_order == 1:
  886. minNumOfItems = 3
  887. # self.SetStatusText(_('Insufficient points, 3+ points needed for 1st order'))
  888. elif self.gr_order == 2:
  889. minNumOfItems = 6
  890. diff = 6 - numOfItems
  891. # self.SetStatusText(_('Insufficient points, 6+ points needed for 2nd order'))
  892. elif self.gr_order == 3:
  893. minNumOfItems = 10
  894. # self.SetStatusText(_('Insufficient points, 10+ points needed for 3rd order'))
  895. for i in range(minNumOfItems - numOfItems):
  896. self.AddGCP(None)
  897. return minNumOfItems
  898. def RMSError(self, xygroup, order):
  899. """
  900. Uses g.transform to calculate forward and backward error for each used GCP
  901. in POINTS file and insert error values into GCP list.
  902. Calculates total forward and backward RMS error for all used points
  903. """
  904. # save GCPs to points file to make sure that all checked GCPs are used
  905. self.SaveGCPs(None)
  906. if self.CheckGCPcount(msg=True) == False:
  907. return
  908. # get list of forward and reverse rms error values for each point
  909. self.grwiz.SwitchEnv('new')
  910. ret = gcmd.RunCommand('g.transform',
  911. parent = self,
  912. read = True,
  913. group = xygroup,
  914. order = order)
  915. self.grwiz.SwitchEnv('original')
  916. if ret:
  917. errlist = ret.splitlines()
  918. if errlist == []:
  919. return
  920. # insert error values into GCP list for checked items
  921. i = 0
  922. sumsq_fwd_err = 0.0
  923. sumsq_bkw_err = 0.0
  924. for index in range(self.list.GetItemCount()):
  925. if self.list.IsChecked(index):
  926. fwd_err, bkw_err = errlist[i].split()
  927. self.list.SetStringItem(index, 4, fwd_err)
  928. self.list.SetStringItem(index, 5, bkw_err)
  929. sumsq_fwd_err += float(fwd_err)**2
  930. sumsq_bkw_err += float(bkw_err)**2
  931. i += 1
  932. else:
  933. self.list.SetStringItem(index, 4, '')
  934. self.list.SetStringItem(index, 5, '')
  935. # calculate RMS error
  936. self.fwd_rmserror = round((sumsq_fwd_err/i)**0.5,4)
  937. self.bkw_rmserror = round((sumsq_bkw_err/i)**0.5,4)
  938. self.list.ResizeColumns()
  939. self.SetStatusText(_('RMS error for selected points forward: %(fwd)s backward: %(bkw)s') % \
  940. { 'fwd' : self.fwd_rmserror, 'bkw' : self.bkw_rmserror })
  941. class GCPList(wx.ListCtrl,
  942. CheckListCtrlMixin,
  943. ListCtrlAutoWidthMixin):
  944. def __init__(self, parent, gcp, id=wx.ID_ANY,
  945. pos=wx.DefaultPosition, size=wx.DefaultSize,
  946. style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES |
  947. wx.LC_SINGLE_SEL):
  948. wx.ListCtrl.__init__(self, parent, id, pos, size, style)
  949. # Mixin settings
  950. CheckListCtrlMixin.__init__(self)
  951. ListCtrlAutoWidthMixin.__init__(self)
  952. # TextEditMixin.__init__(self)
  953. self.gcp = gcp # GCP class
  954. # tracks whether list items are checked or not
  955. self.CheckList = []
  956. self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
  957. self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
  958. self._Create()
  959. self.selected = wx.NOT_FOUND
  960. def _Create(self):
  961. idx_col = 0
  962. for col in (_('use| X coord'),
  963. _('Y coord'),
  964. _('E coord'),
  965. _('N coord'),
  966. _('Forward error'),
  967. _('Backward error')):
  968. self.InsertColumn(idx_col, col)
  969. idx_col += 1
  970. def LoadData(self):
  971. """Load data into list"""
  972. self.DeleteAllItems()
  973. if os.path.isfile(self.gcp.file['points']):
  974. self.gcp.ReadGCPs()
  975. else:
  976. # 3 gcp is minimum
  977. for i in range(3):
  978. self.gcp.AddGCP(None)
  979. # select first point by default
  980. self.selected = 0
  981. self.SetItemState(self.selected,
  982. wx.LIST_STATE_SELECTED,
  983. wx.LIST_STATE_SELECTED)
  984. self.ResizeColumns()
  985. def OnCheckItem(self, index, flag):
  986. """Item is checked/unchecked"""
  987. pass
  988. def AddGCPItem(self):
  989. """
  990. Appends an item to GCP list
  991. """
  992. self.Append(['0.0',
  993. '0.0',
  994. '0.0',
  995. '0.0',
  996. '',
  997. ''])
  998. self.selected = self.GetItemCount() - 1
  999. self.SetItemState(self.selected,
  1000. wx.LIST_STATE_SELECTED,
  1001. wx.LIST_STATE_SELECTED)
  1002. self.ResizeColumns()
  1003. return self.selected
  1004. def DeleteGCPItem(self):
  1005. """
  1006. Deletes selected item in GCP list
  1007. """
  1008. if self.selected == wx.NOT_FOUND:
  1009. return
  1010. self.DeleteItem(self.selected)
  1011. if self.GetItemCount() > 0:
  1012. self.selected = self.GetItemCount() - 1
  1013. self.SetItemState(self.selected,
  1014. wx.LIST_STATE_SELECTED,
  1015. wx.LIST_STATE_SELECTED)
  1016. else:
  1017. self.selected = wx.NOT_FOUND
  1018. return self.selected
  1019. def ResizeColumns(self):
  1020. """Resize columns"""
  1021. minWidth = 90
  1022. for i in range(self.GetColumnCount()):
  1023. self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
  1024. if self.GetColumnWidth(i) < minWidth:
  1025. self.SetColumnWidth(i, minWidth)
  1026. self.SendSizeEvent()
  1027. def GetSelected(self):
  1028. """Get index of selected item"""
  1029. return self.selected
  1030. def OnItemSelected(self, event):
  1031. self.selected = event.GetIndex()
  1032. def OnItemActivated(self, event):
  1033. """
  1034. When item double clicked, open editor to update coordinate values
  1035. """
  1036. coords = []
  1037. index = event.GetIndex()
  1038. for i in range(4):
  1039. coords.append(self.GetItem(index, i).GetText())
  1040. dlg = EditGPC(parent=self, id=wx.ID_ANY, data=coords)
  1041. if dlg.ShowModal() == wx.ID_OK:
  1042. values = dlg.GetValues() # string
  1043. if len(values) == 0:
  1044. wx.MessageBox(parent=self,
  1045. caption=_("Edit GCP"),
  1046. message=_("Invalid coordinate value. Operation cancelled."),
  1047. style=wx.CENTRE | wx.ICON_ERROR | wx.ID_OK)
  1048. else:
  1049. for i in range(len(values)):
  1050. if values[i] != coords[i]:
  1051. self.SetStringItem(index, i, values[i])
  1052. mapdisp = self.gcp.mapcoordlist[index]['gcpcoord'][2]
  1053. self.gcp.mapcoordlist[index]['gcpcoord'] = (float(values[0]), float(values[1]), mapdisp)
  1054. mapdisp = self.gcp.mapcoordlist[index]['mapcoord'][2]
  1055. self.gcp.mapcoordlist[index]['mapcoord'] = (float(values[0]), float(values[1]), mapdisp)
  1056. class VectGroup(wx.Dialog):
  1057. """
  1058. Dialog to create a vector group (VREF file) for georectifying
  1059. """
  1060. def __init__(self, parent, id, grassdb, location, mapset, group,
  1061. style=wx.DEFAULT_DIALOG_STYLE):
  1062. wx.Dialog.__init__(self, parent, id, style=style)
  1063. self.grassdatabase = grassdb
  1064. self.xylocation = location
  1065. self.xymapset = mapset
  1066. self.xygroup = group
  1067. # get list of valid vector directories
  1068. vectlist = os.listdir(os.path.join(self.grassdatabase,self.xylocation,self.xymapset,'vector'))
  1069. for dir in vectlist:
  1070. if os.path.isfile(os.path.join(self.grassdatabase,self.xylocation,self.xymapset,'vector',dir,'coor')):
  1071. pass
  1072. else:
  1073. vectlist.remove(dir)
  1074. self.vgrouplist = []
  1075. self.vgrpfile = os.path.join(self.grassdatabase,self.xylocation,self.xymapset,'group',self.xygroup,'VREF')
  1076. if os.path.isfile(self.vgrpfile):
  1077. f = open(self.vgrpfile)
  1078. try:
  1079. for line in f:
  1080. if line != '':
  1081. self.vgrouplist.append(line.strip(' \n'))
  1082. finally:
  1083. f.close()
  1084. self.btnCancel = wx.Button(self, wx.ID_CANCEL)
  1085. self.btnSubmit = wx.Button(self, wx.ID_OK)
  1086. self.btnSubmit.SetDefault()
  1087. sizer = wx.BoxSizer(wx.VERTICAL)
  1088. box = wx.BoxSizer(wx.HORIZONTAL)
  1089. label = wx.StaticText(parent=self, id=wx.ID_ANY,
  1090. label='Select vector map(s) to add to group:')
  1091. box.Add(label, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, border=5)
  1092. self.addmap = wx.CheckListBox(self, -1, wx.DefaultPosition, wx.DefaultSize, vectlist)
  1093. box.Add(self.addmap, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT| wx.LEFT, border=5)
  1094. sizer.Add(box, flag=wx.ALIGN_RIGHT | wx.ALL, border=3)
  1095. box = wx.BoxSizer(wx.HORIZONTAL)
  1096. label = wx.StaticText(parent=self, id=wx.ID_ANY,
  1097. label='Select vector map(s) to remove from group:')
  1098. box.Add(label, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, border=5)
  1099. self.remmap = wx.CheckListBox(self, -1, wx.DefaultPosition, wx.DefaultSize, self.vgrouplist)
  1100. box.Add(self.remmap, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT| wx.LEFT, border=5)
  1101. sizer.Add(box, flag=wx.ALIGN_RIGHT | wx.ALL, border=3)
  1102. # buttons
  1103. btnSizer = wx.StdDialogButtonSizer()
  1104. btnSizer.AddButton(self.btnCancel)
  1105. btnSizer.AddButton(self.btnSubmit)
  1106. btnSizer.Realize()
  1107. sizer.Add(item=btnSizer, proportion=0,
  1108. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  1109. self.SetSizer(sizer)
  1110. sizer.Fit(self)
  1111. self.Layout()
  1112. self.Bind(wx.EVT_CHECKLISTBOX, self.AddVect, self.addmap)
  1113. self.Bind(wx.EVT_CHECKLISTBOX, self.RemoveVect, self.remmap)
  1114. def AddVect(self, event):
  1115. index = event.GetSelection()
  1116. label = self.addmap.GetString(index)
  1117. if self.addmap.IsChecked(index):
  1118. self.vgrouplist.append(label)
  1119. self.addmap.SetSelection(index)
  1120. event.Skip()
  1121. def RemoveVect(self, event):
  1122. index = event.GetSelection()
  1123. label = self.remmap.GetString(index)
  1124. if self.remmap.IsChecked(index):
  1125. self.vgrouplist.remove(label)
  1126. self.remmap.SetSelection(index)
  1127. event.Skip()
  1128. def MakeVGroup(self):
  1129. f = open(self.vgrpfile, mode='w')
  1130. for vect in self.vgrouplist:
  1131. f.write(vect+'\n')
  1132. class EditGPC(wx.Dialog):
  1133. def __init__(self, parent, data, id=wx.ID_ANY,
  1134. title=_("Edit GCP"),
  1135. style=wx.DEFAULT_DIALOG_STYLE):
  1136. """Dialog for editing GPC and map coordinates in list control"""
  1137. wx.Dialog.__init__(self, parent, id, title=title, style=style)
  1138. panel = wx.Panel(parent=self)
  1139. sizer = wx.BoxSizer(wx.VERTICAL)
  1140. box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
  1141. label=" %s " % _("Ground Control Point"))
  1142. boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1143. # source coordinates
  1144. gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
  1145. self.xcoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
  1146. self.ycoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
  1147. self.ncoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
  1148. self.ecoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
  1149. row = 0
  1150. col = 0
  1151. idx = 0
  1152. for label, win in ((_("X:"), self.xcoord),
  1153. (_("Y:"), self.ycoord),
  1154. (_("E:"), self.ecoord),
  1155. (_("N:"), self.ncoord)):
  1156. label = wx.StaticText(parent=panel, id=wx.ID_ANY,
  1157. label=label)
  1158. gridSizer.Add(item=label,
  1159. flag=wx.ALIGN_CENTER_VERTICAL,
  1160. pos=(row, col))
  1161. col += 1
  1162. win.SetValue(str(data[idx]))
  1163. gridSizer.Add(item=win,
  1164. pos=(row, col))
  1165. col += 1
  1166. idx += 1
  1167. if col > 3:
  1168. row += 1
  1169. col = 0
  1170. boxSizer.Add(item=gridSizer, proportion=1,
  1171. flag=wx.EXPAND | wx.ALL, border=5)
  1172. sizer.Add(item=boxSizer, proportion=1,
  1173. flag=wx.EXPAND | wx.ALL, border=5)
  1174. #
  1175. # buttons
  1176. #
  1177. self.btnCancel = wx.Button(panel, wx.ID_CANCEL)
  1178. self.btnOk = wx.Button(panel, wx.ID_OK)
  1179. self.btnOk.SetDefault()
  1180. btnSizer = wx.StdDialogButtonSizer()
  1181. btnSizer.AddButton(self.btnCancel)
  1182. btnSizer.AddButton(self.btnOk)
  1183. btnSizer.Realize()
  1184. sizer.Add(item=btnSizer, proportion=0,
  1185. flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
  1186. panel.SetSizer(sizer)
  1187. sizer.Fit(self)
  1188. def GetValues(self, columns=None):
  1189. """Return list of values (as strings).
  1190. """
  1191. valuelist = []
  1192. try:
  1193. float(self.xcoord.GetValue())
  1194. float(self.ycoord.GetValue())
  1195. float(self.ecoord.GetValue())
  1196. float(self.ncoord.GetValue())
  1197. except ValueError:
  1198. return valuelist
  1199. valuelist.append(self.xcoord.GetValue())
  1200. valuelist.append(self.ycoord.GetValue())
  1201. valuelist.append(self.ecoord.GetValue())
  1202. valuelist.append(self.ncoord.GetValue())
  1203. return valuelist
  1204. class GrSettingsDialog(wx.Dialog):
  1205. def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
  1206. style=wx.DEFAULT_DIALOG_STYLE):
  1207. wx.Dialog.__init__(self, parent, id, title, pos, size, style)
  1208. """
  1209. Dialog to set profile text options: font, title
  1210. and font size, axis labels and font size
  1211. """
  1212. #
  1213. # initialize variables
  1214. #
  1215. self.parent = parent
  1216. self.symbol = {}
  1217. self._do_layout()
  1218. def _do_layout(self):
  1219. """Do layout"""
  1220. # dialog layout
  1221. sizer = wx.BoxSizer(wx.VERTICAL)
  1222. box = wx.StaticBox(parent=self, id=wx.ID_ANY,
  1223. label=" %s " % _("Symbol settings"))
  1224. boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1225. gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
  1226. gridSizer.AddGrowableCol(1)
  1227. #
  1228. # symbol color
  1229. #
  1230. row = 0
  1231. label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Color:"))
  1232. gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
  1233. col = UserSettings.Get(group='georect', key='symbol', subkey='color')
  1234. colWin = csel.ColourSelect(parent=self, id=wx.ID_ANY,
  1235. colour=wx.Colour(col[0],
  1236. col[1],
  1237. col[2],
  1238. 255))
  1239. self.symbol['color'] = colWin.GetId()
  1240. gridSizer.Add(item=colWin,
  1241. flag=wx.ALIGN_RIGHT,
  1242. pos=(row, 1))
  1243. #
  1244. # symbol width
  1245. #
  1246. row += 1
  1247. label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Width:"))
  1248. gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
  1249. width = int(UserSettings.Get(group='georect', key='symbol', subkey='width'))
  1250. widWin = wx.SpinCtrl(parent=self, id=wx.ID_ANY,
  1251. min=1, max=10)
  1252. widWin.SetValue(width)
  1253. self.symbol['width'] = widWin.GetId()
  1254. gridSizer.Add(item=widWin,
  1255. flag=wx.ALIGN_RIGHT,
  1256. pos=(row, 1))
  1257. boxSizer.Add(item=gridSizer, flag=wx.EXPAND)
  1258. sizer.Add(item=boxSizer, flag=wx.EXPAND | wx.ALL, border=5)
  1259. line = wx.StaticLine(parent=self, id=wx.ID_ANY, size=(20, -1), style=wx.LI_HORIZONTAL)
  1260. sizer.Add(item=line, proportion=0,
  1261. flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border=3)
  1262. #
  1263. # buttons
  1264. #
  1265. btnSave = wx.Button(self, wx.ID_SAVE)
  1266. btnApply = wx.Button(self, wx.ID_APPLY)
  1267. btnCancel = wx.Button(self, wx.ID_CANCEL)
  1268. btnSave.SetDefault()
  1269. # bindigs
  1270. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  1271. btnApply.SetToolTipString(_("Apply changes for the current session"))
  1272. btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
  1273. btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
  1274. btnSave.SetDefault()
  1275. btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  1276. btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
  1277. # sizers
  1278. btnStdSizer = wx.StdDialogButtonSizer()
  1279. btnStdSizer.AddButton(btnCancel)
  1280. btnStdSizer.AddButton(btnSave)
  1281. btnStdSizer.AddButton(btnApply)
  1282. btnStdSizer.Realize()
  1283. sizer.Add(item=btnStdSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
  1284. self.SetSizer(sizer)
  1285. sizer.Fit(self)
  1286. def UpdateSettings(self):
  1287. UserSettings.Set(group='georect', key='symbol', subkey='color',
  1288. value=wx.FindWindowById(self.symbol['color']).GetColour())
  1289. UserSettings.Set(group='georect', key='symbol', subkey='width',
  1290. value=wx.FindWindowById(self.symbol['width']).GetValue())
  1291. def OnSave(self, event):
  1292. """Button 'Save' pressed"""
  1293. self.UpdateSettings()
  1294. fileSettings = {}
  1295. UserSettings.ReadSettingsFile(settings=fileSettings)
  1296. fileSettings['georect'] = UserSettings.Get(group='georect')
  1297. file = UserSettings.SaveToFile(fileSettings)
  1298. self.parent.parent.goutput.WriteLog(_('Georectifier settings saved to file \'%s\'.') % file)
  1299. self.Close()
  1300. def OnApply(self, event):
  1301. """Button 'Apply' pressed"""
  1302. self.UpdateSettings()
  1303. self.Close()
  1304. def OnCancel(self, event):
  1305. """Button 'Cancel' pressed"""
  1306. self.Close()