georect.py 63 KB

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