georect.py 56 KB

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