wizard.py 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149
  1. """!
  2. @package location_wizard.wizard
  3. @brief Location wizard - creates a new GRASS Location. User can choose
  4. from multiple methods.
  5. Classes:
  6. - wizard::TitledPage
  7. - wizard::DatabasePage
  8. - wizard::CoordinateSystemPage
  9. - wizard::ProjectionsPage
  10. - wizard::ItemList
  11. - wizard::ProjParamsPage
  12. - wizard::DatumPage
  13. - wizard::EllipsePage
  14. - wizard::GeoreferencedFilePage
  15. - wizard::WKTPage
  16. - wizard::EPSGPage
  17. - wizard::CustomPage
  18. - wizard::SummaryPage
  19. - wizard::LocationWizard
  20. - wizard::WizardWithHelpButton
  21. (C) 2007-2013 by the GRASS Development Team
  22. This program is free software under the GNU General Public License
  23. (>=v2). Read the file COPYING that comes with GRASS for details.
  24. @author Michael Barton
  25. @author Jachym Cepicky
  26. @author Martin Landa <landa.martin gmail.com>
  27. @author Hamish Bowman (planetary ellipsoids)
  28. """
  29. import os
  30. import locale
  31. import wx
  32. import wx.lib.mixins.listctrl as listmix
  33. import wx.wizard as wiz
  34. import wx.lib.scrolledpanel as scrolled
  35. from core import globalvar
  36. from core import utils
  37. from core.gcmd import RunCommand, GError, GMessage, GWarning
  38. from gui_core.widgets import GenericValidator
  39. from location_wizard.base import BaseClass
  40. from location_wizard.dialogs import SelectTransformDialog
  41. from grass.script import core as grass
  42. global coordsys
  43. global north
  44. global south
  45. global east
  46. global west
  47. global resolution
  48. global wizerror
  49. global translist
  50. class TitledPage(BaseClass, wiz.WizardPageSimple):
  51. """!Class to make wizard pages. Generic methods to make labels,
  52. text entries, and buttons.
  53. """
  54. def __init__(self, parent, title):
  55. self.page = wiz.WizardPageSimple.__init__(self, parent)
  56. # page title
  57. self.title = wx.StaticText(parent = self, id = wx.ID_ANY, label = title)
  58. self.title.SetFont(wx.Font(13, wx.SWISS, wx.NORMAL, wx.BOLD))
  59. # main sizers
  60. self.pagesizer = wx.BoxSizer(wx.VERTICAL)
  61. self.sizer = wx.GridBagSizer(vgap = 0, hgap = 0)
  62. def DoLayout(self):
  63. """!Do page layout"""
  64. self.pagesizer.Add(item = self.title, proportion = 0,
  65. flag = wx.ALIGN_CENTRE | wx.ALL,
  66. border = 5)
  67. self.pagesizer.Add(item = wx.StaticLine(self, -1), proportion = 0,
  68. flag = wx.EXPAND | wx.ALL,
  69. border = 0)
  70. self.pagesizer.Add(item = self.sizer, proportion = 1,
  71. flag = wx.EXPAND)
  72. self.SetAutoLayout(True)
  73. self.SetSizer(self.pagesizer)
  74. self.Layout()
  75. class DatabasePage(TitledPage):
  76. """!Wizard page for setting GIS data directory and location name"""
  77. def __init__(self, wizard, parent, grassdatabase):
  78. TitledPage.__init__(self, wizard, _("Define GRASS Database and Location Name"))
  79. self.grassdatabase = grassdatabase
  80. self.location = ''
  81. self.locTitle = ''
  82. # buttons
  83. self.bbrowse = self.MakeButton(_("Browse"))
  84. # text controls
  85. self.tgisdbase = self.MakeTextCtrl(grassdatabase, size = (300, -1))
  86. self.tlocation = self.MakeTextCtrl("newLocation", size = (300, -1))
  87. self.tlocation.SetValidator(GenericValidator(grass.legal_name, self._nameValidationFailed))
  88. self.tlocTitle = self.MakeTextCtrl(size = (400, -1))
  89. # layout
  90. self.sizer.Add(item = self.MakeLabel(_("GIS Data Directory:")),
  91. flag = wx.ALIGN_RIGHT |
  92. wx.ALIGN_CENTER_VERTICAL |
  93. wx.ALL, border = 5,
  94. pos = (1, 1))
  95. self.sizer.Add(item = self.tgisdbase,
  96. flag = wx.ALIGN_LEFT |
  97. wx.ALIGN_CENTER_VERTICAL |
  98. wx.ALL, border = 5,
  99. pos = (1, 2))
  100. self.sizer.Add(item = self.bbrowse,
  101. flag = wx.ALIGN_LEFT |
  102. wx.ALIGN_CENTER_VERTICAL |
  103. wx.ALL, border = 5,
  104. pos = (1, 3))
  105. self.sizer.Add(item = self.MakeLabel("%s:" % _("Project Location"),
  106. tooltip = _("Name of location directory in GIS Data Directory")),
  107. flag = wx.ALIGN_RIGHT |
  108. wx.ALIGN_CENTER_VERTICAL |
  109. wx.ALL, border = 5,
  110. pos = (2, 1))
  111. self.sizer.Add(item = self.tlocation,
  112. flag = wx.ALIGN_LEFT |
  113. wx.ALIGN_CENTER_VERTICAL |
  114. wx.ALL, border = 5,
  115. pos = (2, 2))
  116. self.sizer.Add(item = self.MakeLabel("%s:" % _("Optional Location Title"),
  117. tooltip = _("Optional location title, "
  118. "you can leave this field blank.")),
  119. flag = wx.ALIGN_RIGHT |
  120. wx.ALIGN_TOP | wx.ALIGN_CENTER_VERTICAL |
  121. wx.ALL, border = 5,
  122. pos = (3, 1))
  123. self.sizer.Add(item = self.tlocTitle,
  124. flag = wx.ALIGN_LEFT |
  125. wx.ALIGN_CENTER_VERTICAL |
  126. wx.ALL, border = 5,
  127. pos = (3, 2), span = (1, 2))
  128. self.sizer.AddGrowableCol(3)
  129. # bindings
  130. self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.bbrowse)
  131. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  132. self.tgisdbase.Bind(wx.EVT_TEXT, self.OnChangeName)
  133. self.tlocation.Bind(wx.EVT_TEXT, self.OnChangeName)
  134. def _nameValidationFailed(self, ctrl):
  135. message = _("Name <%(name)s> is not a valid name for location. "
  136. "Please use only ASCII characters excluding %(chars)s "
  137. "and space.") % {'name': ctrl.GetValue(), 'chars': '/"\'@,=*~'}
  138. GError(parent=self, message=message, caption=_("Invalid location name"))
  139. def OnChangeName(self, event):
  140. """!Name for new location was changed"""
  141. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  142. if len(event.GetString()) > 0:
  143. if not nextButton.IsEnabled():
  144. nextButton.Enable()
  145. else:
  146. nextButton.Disable()
  147. event.Skip()
  148. def OnBrowse(self, event):
  149. """!Choose GRASS data directory"""
  150. dlg = wx.DirDialog(self, _("Choose GRASS data directory:"),
  151. os.getcwd(), wx.DD_DEFAULT_STYLE)
  152. if dlg.ShowModal() == wx.ID_OK:
  153. self.grassdatabase = dlg.GetPath()
  154. self.tgisdbase.SetValue(self.grassdatabase)
  155. dlg.Destroy()
  156. def OnPageChanging(self, event = None):
  157. error = None
  158. if os.path.isdir(os.path.join(self.tgisdbase.GetValue(), self.tlocation.GetValue())):
  159. error = _("Location already exists in GRASS Database.")
  160. if error:
  161. GError(parent = self,
  162. message="%s <%s>.%s%s" % (_("Unable to create location"),
  163. str(self.tlocation.GetValue()),
  164. os.linesep,
  165. error))
  166. event.Veto()
  167. return
  168. self.location = self.tlocation.GetValue()
  169. self.grassdatabase = self.tgisdbase.GetValue()
  170. self.locTitle = self.tlocTitle.GetValue()
  171. if os.linesep in self.locTitle or \
  172. len(self.locTitle) > 255:
  173. GWarning(parent = self,
  174. message = _("Title of the location is limited only to one line and "
  175. "256 characters. The rest of the text will be ignored."))
  176. self.locTitle = self.locTitle.split(os.linesep)[0][:255]
  177. class CoordinateSystemPage(TitledPage):
  178. """!Wizard page for choosing method for location creation"""
  179. def __init__(self, wizard, parent):
  180. TitledPage.__init__(self, wizard, _("Choose method for creating a new location"))
  181. self.parent = parent
  182. global coordsys
  183. # toggles
  184. self.radio1 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  185. label = _("Select coordinate system parameters from a list"),
  186. style = wx.RB_GROUP)
  187. self.radio2 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  188. label = _("Select EPSG code of spatial reference system"))
  189. self.radio3 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  190. label = _("Read projection and datum terms from a "
  191. "georeferenced data file"))
  192. self.radio4 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  193. label = _("Read projection and datum terms from a "
  194. "WKT or PRJ file"))
  195. self.radio5 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  196. label = _("Specify projection and datum terms using custom "
  197. "PROJ.4 parameters"))
  198. self.radio6 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  199. label = _("Create a generic Cartesian coordinate system (XY)"))
  200. # layout
  201. self.sizer.SetVGap(10)
  202. self.sizer.Add(item = self.radio1,
  203. flag = wx.ALIGN_LEFT, pos = (1, 1))
  204. self.sizer.Add(item = self.radio2,
  205. flag = wx.ALIGN_LEFT, pos = (2, 1))
  206. self.sizer.Add(item = self.radio3,
  207. flag = wx.ALIGN_LEFT, pos = (3, 1))
  208. self.sizer.Add(item = self.radio4,
  209. flag = wx.ALIGN_LEFT, pos = (4, 1))
  210. self.sizer.Add(item = self.radio5,
  211. flag = wx.ALIGN_LEFT, pos = (5, 1))
  212. self.sizer.Add(item = self.radio6,
  213. flag = wx.ALIGN_LEFT, pos = (6, 1))
  214. self.sizer.AddGrowableCol(1)
  215. # bindings
  216. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio1.GetId())
  217. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio2.GetId())
  218. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio3.GetId())
  219. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio4.GetId())
  220. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio5.GetId())
  221. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio6.GetId())
  222. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  223. def OnEnterPage(self, event):
  224. global coordsys
  225. if not coordsys:
  226. coordsys = "proj"
  227. self.radio1.SetValue(True)
  228. else:
  229. if coordsys == 'proj':
  230. self.radio1.SetValue(True)
  231. if coordsys == "epsg":
  232. self.radio2.SetValue(True)
  233. if coordsys == "file":
  234. self.radio3.SetValue(True)
  235. if coordsys == "wkt":
  236. self.radio4.SetValue(True)
  237. if coordsys == "custom":
  238. self.radio5.SetValue(True)
  239. if coordsys == "xy":
  240. self.radio6.SetValue(True)
  241. if event.GetDirection():
  242. if coordsys == 'proj':
  243. self.SetNext(self.parent.projpage)
  244. self.parent.sumpage.SetPrev(self.parent.datumpage)
  245. if coordsys == "epsg":
  246. self.SetNext(self.parent.epsgpage)
  247. self.parent.sumpage.SetPrev(self.parent.epsgpage)
  248. if coordsys == "file":
  249. self.SetNext(self.parent.filepage)
  250. self.parent.sumpage.SetPrev(self.parent.filepage)
  251. if coordsys == "wkt":
  252. self.SetNext(self.parent.wktpage)
  253. self.parent.sumpage.SetPrev(self.parent.wktpage)
  254. if coordsys == "custom":
  255. self.SetNext(self.parent.custompage)
  256. self.parent.sumpage.SetPrev(self.parent.custompage)
  257. if coordsys == "xy":
  258. self.SetNext(self.parent.sumpage)
  259. self.parent.sumpage.SetPrev(self.parent.csystemspage)
  260. if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
  261. wx.FindWindowById(wx.ID_FORWARD).Enable()
  262. def SetVal(self, event):
  263. """!Choose method"""
  264. global coordsys
  265. if event.GetId() == self.radio1.GetId():
  266. coordsys = "proj"
  267. self.SetNext(self.parent.projpage)
  268. self.parent.sumpage.SetPrev(self.parent.datumpage)
  269. elif event.GetId() == self.radio2.GetId():
  270. coordsys = "epsg"
  271. self.SetNext(self.parent.epsgpage)
  272. self.parent.sumpage.SetPrev(self.parent.epsgpage)
  273. elif event.GetId() == self.radio3.GetId():
  274. coordsys = "file"
  275. self.SetNext(self.parent.filepage)
  276. self.parent.sumpage.SetPrev(self.parent.filepage)
  277. elif event.GetId() == self.radio4.GetId():
  278. coordsys = "wkt"
  279. self.SetNext(self.parent.wktpage)
  280. self.parent.sumpage.SetPrev(self.parent.wktpage)
  281. elif event.GetId() == self.radio5.GetId():
  282. coordsys = "custom"
  283. self.SetNext(self.parent.custompage)
  284. self.parent.sumpage.SetPrev(self.parent.custompage)
  285. elif event.GetId() == self.radio6.GetId():
  286. coordsys = "xy"
  287. self.SetNext(self.parent.sumpage)
  288. self.parent.sumpage.SetPrev(self.parent.csystemspage)
  289. class ProjectionsPage(TitledPage):
  290. """!Wizard page for selecting projection (select coordinate system option)"""
  291. def __init__(self, wizard, parent):
  292. TitledPage.__init__(self, wizard, _("Choose projection"))
  293. self.parent = parent
  294. self.proj = ''
  295. self.projdesc = ''
  296. self.p4proj = ''
  297. # text input
  298. self.tproj = self.MakeTextCtrl("", size = (200,-1))
  299. # search box
  300. self.searchb = wx.SearchCtrl(self, size = (200,-1),
  301. style = wx.TE_PROCESS_ENTER)
  302. # projection list
  303. self.projlist = ItemList(self, data = self.parent.projdesc.items(),
  304. columns = [_('Code'), _('Description')])
  305. self.projlist.resizeLastColumn(30)
  306. # layout
  307. self.sizer.AddGrowableCol(3)
  308. self.sizer.Add(item = self.MakeLabel(_("Projection code:")),
  309. flag = wx.ALIGN_LEFT |
  310. wx.ALIGN_CENTER_VERTICAL |
  311. wx.ALL, border = 5, pos = (1, 1))
  312. self.sizer.Add(item = self.tproj,
  313. flag = wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL,
  314. border = 5, pos = (1, 2))
  315. self.sizer.Add(item = self.MakeLabel(_("Search in description:")),
  316. flag = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  317. border = 5, pos = (2, 1))
  318. self.sizer.Add(item = self.searchb,
  319. flag = wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL,
  320. border = 5, pos = (2, 2))
  321. self.sizer.AddGrowableRow(3)
  322. self.sizer.Add(item = self.projlist,
  323. flag = wx.EXPAND |
  324. wx.ALIGN_LEFT |
  325. wx.ALL, border = 5, pos = (3, 1), span = (1, 3))
  326. # events
  327. self.tproj.Bind(wx.EVT_TEXT, self.OnText)
  328. self.tproj.Bind(wx.EVT_TEXT_ENTER, self.OnText)
  329. self.searchb.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
  330. self.projlist.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
  331. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  332. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  333. def OnPageChanging(self,event):
  334. if event.GetDirection() and self.proj not in self.parent.projections.keys():
  335. event.Veto()
  336. def OnText(self, event):
  337. """!Projection name changed"""
  338. self.proj = event.GetString().lower()
  339. self.p4proj = ''
  340. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  341. if self.proj not in self.parent.projections.keys() and nextButton.IsEnabled():
  342. nextButton.Enable(False)
  343. if self.proj in self.parent.projections.keys():
  344. if self.proj == 'stp':
  345. wx.MessageBox('Currently State Plane projections must be selected using the '
  346. 'text-based setup (g.setproj), or entered by EPSG code or '
  347. 'custom PROJ.4 terms.',
  348. 'Warning', wx.ICON_WARNING)
  349. self.proj = ''
  350. self.tproj.SetValue(self.proj)
  351. nextButton.Enable(False)
  352. return
  353. elif self.proj.lower() == 'll':
  354. self.p4proj = '+proj=longlat'
  355. else:
  356. self.p4proj = '+proj=' + self.proj.lower()
  357. self.projdesc = self.parent.projections[self.proj][0]
  358. nextButton.Enable()
  359. def OnEnterPage(self, event):
  360. if len(self.proj) == 0:
  361. # disable 'next' button by default
  362. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  363. else:
  364. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  365. event.Skip()
  366. def OnSearch(self, event):
  367. """!Search projection by desc"""
  368. str = event.GetString()
  369. try:
  370. self.proj, self.projdesc = self.projlist.Search(index = [0,1], pattern = event.GetString())
  371. except:
  372. self.proj = self.projdesc = ''
  373. event.Skip()
  374. def OnItemSelected(self, event):
  375. """!Projection selected"""
  376. index = event.m_itemIndex
  377. # set values
  378. self.proj = self.projlist.GetItem(index, 0).GetText().lower()
  379. self.tproj.SetValue(self.proj)
  380. event.Skip()
  381. class ItemList(wx.ListCtrl,
  382. listmix.ListCtrlAutoWidthMixin,
  383. listmix.ColumnSorterMixin):
  384. """!Generic list (for projections, ellipsoids, etc.)"""
  385. def __init__(self, parent, columns, data = None):
  386. wx.ListCtrl.__init__(self, parent = parent, id = wx.ID_ANY,
  387. style = wx.LC_REPORT |
  388. wx.LC_VIRTUAL |
  389. wx.LC_HRULES |
  390. wx.LC_VRULES |
  391. wx.LC_SINGLE_SEL |
  392. wx.LC_SORT_ASCENDING, size = (550, 125))
  393. # original data or None
  394. self.sourceData = data
  395. #
  396. # insert columns
  397. #
  398. i = 0
  399. for column in columns:
  400. self.InsertColumn(i, column)
  401. i += 1
  402. #
  403. # add some attributes
  404. #
  405. self.attr1 = wx.ListItemAttr()
  406. self.attr1.SetBackgroundColour(wx.Colour(238,238,238))
  407. self.attr2 = wx.ListItemAttr()
  408. self.attr2.SetBackgroundColour("white")
  409. if self.sourceData:
  410. self.Populate()
  411. for i in range(self.GetColumnCount()):
  412. self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
  413. if self.GetColumnWidth(i) < 80:
  414. self.SetColumnWidth(i, 80)
  415. #
  416. # listmix
  417. #
  418. listmix.ListCtrlAutoWidthMixin.__init__(self)
  419. listmix.ColumnSorterMixin.__init__(self, self.GetColumnCount())
  420. self.il = wx.ImageList(16, 16)
  421. self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP, wx.ART_TOOLBAR,
  422. (16,16)))
  423. self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR,
  424. (16,16)))
  425. self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
  426. #
  427. # sort by first column
  428. #
  429. if self.sourceData:
  430. self.SortListItems(col = 0, ascending = True)
  431. #
  432. # bindings
  433. #
  434. self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColumnClick)
  435. def Populate(self, data = None, update = False):
  436. """!Populate list"""
  437. self.itemDataMap = {}
  438. self.itemIndexMap = []
  439. if data is None:
  440. data = self.sourceData
  441. elif update:
  442. self.sourceData = data
  443. try:
  444. data.sort()
  445. self.DeleteAllItems()
  446. row = 0
  447. for value in data:
  448. self.itemDataMap[row] = [value[0]]
  449. for i in range(1, len(value)):
  450. self.itemDataMap[row].append(value[i])
  451. self.itemIndexMap.append(row)
  452. row += 1
  453. self.SetItemCount(row)
  454. # set column width
  455. self.SetColumnWidth(0, 80)
  456. self.SetColumnWidth(1, 300)
  457. self.SendSizeEvent()
  458. except StandardError, e:
  459. wx.MessageBox(parent = self,
  460. message = _("Unable to read list: %s") % e,
  461. caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
  462. def OnColumnClick(self, event):
  463. """!Sort by column"""
  464. self._col = event.GetColumn()
  465. # remove duplicated arrow symbol from column header
  466. # FIXME: should be done automatically
  467. info = wx.ListItem()
  468. info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE
  469. info.m_image = -1
  470. for column in range(self.GetColumnCount()):
  471. info.m_text = self.GetColumn(column).GetText()
  472. self.SetColumn(column, info)
  473. event.Skip()
  474. def GetSortImages(self):
  475. """!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
  476. return (self.sm_dn, self.sm_up)
  477. def OnGetItemText(self, item, col):
  478. """!Get item text"""
  479. index = self.itemIndexMap[item]
  480. s = str(self.itemDataMap[index][col])
  481. return s
  482. def OnGetItemAttr(self, item):
  483. """!Get item attributes"""
  484. index = self.itemIndexMap[item]
  485. if ( index % 2) == 0:
  486. return self.attr2
  487. else:
  488. return self.attr1
  489. def SortItems(self, sorter = cmp):
  490. """!Sort items"""
  491. items = list(self.itemDataMap.keys())
  492. items.sort(self.Sorter)
  493. self.itemIndexMap = items
  494. # redraw the list
  495. self.Refresh()
  496. def Sorter(self, key1, key2):
  497. colName = self.GetColumn(self._col).GetText()
  498. ascending = self._colSortFlag[self._col]
  499. # convert always string
  500. item1 = self.itemDataMap[key1][self._col]
  501. item2 = self.itemDataMap[key2][self._col]
  502. if type(item1) == type('') or type(item2) == type(''):
  503. cmpVal = locale.strcoll(str(item1), str(item2))
  504. else:
  505. cmpVal = cmp(item1, item2)
  506. # If the items are equal then pick something else to make the sort value unique
  507. if cmpVal == 0:
  508. cmpVal = apply(cmp, self.GetSecondarySortValues(self._col, key1, key2))
  509. if ascending:
  510. return cmpVal
  511. else:
  512. return -cmpVal
  513. def GetListCtrl(self):
  514. """!Used by listmix.ColumnSorterMixin"""
  515. return self
  516. def Search (self, index, pattern):
  517. """!Search projection by description
  518. Return first found item or None
  519. """
  520. if pattern == '':
  521. self.Populate(self.sourceData)
  522. return []
  523. data = []
  524. pattern = pattern.lower()
  525. for i in range(len(self.sourceData)):
  526. for idx in index:
  527. try:
  528. value = str(self.sourceData[i][idx]).lower()
  529. if pattern in value:
  530. data.append(self.sourceData[i])
  531. break
  532. except UnicodeDecodeError:
  533. # osgeo4w problem (should be fixed)
  534. pass
  535. self.Populate(data)
  536. if len(data) > 0:
  537. return data[0]
  538. else:
  539. return []
  540. class ProjParamsPage(TitledPage):
  541. """!Wizard page for selecting method of setting coordinate system
  542. parameters (select coordinate system option)
  543. """
  544. def __init__(self, wizard, parent):
  545. TitledPage.__init__(self, wizard, _("Choose projection parameters"))
  546. global coordsys
  547. self.parent = parent
  548. self.panel = None
  549. self.prjParamSizer = None
  550. self.pparam = dict()
  551. self.p4projparams = ''
  552. self.projdesc = ''
  553. self.sizer.AddGrowableCol(1)
  554. self.sizer.AddGrowableRow(1)
  555. radioSBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  556. label = " %s " % _("Select datum or ellipsoid (next page)"))
  557. radioSBSizer = wx.StaticBoxSizer(radioSBox)
  558. self.sizer.Add(item = radioSBSizer, pos = (0, 1),
  559. flag = wx.EXPAND | wx.ALIGN_TOP | wx.TOP, border = 10)
  560. self.radio1 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  561. label = _("Datum with associated ellipsoid"),
  562. style = wx.RB_GROUP)
  563. self.radio2 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  564. label = _("Ellipsoid only"))
  565. # default button setting
  566. if self.radio1.GetValue() == False and self.radio2.GetValue() == False:
  567. self.radio1.SetValue(True)
  568. self.SetNext(self.parent.datumpage)
  569. # self.parent.sumpage.SetPrev(self.parent.datumpage)
  570. radioSBSizer.Add(item = self.radio1,
  571. flag = wx.ALIGN_LEFT | wx.RIGHT, border = 20)
  572. radioSBSizer.Add(item = self.radio2,
  573. flag = wx.ALIGN_LEFT)
  574. # bindings
  575. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio1.GetId())
  576. self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio2.GetId())
  577. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChange)
  578. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  579. def OnParamEntry(self, event):
  580. """!Parameter value changed"""
  581. id = event.GetId()
  582. val = event.GetString()
  583. if id not in self.pparam:
  584. event.Skip()
  585. return
  586. param = self.pparam[id]
  587. win = self.FindWindowById(id)
  588. if param['type'] == 'zone':
  589. val = event.GetInt()
  590. if val < 1:
  591. win.SetValue(1)
  592. elif val > 60:
  593. win.SetValue(60)
  594. if param['type'] == 'bool':
  595. param['value'] = event.GetSelection()
  596. else:
  597. param['value'] = val
  598. event.Skip()
  599. def OnPageChange(self,event=None):
  600. """!Go to next page"""
  601. if event.GetDirection():
  602. self.p4projparams = ''
  603. for id, param in self.pparam.iteritems():
  604. if param['type'] == 'bool':
  605. if param['value'] == False:
  606. continue
  607. else:
  608. self.p4projparams += (' +' + param['proj4'])
  609. else:
  610. if param['value'] is None:
  611. wx.MessageBox(parent = self,
  612. message = _('You must enter a value for %s') % param['desc'],
  613. caption = _('Error'), style = wx.ICON_ERROR | wx.CENTRE)
  614. event.Veto()
  615. else:
  616. self.p4projparams += (' +' + param['proj4'] + '=' + str(param['value']))
  617. def OnEnterPage(self,event):
  618. """!Page entered"""
  619. self.projdesc = self.parent.projections[self.parent.projpage.proj][0]
  620. if self.prjParamSizer is None:
  621. # entering page for the first time
  622. self.paramSBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  623. label = _(" Enter parameters for %s projection ") % self.projdesc)
  624. paramSBSizer = wx.StaticBoxSizer(self.paramSBox)
  625. self.panel = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
  626. self.panel.SetupScrolling()
  627. self.prjParamSizer = wx.GridBagSizer(vgap = 0, hgap = 0)
  628. self.sizer.Add(item = paramSBSizer, pos = (1, 1),
  629. flag = wx.EXPAND)
  630. paramSBSizer.Add(item = self.panel, proportion = 1,
  631. flag = wx.ALIGN_CENTER | wx.EXPAND)
  632. paramSBSizer.Fit(self.panel)
  633. self.panel.SetSizer(self.prjParamSizer)
  634. if event.GetDirection():
  635. self.prjParamSizer.Clear(True)
  636. self.paramSBox.SetLabel(_(" Enter parameters for %s projection ") % self.projdesc)
  637. self.pparam = dict()
  638. row = 0
  639. for paramgrp in self.parent.projections[self.parent.projpage.proj][1]:
  640. # get parameters
  641. id = wx.NewId()
  642. param = self.pparam[id] = { 'type' : self.parent.paramdesc[paramgrp[0]][0],
  643. 'proj4': self.parent.paramdesc[paramgrp[0]][1],
  644. 'desc' : self.parent.paramdesc[paramgrp[0]][2] }
  645. # default values
  646. if param['type'] == 'bool':
  647. param['value'] = 0
  648. elif param['type'] == 'zone':
  649. param['value'] = 30
  650. param['desc'] += ' (1-60)'
  651. else:
  652. param['value'] = paramgrp[2]
  653. label = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = param['desc'],
  654. style = wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)
  655. if param['type'] == 'bool':
  656. win = wx.Choice(parent = self.panel, id = id, size = (100,-1),
  657. choices = [_('No'), _('Yes')])
  658. win.SetSelection(param['value'])
  659. win.Bind(wx.EVT_CHOICE, self.OnParamEntry)
  660. elif param['type'] == 'zone':
  661. win = wx.SpinCtrl(parent = self.panel, id = id,
  662. size = (100, -1),
  663. style = wx.SP_ARROW_KEYS | wx.SP_WRAP,
  664. min = 1, max = 60)
  665. win.SetValue(param['value'])
  666. win.Bind(wx.EVT_SPINCTRL, self.OnParamEntry)
  667. win.Bind(wx.EVT_TEXT, self.OnParamEntry)
  668. else:
  669. win = wx.TextCtrl(parent = self.panel, id = id,
  670. value = param['value'],
  671. size=(100, -1))
  672. win.Bind(wx.EVT_TEXT, self.OnParamEntry)
  673. if paramgrp[1] == 'noask':
  674. win.Enable(False)
  675. self.prjParamSizer.Add(item = label, pos = (row, 1),
  676. flag = wx.ALIGN_RIGHT |
  677. wx.ALIGN_CENTER_VERTICAL |
  678. wx.RIGHT, border = 5)
  679. self.prjParamSizer.Add(item = win, pos = (row, 2),
  680. flag = wx.ALIGN_LEFT |
  681. wx.ALIGN_CENTER_VERTICAL |
  682. wx.LEFT, border = 5)
  683. row += 1
  684. self.panel.SetSize(self.panel.GetBestSize())
  685. self.panel.Layout()
  686. self.Layout()
  687. self.Update()
  688. if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
  689. wx.FindWindowById(wx.ID_FORWARD).Enable()
  690. event.Skip()
  691. def SetVal(self, event):
  692. """!Set value"""
  693. if event.GetId() == self.radio1.GetId():
  694. self.SetNext(self.parent.datumpage)
  695. self.parent.sumpage.SetPrev(self.parent.datumpage)
  696. elif event.GetId() == self.radio2.GetId():
  697. self.SetNext(self.parent.ellipsepage)
  698. self.parent.sumpage.SetPrev(self.parent.ellipsepage)
  699. class DatumPage(TitledPage):
  700. """!Wizard page for selecting datum (with associated ellipsoid)
  701. and datum transformation parameters (select coordinate system option)
  702. """
  703. def __init__(self, wizard, parent):
  704. TitledPage.__init__(self, wizard, _("Specify geodetic datum"))
  705. self.parent = parent
  706. self.datum = ''
  707. self.datumdesc = ''
  708. self.ellipse = ''
  709. self.datumparams = ''
  710. self.proj4params = ''
  711. # text input
  712. self.tdatum = self.MakeTextCtrl("", size = (200,-1))
  713. # search box
  714. self.searchb = wx.SearchCtrl(self, size = (200,-1),
  715. style = wx.TE_PROCESS_ENTER)
  716. # create list control for datum/elipsoid list
  717. data = []
  718. for key in self.parent.datums.keys():
  719. data.append([key, self.parent.datums[key][0], self.parent.datums[key][1]])
  720. self.datumlist = ItemList(self,
  721. data = data,
  722. columns = [_('Code'), _('Ellipsoid'), _('Description')])
  723. self.datumlist.resizeLastColumn(10)
  724. # layout
  725. self.sizer.AddGrowableCol(4)
  726. self.sizer.Add(item = self.MakeLabel(_("Datum code:")),
  727. flag = wx.ALIGN_LEFT |
  728. wx.ALIGN_CENTER_VERTICAL |
  729. wx.ALL, border = 5, pos = (1, 1))
  730. self.sizer.Add(item = self.tdatum,
  731. flag = wx.ALIGN_LEFT |
  732. wx.ALIGN_CENTER_VERTICAL |
  733. wx.ALL, border = 5, pos = (1, 2))
  734. self.sizer.Add(item = self.MakeLabel(_("Search in description:")),
  735. flag = wx.ALIGN_LEFT |
  736. wx.ALIGN_CENTER_VERTICAL |
  737. wx.ALL, border = 5, pos = (2, 1))
  738. self.sizer.Add(item = self.searchb,
  739. flag = wx.ALIGN_LEFT |
  740. wx.ALIGN_CENTER_VERTICAL |
  741. wx.ALL, border = 5, pos = (2, 2))
  742. self.sizer.AddGrowableRow(3)
  743. self.sizer.Add(item = self.datumlist,
  744. flag = wx.EXPAND |
  745. wx.ALIGN_LEFT |
  746. wx.ALL, border = 5, pos = (3, 1), span = (1, 4))
  747. # events
  748. self.datumlist.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnDatumSelected)
  749. self.searchb.Bind(wx.EVT_TEXT_ENTER, self.OnDSearch)
  750. self.tdatum.Bind(wx.EVT_TEXT, self.OnDText)
  751. self.tdatum.Bind(wx.EVT_TEXT_ENTER, self.OnDText)
  752. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  753. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  754. # do page layout
  755. # self.DoLayout()
  756. def OnPageChanging(self, event):
  757. self.proj4params = ''
  758. proj = self.parent.projpage.p4proj
  759. if event.GetDirection():
  760. if self.datum not in self.parent.datums:
  761. event.Veto()
  762. else:
  763. # check for datum tranforms
  764. # proj4string = self.parent.CreateProj4String() + ' +datum=%s' % self.datum
  765. ret = RunCommand('g.proj',
  766. read = True,
  767. proj4 = '%s' % proj,
  768. datum = '%s' % self.datum,
  769. datum_trans = '-1')
  770. # wx.Messagebox('here')
  771. if ret != '':
  772. dtrans = ''
  773. # open a dialog to select datum transform number
  774. dlg = SelectTransformDialog(self.parent.parent, transforms=ret)
  775. if dlg.ShowModal() == wx.ID_OK:
  776. dtrans = dlg.GetTransform()
  777. if dtrans == '':
  778. dlg.Destroy()
  779. event.Veto()
  780. return 'Datum transform is required.'
  781. else:
  782. dlg.Destroy()
  783. event.Veto()
  784. return 'Datum transform is required.'
  785. self.parent.datum_trans = dtrans
  786. self.GetNext().SetPrev(self)
  787. self.parent.ellipsepage.ellipse = self.ellipse
  788. self.parent.ellipsepage.ellipseparams = self.parent.ellipsoids[self.ellipse][1]
  789. def OnEnterPage(self,event):
  790. self.parent.datum_trans = None
  791. if event.GetDirection():
  792. if len(self.datum) == 0:
  793. # disable 'next' button by default when entering from previous page
  794. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  795. else:
  796. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  797. event.Skip()
  798. def OnDText(self, event):
  799. """!Datum code changed"""
  800. self.datum = event.GetString()
  801. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  802. if len(self.datum) == 0 or self.datum not in self.parent.datums:
  803. nextButton.Enable(False)
  804. else:
  805. self.ellipse = self.parent.datums[self.datum][0]
  806. self.datumdesc = self.parent.datums[self.datum][1]
  807. self.datumparams = self.parent.datums[self.datum][2]
  808. try:
  809. self.datumparams.remove('dx=0.0')
  810. except:
  811. pass
  812. try:
  813. self.datumparams.remove('dy=0.0')
  814. except:
  815. pass
  816. try:
  817. self.datumparams.remove('dz=0.0')
  818. except:
  819. pass
  820. nextButton.Enable(True)
  821. self.Update()
  822. event.Skip()
  823. def OnDSearch(self, event):
  824. """!Search geodetic datum by desc"""
  825. str = self.searchb.GetValue()
  826. try:
  827. self.datum, self.ellipsoid, self.datumdesc = self.datumlist.Search(index = [0,1,2], pattern = str)
  828. except:
  829. self.datum = self.datumdesc = self.ellipsoid = ''
  830. event.Skip()
  831. def OnDatumSelected(self, event):
  832. """!Datum selected"""
  833. index = event.m_itemIndex
  834. item = event.GetItem()
  835. self.datum = self.datumlist.GetItem(index, 0).GetText()
  836. self.tdatum.SetValue(self.datum)
  837. event.Skip()
  838. class EllipsePage(TitledPage):
  839. """!Wizard page for selecting ellipsoid (select coordinate system option)"""
  840. def __init__(self, wizard, parent):
  841. TitledPage.__init__(self, wizard, _("Specify ellipsoid"))
  842. self.parent = parent
  843. self.ellipse = ''
  844. self.ellipsedesc = ''
  845. self.ellipseparams = ''
  846. self.proj4params = ''
  847. # text input
  848. self.tellipse = self.MakeTextCtrl("", size = (200,-1))
  849. # search box
  850. self.searchb = wx.SearchCtrl(self, size = (200,-1),
  851. style = wx.TE_PROCESS_ENTER)
  852. # radio buttons
  853. self.radio1 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  854. label = _("Earth based"),
  855. style = wx.RB_GROUP)
  856. self.radio2 = wx.RadioButton(parent = self, id = wx.ID_ANY,
  857. label = _("Planetary bodies"))
  858. # create list control for ellipse list
  859. data = []
  860. # extract code, desc
  861. for key in self.parent.ellipsoids.keys():
  862. data.append([key, self.parent.ellipsoids[key][0]])
  863. self.ellipselist = ItemList(self, data = data,
  864. columns = [_('Code'), _('Description')])
  865. self.ellipselist.resizeLastColumn(30)
  866. # layout
  867. self.sizer.AddGrowableCol(4)
  868. self.sizer.Add(item = self.MakeLabel(_("Ellipsoid code:")),
  869. flag = wx.ALIGN_RIGHT |
  870. wx.ALIGN_CENTER_VERTICAL |
  871. wx.ALL, border = 5, pos = (1, 1))
  872. self.sizer.Add(item = self.tellipse,
  873. flag = wx.ALIGN_LEFT |
  874. wx.ALIGN_CENTER_VERTICAL |
  875. wx.ALL, border = 5, pos = (1, 2))
  876. self.sizer.Add(item = self.radio1,
  877. flag = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
  878. border = 25, pos = (1, 3))
  879. self.sizer.Add(item = self.MakeLabel(_("Search in description:")),
  880. flag = wx.ALIGN_RIGHT |
  881. wx.ALIGN_CENTER_VERTICAL |
  882. wx.ALL, border = 5, pos = (2, 1))
  883. self.sizer.Add(item = self.searchb,
  884. flag = wx.ALIGN_LEFT |
  885. wx.ALIGN_CENTER_VERTICAL |
  886. wx.ALL, border = 5, pos = (2, 2))
  887. self.sizer.Add(item = self.radio2,
  888. flag = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
  889. border = 25, pos = (2, 3))
  890. self.sizer.AddGrowableRow(3)
  891. self.sizer.Add(item = self.ellipselist,
  892. flag = wx.EXPAND |
  893. wx.ALIGN_LEFT |
  894. wx.ALL, border = 5, pos = (3, 1), span = (1, 4))
  895. # events
  896. self.ellipselist.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
  897. self.tellipse.Bind(wx.EVT_TEXT, self.OnText)
  898. self.tellipse.Bind(wx.EVT_TEXT_ENTER, self.OnText)
  899. self.searchb.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
  900. self.radio1.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio1.GetId())
  901. self.radio2.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio2.GetId())
  902. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  903. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  904. def OnEnterPage(self,event):
  905. if len(self.ellipse) == 0:
  906. # disable 'next' button by default
  907. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  908. else:
  909. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  910. self.scope = 'earth'
  911. event.Skip()
  912. def OnPageChanging(self, event):
  913. if event.GetDirection() \
  914. and self.ellipse not in self.parent.ellipsoids \
  915. and self.ellipse not in self.parent.planetary_ellipsoids:
  916. event.Veto()
  917. #print self.ellipse, self.ellipsedesc, self.ellipseparams
  918. self.proj4params = ''
  919. self.GetNext().SetPrev(self)
  920. self.parent.datumpage.datumparams = ''
  921. # self.GetNext().SetPrev(self) (???)
  922. #FIXME: index number doesn't translate when you've given a valid name from the other list
  923. def OnText(self, event):
  924. """!Ellipspoid code changed"""
  925. self.ellipse = event.GetString()
  926. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  927. if len(self.ellipse) == 0 or \
  928. (self.ellipse not in self.parent.ellipsoids and \
  929. self.ellipse not in self.parent.planetary_ellipsoids):
  930. nextButton.Enable(False)
  931. self.ellipsedesc = ''
  932. self.ellipseparams = ''
  933. self.proj4params = ''
  934. elif self.ellipse in self.parent.ellipsoids:
  935. self.ellipsedesc = self.parent.ellipsoids[self.ellipse][0]
  936. self.ellipseparams = self.parent.ellipsoids[self.ellipse][1]
  937. nextButton.Enable(True)
  938. elif self.ellipse in self.parent.planetary_ellipsoids:
  939. self.ellipsedesc = self.parent.planetary_ellipsoids[self.ellipse][0]
  940. self.ellipseparams = self.parent.planetary_ellipsoids[self.ellipse][1]
  941. nextButton.Enable(True)
  942. #print self.ellipse, self.ellipsedesc, self.ellipseparams
  943. def OnSearch(self, event):
  944. """!Search ellipsoid by desc"""
  945. try:
  946. self.ellipse, self.ellipsedesc = \
  947. self.ellipselist.Search(index=[0,1], pattern=event.GetString())
  948. if self.scope is 'earth':
  949. self.ellipseparams = self.parent.ellipsoids[self.ellipse][1]
  950. else:
  951. self.ellipseparams = self.parent.planetary_ellipsoids[self.ellipse][1]
  952. except:
  953. self.ellipse = self.ellipsedesc = self.ellipseparams = ''
  954. event.Skip()
  955. def OnItemSelected(self,event):
  956. """!Ellipsoid selected"""
  957. index = event.m_itemIndex
  958. item = event.GetItem()
  959. self.ellipse = self.ellipselist.GetItem(index, 0).GetText()
  960. self.tellipse.SetValue(self.ellipse)
  961. event.Skip()
  962. def SetVal(self, event):
  963. """!Choose table to use"""
  964. self.ellipselist.DeleteAllItems()
  965. data = []
  966. if event.GetId() == self.radio1.GetId():
  967. self.scope = 'earth'
  968. for key in self.parent.ellipsoids.keys():
  969. data.append([key, self.parent.ellipsoids[key][0]])
  970. elif event.GetId() == self.radio2.GetId():
  971. self.scope = 'planetary'
  972. for key in self.parent.planetary_ellipsoids.keys():
  973. data.append([key, self.parent.planetary_ellipsoids[key][0]])
  974. self.ellipselist.Populate(data = data, update = True)
  975. class GeoreferencedFilePage(TitledPage):
  976. """!Wizard page for selecting georeferenced file to use
  977. for setting coordinate system parameters"""
  978. def __init__(self, wizard, parent):
  979. TitledPage.__init__(self, wizard, _("Select georeferenced file"))
  980. self.georeffile = ''
  981. # create controls
  982. self.lfile= self.MakeLabel(_("Georeferenced file:"))
  983. self.tfile = self.MakeTextCtrl(size = (300,-1))
  984. self.bbrowse = self.MakeButton(_("Browse"))
  985. # do layout
  986. self.sizer.AddGrowableCol(3)
  987. self.sizer.Add(item = self.lfile, flag = wx.ALIGN_LEFT |
  988. wx.ALIGN_CENTRE_VERTICAL |
  989. wx.ALL, border = 5, pos = (1, 1))
  990. self.sizer.Add(item = self.tfile, flag = wx.ALIGN_LEFT |
  991. wx.ALIGN_CENTRE_VERTICAL |
  992. wx.ALL, border = 5, pos = (1, 2))
  993. self.sizer.Add(item = self.bbrowse, flag = wx.ALIGN_LEFT |
  994. wx.ALL, border = 5, pos = (1, 3))
  995. self.bbrowse.Bind(wx.EVT_BUTTON, self.OnBrowse)
  996. self.tfile.Bind(wx.EVT_TEXT, self.OnText)
  997. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  998. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  999. # do page layout
  1000. # self.DoLayout()
  1001. def OnEnterPage(self, event):
  1002. if len(self.georeffile) == 0:
  1003. # disable 'next' button by default
  1004. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  1005. else:
  1006. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  1007. event.Skip()
  1008. def OnPageChanging(self, event):
  1009. if event.GetDirection() and not os.path.isfile(self.georeffile):
  1010. event.Veto()
  1011. self.GetNext().SetPrev(self)
  1012. event.Skip()
  1013. def OnText(self, event):
  1014. """!File changed"""
  1015. self.georeffile = event.GetString()
  1016. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  1017. if len(self.georeffile) > 0 and os.path.isfile(self.georeffile):
  1018. if not nextButton.IsEnabled():
  1019. nextButton.Enable(True)
  1020. else:
  1021. if nextButton.IsEnabled():
  1022. nextButton.Enable(False)
  1023. event.Skip()
  1024. def OnBrowse(self, event):
  1025. """!Choose file"""
  1026. dlg = wx.FileDialog(self,
  1027. _("Select georeferenced file"),
  1028. os.getcwd(), "", "*.*", wx.OPEN)
  1029. if dlg.ShowModal() == wx.ID_OK:
  1030. path = dlg.GetPath()
  1031. self.tfile.SetValue(path)
  1032. dlg.Destroy()
  1033. event.Skip()
  1034. class WKTPage(TitledPage):
  1035. """!Wizard page for selecting WKT file to use
  1036. for setting coordinate system parameters"""
  1037. def __init__(self, wizard, parent):
  1038. TitledPage.__init__(self, wizard, _("Select WKT or PRJ file"))
  1039. self.wktfile = ''
  1040. # create controls
  1041. self.lfile= self.MakeLabel(_("WKT or PRJ file:"))
  1042. self.tfile = self.MakeTextCtrl(size = (300,-1))
  1043. self.bbrowse = self.MakeButton(_("Browse"))
  1044. # do layout
  1045. self.sizer.AddGrowableCol(3)
  1046. self.sizer.Add(item = self.lfile, flag = wx.ALIGN_LEFT |
  1047. wx.ALIGN_CENTRE_VERTICAL |
  1048. wx.ALL, border = 5, pos = (1, 1))
  1049. self.sizer.Add(item = self.tfile, flag = wx.ALIGN_LEFT |
  1050. wx.ALIGN_CENTRE_VERTICAL |
  1051. wx.ALL, border = 5, pos = (1, 2))
  1052. self.sizer.Add(item = self.bbrowse, flag = wx.ALIGN_LEFT |
  1053. wx.ALL, border = 5, pos = (1, 3))
  1054. self.bbrowse.Bind(wx.EVT_BUTTON, self.OnBrowse)
  1055. self.tfile.Bind(wx.EVT_TEXT, self.OnText)
  1056. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  1057. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  1058. def OnEnterPage(self, event):
  1059. if len(self.wktfile) == 0:
  1060. # disable 'next' button by default
  1061. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  1062. else:
  1063. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  1064. event.Skip()
  1065. def OnPageChanging(self, event):
  1066. if event.GetDirection() and not os.path.isfile(self.wktfile):
  1067. event.Veto()
  1068. self.GetNext().SetPrev(self)
  1069. event.Skip()
  1070. def OnText(self, event):
  1071. """!File changed"""
  1072. self.wktfile = event.GetString()
  1073. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  1074. if len(self.wktfile) > 0 and os.path.isfile(self.wktfile):
  1075. if not nextButton.IsEnabled():
  1076. nextButton.Enable(True)
  1077. else:
  1078. if nextButton.IsEnabled():
  1079. nextButton.Enable(False)
  1080. event.Skip()
  1081. def OnBrowse(self, event):
  1082. """!Choose file"""
  1083. dlg = wx.FileDialog(parent = self, message = _("Select WKT or PRJ file"),
  1084. defaultDir = os.getcwd(),
  1085. wildcard = "PRJ files (*.prj)|*.prj|Files (*.*)|*.*", style = wx.OPEN)
  1086. if dlg.ShowModal() == wx.ID_OK:
  1087. path = dlg.GetPath()
  1088. self.tfile.SetValue(path)
  1089. dlg.Destroy()
  1090. event.Skip()
  1091. class EPSGPage(TitledPage):
  1092. """!Wizard page for selecting EPSG code for
  1093. setting coordinate system parameters"""
  1094. def __init__(self, wizard, parent):
  1095. TitledPage.__init__(self, wizard, _("Choose EPSG Code"))
  1096. self.parent = parent
  1097. self.epsgCodeDict = {}
  1098. self.epsgcode = None
  1099. self.epsgdesc = ''
  1100. self.epsgparams = ''
  1101. # labels
  1102. self.lfile = self.MakeLabel(_("Path to the EPSG-codes file:"),
  1103. style = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
  1104. self.lcode = self.MakeLabel(_("EPSG code:"),
  1105. style = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
  1106. # text input
  1107. epsgdir = utils.PathJoin(os.environ["GRASS_PROJSHARE"], 'epsg')
  1108. self.tfile = self.MakeTextCtrl(text = epsgdir, size = (200,-1),
  1109. style = wx.TE_PROCESS_ENTER)
  1110. self.tcode = self.MakeTextCtrl(size = (200,-1))
  1111. # buttons
  1112. self.bbrowse = self.MakeButton(_("Browse"))
  1113. # search box
  1114. self.searchb = wx.SearchCtrl(self, size = (200,-1),
  1115. style = wx.TE_PROCESS_ENTER)
  1116. self.epsglist = ItemList(self, data = None,
  1117. columns = [_('Code'), _('Description'), _('Parameters')])
  1118. # layout
  1119. self.sizer.AddGrowableCol(3)
  1120. self.sizer.Add(item = self.lfile,
  1121. flag = wx.ALIGN_LEFT |
  1122. wx.ALIGN_CENTER_VERTICAL |
  1123. wx.ALL, border = 5, pos = (1, 1), span = (1, 2))
  1124. self.sizer.Add(item = self.tfile,
  1125. flag = wx.ALIGN_LEFT |
  1126. wx.ALIGN_CENTER_VERTICAL |
  1127. wx.ALL, border = 5, pos = (1, 3))
  1128. self.sizer.Add(item = self.bbrowse,
  1129. flag = wx.ALIGN_LEFT |
  1130. wx.ALIGN_CENTER_VERTICAL |
  1131. wx.ALL, border = 5, pos = (1, 4))
  1132. self.sizer.Add(item = self.lcode,
  1133. flag = wx.ALIGN_LEFT |
  1134. wx.ALIGN_CENTER_VERTICAL |
  1135. wx.ALL, border = 5, pos = (2, 1), span = (1, 2))
  1136. self.sizer.Add(item = self.tcode,
  1137. flag = wx.ALIGN_LEFT |
  1138. wx.ALIGN_CENTER_VERTICAL |
  1139. wx.ALL, border = 5, pos = (2, 3))
  1140. self.sizer.Add(item = self.searchb,
  1141. flag = wx.ALIGN_LEFT |
  1142. wx.ALIGN_CENTER_VERTICAL |
  1143. wx.ALL, border = 5, pos = (3, 3))
  1144. self.sizer.AddGrowableRow(4)
  1145. self.sizer.Add(item = self.epsglist,
  1146. flag = wx.ALIGN_LEFT | wx.EXPAND, pos = (4, 1),
  1147. span = (1, 4))
  1148. # events
  1149. self.bbrowse.Bind(wx.EVT_BUTTON, self.OnBrowse)
  1150. self.tfile.Bind(wx.EVT_TEXT_ENTER, self.OnBrowseCodes)
  1151. self.tcode.Bind(wx.EVT_TEXT, self.OnText)
  1152. self.tcode.Bind(wx.EVT_TEXT_ENTER, self.OnText)
  1153. self.epsglist.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
  1154. self.searchb.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
  1155. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  1156. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  1157. def OnEnterPage(self, event):
  1158. self.parent.datum_trans = None
  1159. if event.GetDirection():
  1160. if not self.epsgcode:
  1161. # disable 'next' button by default
  1162. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  1163. else:
  1164. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  1165. # load default epsg database file
  1166. self.OnBrowseCodes(None)
  1167. event.Skip()
  1168. def OnPageChanging(self, event):
  1169. if event.GetDirection():
  1170. if not self.epsgcode:
  1171. event.Veto()
  1172. return
  1173. else:
  1174. # check for datum transforms
  1175. ret = RunCommand('g.proj',
  1176. read = True,
  1177. epsg = self.epsgcode,
  1178. datum_trans = '-1')
  1179. if ret != '':
  1180. dtrans = ''
  1181. # open a dialog to select datum transform number
  1182. dlg = SelectTransformDialog(self.parent.parent, transforms = ret)
  1183. if dlg.ShowModal() == wx.ID_OK:
  1184. dtrans = dlg.GetTransform()
  1185. if dtrans == '':
  1186. dlg.Destroy()
  1187. event.Veto()
  1188. return 'Datum transform is required.'
  1189. else:
  1190. dlg.Destroy()
  1191. event.Veto()
  1192. return 'Datum transform is required.'
  1193. self.parent.datum_trans = dtrans
  1194. self.GetNext().SetPrev(self)
  1195. def OnText(self, event):
  1196. self.epsgcode = event.GetString()
  1197. try:
  1198. self.epsgcode = int(self.epsgcode)
  1199. except:
  1200. self.epsgcode = None
  1201. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  1202. if self.epsgcode and self.epsgcode in self.epsgCodeDict.keys():
  1203. self.epsgdesc = self.epsgCodeDict[self.epsgcode][0]
  1204. self.epsgparams = self.epsgCodeDict[self.epsgcode][1]
  1205. if not nextButton.IsEnabled():
  1206. nextButton.Enable(True)
  1207. else:
  1208. self.epsgcode = None # not found
  1209. if nextButton.IsEnabled():
  1210. nextButton.Enable(False)
  1211. self.epsgdesc = self.epsgparams = ''
  1212. def OnSearch(self, event):
  1213. value = self.searchb.GetValue()
  1214. if value == '':
  1215. self.epsgcode = None
  1216. self.epsgdesc = self.epsgparams = ''
  1217. self.tcode.SetValue('')
  1218. self.searchb.SetValue('')
  1219. self.OnBrowseCodes(None)
  1220. else:
  1221. try:
  1222. self.epsgcode, self.epsgdesc, self.epsgparams = \
  1223. self.epsglist.Search(index=[0,1,2], pattern=value)
  1224. except (IndexError, ValueError): # -> no item found
  1225. self.epsgcode = None
  1226. self.epsgdesc = self.epsgparams = ''
  1227. self.tcode.SetValue('')
  1228. self.searchb.SetValue('')
  1229. event.Skip()
  1230. def OnBrowse(self, event):
  1231. """!Define path for EPSG code file"""
  1232. path = os.path.dirname(self.tfile.GetValue())
  1233. if not path:
  1234. path = os.getcwd()
  1235. dlg = wx.FileDialog(parent = self, message = _("Choose EPSG codes file"),
  1236. defaultDir = path, defaultFile = "", wildcard = "*", style = wx.OPEN)
  1237. if dlg.ShowModal() == wx.ID_OK:
  1238. path = dlg.GetPath()
  1239. self.tfile.SetValue(path)
  1240. self.OnBrowseCodes(None)
  1241. dlg.Destroy()
  1242. event.Skip()
  1243. def OnItemSelected(self, event):
  1244. """!EPSG code selected from the list"""
  1245. index = event.m_itemIndex
  1246. item = event.GetItem()
  1247. self.epsgcode = int(self.epsglist.GetItem(index, 0).GetText())
  1248. self.epsgdesc = self.epsglist.GetItem(index, 1).GetText()
  1249. self.tcode.SetValue(str(self.epsgcode))
  1250. event.Skip()
  1251. def OnBrowseCodes(self, event, search = None):
  1252. """!Browse EPSG codes"""
  1253. self.epsgCodeDict = utils.ReadEpsgCodes(self.tfile.GetValue())
  1254. if type(self.epsgCodeDict) != dict:
  1255. wx.MessageBox(parent = self,
  1256. message = _("Unable to read EPGS codes: %s") % self.epsgCodeDict,
  1257. caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
  1258. self.epsglist.Populate(list(), update = True)
  1259. return
  1260. data = list()
  1261. for code, val in self.epsgCodeDict.iteritems():
  1262. if code is not None:
  1263. data.append((code, val[0], val[1]))
  1264. self.epsglist.Populate(data, update = True)
  1265. class CustomPage(TitledPage):
  1266. """!Wizard page for entering custom PROJ.4 string
  1267. for setting coordinate system parameters"""
  1268. def __init__(self, wizard, parent):
  1269. TitledPage.__init__(self, wizard,
  1270. _("Choose method of specifying georeferencing parameters"))
  1271. global coordsys
  1272. self.customstring = ''
  1273. self.parent = parent
  1274. # widgets
  1275. self.text_proj4string = self.MakeTextCtrl(size = (400, 200),
  1276. style = wx.TE_MULTILINE)
  1277. self.label_proj4string = self.MakeLabel(_("Enter PROJ.4 parameters string:"))
  1278. # layout
  1279. self.sizer.AddGrowableCol(2)
  1280. self.sizer.Add(self.label_proj4string,
  1281. flag = wx.ALIGN_LEFT | wx.ALL,
  1282. border = 5, pos = (1, 1))
  1283. self.sizer.AddGrowableRow(2)
  1284. self.sizer.Add(self.text_proj4string,
  1285. flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
  1286. border = 5, pos = (2, 1), span = (1, 2))
  1287. self.text_proj4string.Bind(wx.EVT_TEXT, self.GetProjstring)
  1288. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
  1289. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  1290. def OnEnterPage(self, event):
  1291. if len(self.customstring) == 0:
  1292. # disable 'next' button by default
  1293. wx.FindWindowById(wx.ID_FORWARD).Enable(False)
  1294. else:
  1295. wx.FindWindowById(wx.ID_FORWARD).Enable(True)
  1296. def OnPageChanging(self, event):
  1297. if event.GetDirection():
  1298. # check for datum tranforms
  1299. ret, out, err = RunCommand('g.proj',
  1300. read = True, getErrorMsg = True,
  1301. proj4 = self.customstring,
  1302. datum_trans = '-1')
  1303. if ret != 0:
  1304. wx.MessageBox(parent = self,
  1305. message = err,
  1306. caption = _("Error"),
  1307. style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
  1308. event.Veto()
  1309. return
  1310. if out:
  1311. dtrans = ''
  1312. # open a dialog to select datum transform number
  1313. dlg = SelectTransformDialog(self.parent.parent, transforms = out)
  1314. if dlg.ShowModal() == wx.ID_OK:
  1315. dtrans = dlg.GetTransform()
  1316. if dtrans == '':
  1317. dlg.Destroy()
  1318. event.Veto()
  1319. return _('Datum transform is required.')
  1320. else:
  1321. dlg.Destroy()
  1322. event.Veto()
  1323. return _('Datum transform is required.')
  1324. self.parent.datum_trans = dtrans
  1325. self.GetNext().SetPrev(self)
  1326. def GetProjstring(self, event):
  1327. """!Change proj string"""
  1328. # TODO: check PROJ.4 syntax
  1329. self.customstring = event.GetString()
  1330. nextButton = wx.FindWindowById(wx.ID_FORWARD)
  1331. if len(self.customstring) == 0:
  1332. if nextButton.IsEnabled():
  1333. nextButton.Enable(False)
  1334. else:
  1335. if not nextButton.IsEnabled():
  1336. nextButton.Enable()
  1337. class SummaryPage(TitledPage):
  1338. """!Shows summary result of choosing coordinate system parameters
  1339. prior to creating location"""
  1340. def __init__(self, wizard, parent):
  1341. TitledPage.__init__(self, wizard, _("Summary"))
  1342. self.parent = parent
  1343. self.panelTitle = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
  1344. self.panelProj4string = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
  1345. self.panelProj = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
  1346. # labels
  1347. self.ldatabase = self.MakeLabel()
  1348. self.llocation = self.MakeLabel()
  1349. self.llocTitle = self.MakeLabel(parent = self.panelTitle)
  1350. self.lprojection = self.MakeLabel(parent = self.panelProj)
  1351. self.lproj4string = self.MakeLabel(parent = self.panelProj4string)
  1352. self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
  1353. # do sub-page layout
  1354. self._doLayout()
  1355. def _doLayout(self):
  1356. """!Do page layout"""
  1357. self.sizer.AddGrowableCol(1)
  1358. self.sizer.AddGrowableRow(3, 1)
  1359. self.sizer.AddGrowableRow(4, 1)
  1360. self.sizer.AddGrowableRow(5, 5)
  1361. titleSizer = wx.BoxSizer(wx.VERTICAL)
  1362. titleSizer.Add(item = self.llocTitle, proportion = 1,
  1363. flag = wx.EXPAND | wx.ALL, border = 5)
  1364. self.panelTitle.SetSizer(titleSizer)
  1365. projSizer = wx.BoxSizer(wx.VERTICAL)
  1366. projSizer.Add(item = self.lprojection, proportion = 1,
  1367. flag = wx.EXPAND | wx.ALL, border = 5)
  1368. self.panelProj.SetSizer(projSizer)
  1369. proj4stringSizer = wx.BoxSizer(wx.VERTICAL)
  1370. proj4stringSizer.Add(item = self.lproj4string, proportion = 1,
  1371. flag = wx.EXPAND | wx.ALL, border = 5)
  1372. self.panelProj4string.SetSizer(proj4stringSizer)
  1373. self.panelProj4string.SetupScrolling()
  1374. self.panelProj.SetupScrolling(scroll_y = False)
  1375. self.panelTitle.SetupScrolling(scroll_y = False)
  1376. self.sizer.Add(item = self.MakeLabel(_("GRASS Database:")),
  1377. flag = wx.ALIGN_LEFT | wx.ALL,
  1378. border = 5, pos = (1, 0))
  1379. self.sizer.Add(item = self.ldatabase,
  1380. flag = wx.ALIGN_LEFT | wx.ALL,
  1381. border = 5, pos = (1, 1))
  1382. self.sizer.Add(item = self.MakeLabel(_("Location Name:")),
  1383. flag = wx.ALIGN_LEFT | wx.ALL,
  1384. border = 5, pos = (2, 0))
  1385. self.sizer.Add(item = self.llocation,
  1386. flag = wx.ALIGN_LEFT | wx.ALL,
  1387. border = 5, pos = (2, 1))
  1388. self.sizer.Add(item = self.MakeLabel(_("Location Title:")),
  1389. flag = wx.ALIGN_LEFT | wx.ALL,
  1390. border = 5, pos = (3, 0))
  1391. self.sizer.Add(item = self.panelTitle,
  1392. flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
  1393. border = 0, pos = (3, 1))
  1394. self.sizer.Add(item = self.MakeLabel(_("Projection:")),
  1395. flag = wx.ALIGN_LEFT | wx.ALL,
  1396. border = 5, pos = (4, 0))
  1397. self.sizer.Add(item = self.panelProj,
  1398. flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
  1399. border = 0, pos = (4, 1))
  1400. self.sizer.Add(item = self.MakeLabel(_("PROJ.4 definition:")),
  1401. flag = wx.ALIGN_LEFT | wx.ALL,
  1402. border = 5, pos = (5, 0))
  1403. self.sizer.Add(item = self.panelProj4string,
  1404. flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
  1405. border = 0, pos = (5, 1))
  1406. def OnEnterPage(self, event):
  1407. """!Insert values into text controls for summary of location
  1408. creation options
  1409. """
  1410. database = self.parent.startpage.grassdatabase
  1411. location = self.parent.startpage.location
  1412. proj4string = self.parent.CreateProj4String()
  1413. epsgcode = self.parent.epsgpage.epsgcode
  1414. datum = self.parent.datumpage.datum
  1415. dtrans = self.parent.datum_trans
  1416. global coordsys
  1417. #print coordsys,proj4string
  1418. if coordsys in ('proj', 'epsg'):
  1419. if coordsys == 'proj':
  1420. addl_opts = {}
  1421. if len(datum) > 0:
  1422. addl_opts['datum'] = '%s' % datum
  1423. addl_opts['datumtrans'] = dtrans
  1424. ret, projlabel, err = RunCommand('g.proj',
  1425. flags = 'jf',
  1426. proj4 = proj4string,
  1427. location = location,
  1428. getErrorMsg = True,
  1429. read = True,
  1430. **addl_opts)
  1431. elif coordsys == 'epsg':
  1432. ret, projlabel, err = RunCommand('g.proj',
  1433. flags = 'jf',
  1434. epsg = epsgcode,
  1435. datum_trans = dtrans,
  1436. location = location,
  1437. getErrorMsg = True,
  1438. read = True)
  1439. finishButton = wx.FindWindowById(wx.ID_FORWARD)
  1440. if ret == 0:
  1441. if datum != '':
  1442. projlabel = projlabel + ' ' + 'datum=%s' % datum
  1443. self.lproj4string.SetLabel(projlabel.replace(' ', os.linesep))
  1444. finishButton.Enable(True)
  1445. else:
  1446. GError(err, parent = self)
  1447. self.lproj4string.SetLabel('')
  1448. finishButton.Enable(False)
  1449. projdesc = self.parent.projpage.projdesc
  1450. ellipsedesc = self.parent.ellipsepage.ellipsedesc
  1451. datumdesc = self.parent.datumpage.datumdesc
  1452. #print projdesc,ellipsedesc,datumdesc
  1453. self.ldatabase.SetLabel(database)
  1454. self.llocation.SetLabel(location)
  1455. self.llocTitle.SetLabel(self.parent.startpage.locTitle)
  1456. label = ''
  1457. if coordsys == 'epsg':
  1458. label = 'EPSG code %s (%s)' % (self.parent.epsgpage.epsgcode, self.parent.epsgpage.epsgdesc)
  1459. elif coordsys == 'file':
  1460. label = 'matches file %s' % self.parent.filepage.georeffile
  1461. self.lproj4string.SetLabel("")
  1462. elif coordsys == 'wkt':
  1463. label = 'matches file %s' % self.parent.wktpage.wktfile
  1464. self.lproj4string.SetLabel("")
  1465. elif coordsys == 'proj':
  1466. label = ('%s, %s %s' % (projdesc, datumdesc, ellipsedesc))
  1467. elif coordsys == 'xy':
  1468. label = ('XY coordinate system (not projected).')
  1469. self.lproj4string.SetLabel("")
  1470. elif coordsys == 'custom':
  1471. label = _("custom")
  1472. self.lproj4string.SetLabel(('%s' % self.parent.custompage.customstring.replace(' ', os.linesep)))
  1473. self.lprojection.SetLabel(label)
  1474. def OnFinish(self, event):
  1475. dlg = wx.MessageDialog(parent = self.wizard,
  1476. message = _("Do you want to create GRASS location <%s>?") % location,
  1477. caption = _("Create new location?"),
  1478. style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
  1479. if dlg.ShowModal() == wx.ID_NO:
  1480. dlg.Destroy()
  1481. event.Veto()
  1482. else:
  1483. dlg.Destroy()
  1484. event.Skip()
  1485. class LocationWizard(wx.Object):
  1486. """!Start wizard here and finish wizard here
  1487. """
  1488. def __init__(self, parent, grassdatabase):
  1489. self.__cleanUp()
  1490. global coordsys
  1491. self.parent = parent
  1492. #
  1493. # define wizard image
  1494. #
  1495. imagePath = os.path.join(globalvar.ETCIMGDIR, "loc_wizard_qgis.png")
  1496. wizbmp = wx.Image(imagePath, wx.BITMAP_TYPE_PNG)
  1497. wizbmp = wizbmp.ConvertToBitmap()
  1498. #
  1499. # get georeferencing information from tables in $GISBASE/etc
  1500. #
  1501. self.__readData()
  1502. #
  1503. # datum transform number and list of datum transforms
  1504. #
  1505. self.datum_trans = None
  1506. self.proj4string = ''
  1507. # file from which new location is created
  1508. self.georeffile = None
  1509. #
  1510. # define wizard pages
  1511. #
  1512. self.wizard = WizardWithHelpButton(parent, id = wx.ID_ANY, title = _("Define new GRASS Location"),
  1513. bitmap = wizbmp)
  1514. self.wizard.Bind(wiz.EVT_WIZARD_HELP, self.OnHelp)
  1515. self.startpage = DatabasePage(self.wizard, self, grassdatabase)
  1516. self.csystemspage = CoordinateSystemPage(self.wizard, self)
  1517. self.projpage = ProjectionsPage(self.wizard, self)
  1518. self.datumpage = DatumPage(self.wizard, self)
  1519. self.paramspage = ProjParamsPage(self.wizard,self)
  1520. self.epsgpage = EPSGPage(self.wizard, self)
  1521. self.filepage = GeoreferencedFilePage(self.wizard, self)
  1522. self.wktpage = WKTPage(self.wizard, self)
  1523. self.ellipsepage = EllipsePage(self.wizard, self)
  1524. self.custompage = CustomPage(self.wizard, self)
  1525. self.sumpage = SummaryPage(self.wizard, self)
  1526. #
  1527. # set the initial order of the pages
  1528. # (should follow the epsg line)
  1529. #
  1530. self.startpage.SetNext(self.csystemspage)
  1531. self.csystemspage.SetPrev(self.startpage)
  1532. self.csystemspage.SetNext(self.sumpage)
  1533. self.projpage.SetPrev(self.csystemspage)
  1534. self.projpage.SetNext(self.paramspage)
  1535. self.paramspage.SetPrev(self.projpage)
  1536. self.paramspage.SetNext(self.datumpage)
  1537. self.datumpage.SetPrev(self.paramspage)
  1538. self.datumpage.SetNext(self.sumpage)
  1539. self.ellipsepage.SetPrev(self.paramspage)
  1540. self.ellipsepage.SetNext(self.sumpage)
  1541. self.epsgpage.SetPrev(self.csystemspage)
  1542. self.epsgpage.SetNext(self.sumpage)
  1543. self.filepage.SetPrev(self.csystemspage)
  1544. self.filepage.SetNext(self.sumpage)
  1545. self.wktpage.SetPrev(self.csystemspage)
  1546. self.wktpage.SetNext(self.sumpage)
  1547. self.custompage.SetPrev(self.csystemspage)
  1548. self.custompage.SetNext(self.sumpage)
  1549. self.sumpage.SetPrev(self.csystemspage)
  1550. #
  1551. # do pages layout
  1552. #
  1553. self.startpage.DoLayout()
  1554. self.csystemspage.DoLayout()
  1555. self.projpage.DoLayout()
  1556. self.datumpage.DoLayout()
  1557. self.paramspage.DoLayout()
  1558. self.epsgpage.DoLayout()
  1559. self.filepage.DoLayout()
  1560. self.wktpage.DoLayout()
  1561. self.ellipsepage.DoLayout()
  1562. self.custompage.DoLayout()
  1563. self.sumpage.DoLayout()
  1564. self.wizard.FitToPage(self.datumpage)
  1565. size = self.wizard.GetPageSize()
  1566. self.wizard.SetPageSize((size[0], size[1] + 75))
  1567. # new location created?
  1568. self.location = None
  1569. success = False
  1570. # location created in different GIS database?
  1571. self.altdb = False
  1572. #
  1573. # run wizard...
  1574. #
  1575. if self.wizard.RunWizard(self.startpage):
  1576. msg = self.OnWizFinished()
  1577. if not msg:
  1578. self.wizard.Destroy()
  1579. self.location = self.startpage.location
  1580. self.grassdatabase = self.startpage.grassdatabase
  1581. self.georeffile = self.filepage.georeffile
  1582. # FIXME here was code for setting default region, what for is this if:
  1583. # if self.altdb == False:
  1584. else: # -> error
  1585. self.wizard.Destroy()
  1586. GError(parent = self.parent,
  1587. message = "%s" % _("Unable to create new location. "
  1588. "Location <%(loc)s> not created.\n\n"
  1589. "Details: %(err)s") % \
  1590. { 'loc' : self.startpage.location,
  1591. 'err' : msg })
  1592. else: # -> canceled
  1593. self.wizard.Destroy()
  1594. GMessage(parent = self.parent,
  1595. message = _("Location wizard canceled. "
  1596. "Location not created."))
  1597. self.__cleanUp()
  1598. def __cleanUp(self):
  1599. global coordsys
  1600. global north
  1601. global south
  1602. global east
  1603. global west
  1604. global resolution
  1605. global wizerror
  1606. global translist
  1607. coordsys = None
  1608. north = None
  1609. south = None
  1610. east = None
  1611. west = None
  1612. resolution = None
  1613. transformlist = list()
  1614. def __readData(self):
  1615. """!Get georeferencing information from tables in $GISBASE/etc/proj"""
  1616. # read projection and parameters
  1617. f = open(os.path.join(globalvar.ETCDIR, "proj", "parms.table"), "r")
  1618. self.projections = {}
  1619. self.projdesc = {}
  1620. for line in f.readlines():
  1621. line = line.strip()
  1622. try:
  1623. proj, projdesc, params = line.split(':')
  1624. paramslist = params.split(';')
  1625. plist = []
  1626. for p in paramslist:
  1627. if p == '': continue
  1628. p1, pdefault = p.split(',')
  1629. pterm, pask = p1.split('=')
  1630. p = [pterm.strip(), pask.strip(), pdefault.strip()]
  1631. plist.append(p)
  1632. self.projections[proj.lower().strip()] = (projdesc.strip(), plist)
  1633. self.projdesc[proj.lower().strip()] = projdesc.strip()
  1634. except:
  1635. continue
  1636. f.close()
  1637. # read datum definitions
  1638. f = open(os.path.join(globalvar.ETCDIR, "proj", "datum.table"), "r")
  1639. self.datums = {}
  1640. paramslist = []
  1641. for line in f.readlines():
  1642. line = line.expandtabs(1)
  1643. line = line.strip()
  1644. if line == '' or line[0] == "#":
  1645. continue
  1646. datum, info = line.split(" ", 1)
  1647. info = info.strip()
  1648. datumdesc, params = info.split(" ", 1)
  1649. datumdesc = datumdesc.strip('"')
  1650. paramlist = params.split()
  1651. ellipsoid = paramlist.pop(0)
  1652. self.datums[datum] = (ellipsoid, datumdesc.replace('_', ' '), paramlist)
  1653. f.close()
  1654. # read Earth-based ellipsiod definitions
  1655. f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table"), "r")
  1656. self.ellipsoids = {}
  1657. for line in f.readlines():
  1658. line = line.expandtabs(1)
  1659. line = line.strip()
  1660. if line == '' or line[0] == "#":
  1661. continue
  1662. ellipse, rest = line.split(" ", 1)
  1663. rest = rest.strip('" ')
  1664. desc, params = rest.split('"', 1)
  1665. desc = desc.strip('" ')
  1666. paramslist = params.split()
  1667. self.ellipsoids[ellipse] = (desc, paramslist)
  1668. f.close()
  1669. # read Planetary ellipsiod definitions
  1670. f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system"), "r")
  1671. self.planetary_ellipsoids = {}
  1672. for line in f.readlines():
  1673. line = line.expandtabs(1)
  1674. line = line.strip()
  1675. if line == '' or line[0] == "#":
  1676. continue
  1677. ellipse, rest = line.split(" ", 1)
  1678. rest = rest.strip('" ')
  1679. desc, params = rest.split('"', 1)
  1680. desc = desc.strip('" ')
  1681. paramslist = params.split()
  1682. self.planetary_ellipsoids[ellipse] = (desc, paramslist)
  1683. f.close()
  1684. # read projection parameter description and parsing table
  1685. f = open(os.path.join(globalvar.ETCDIR, "proj", "desc.table"), "r")
  1686. self.paramdesc = {}
  1687. for line in f.readlines():
  1688. line = line.strip()
  1689. try:
  1690. pparam, datatype, proj4term, desc = line.split(':')
  1691. self.paramdesc[pparam] = (datatype, proj4term, desc)
  1692. except:
  1693. continue
  1694. f.close()
  1695. def OnWizFinished(self):
  1696. """!Wizard finished, create new location
  1697. @return error message on error
  1698. @return None on success
  1699. """
  1700. database = self.startpage.grassdatabase
  1701. location = self.startpage.location
  1702. # location already exists?
  1703. if os.path.isdir(os.path.join(database,location)):
  1704. GError(parent = self.wizard,
  1705. message = "%s <%s>: %s" % \
  1706. (_("Unable to create new location"),
  1707. os.path.join(database, location),
  1708. _("Location already exists in GRASS Database.")))
  1709. return None
  1710. # current GISDbase or a new one?
  1711. current_gdb = grass.gisenv()['GISDBASE']
  1712. if current_gdb != database:
  1713. # change to new GISDbase or create new one
  1714. if os.path.isdir(database) != True:
  1715. # create new directory
  1716. os.mkdir(database)
  1717. # change to new GISDbase directory
  1718. RunCommand('g.gisenv',
  1719. parent = self.wizard,
  1720. set = 'GISDBASE=%s' % database)
  1721. wx.MessageBox(parent = self.wizard,
  1722. message = _("Location <%(loc)s> will be created "
  1723. "in GIS data directory <%(dir)s>. "
  1724. "You will need to change the default GIS "
  1725. "data directory in the GRASS startup screen.") % \
  1726. { 'loc' : location, 'dir' : database},
  1727. caption = _("New GIS data directory"),
  1728. style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
  1729. # location created in alternate GISDbase
  1730. self.altdb = True
  1731. global coordsys
  1732. try:
  1733. if coordsys == "xy":
  1734. grass.create_location(dbase = self.startpage.grassdatabase,
  1735. location = self.startpage.location,
  1736. desc = self.startpage.locTitle)
  1737. elif coordsys == "proj":
  1738. grass.create_location(dbase = self.startpage.grassdatabase,
  1739. location = self.startpage.location,
  1740. proj4 = self.CreateProj4String(),
  1741. datum = self.datumpage.datum,
  1742. datum_trans = self.datum_trans,
  1743. desc = self.startpage.locTitle)
  1744. elif coordsys == 'custom':
  1745. grass.create_location(dbase = self.startpage.grassdatabase,
  1746. location = self.startpage.location,
  1747. proj4 = self.custompage.customstring,
  1748. desc = self.startpage.locTitle)
  1749. elif coordsys == "epsg":
  1750. if not self.epsgpage.epsgcode:
  1751. return _('EPSG code missing.')
  1752. grass.create_location(dbase = self.startpage.grassdatabase,
  1753. location = self.startpage.location,
  1754. epsg = self.epsgpage.epsgcode,
  1755. datum = self.datumpage.datum,
  1756. datum_trans = self.datum_trans,
  1757. desc = self.startpage.locTitle)
  1758. elif coordsys == "file":
  1759. if not self.filepage.georeffile or \
  1760. not os.path.isfile(self.filepage.georeffile):
  1761. return _("File <%s> not found." % self.filepage.georeffile)
  1762. grass.create_location(dbase = self.startpage.grassdatabase,
  1763. location = self.startpage.location,
  1764. filename = self.filepage.georeffile,
  1765. desc = self.startpage.locTitle)
  1766. elif coordsys == "wkt":
  1767. if not self.wktpage.wktfile or \
  1768. not os.path.isfile(self.wktpage.wktfile):
  1769. return _("File <%s> not found." % self.wktpage.wktfile)
  1770. grass.create_location(dbase = self.startpage.grassdatabase,
  1771. location = self.startpage.location,
  1772. wkt = self.wktpage.wktfile,
  1773. desc = self.startpage.locTitle)
  1774. except grass.ScriptError, e:
  1775. return e.value
  1776. return None
  1777. def CreateProj4String(self):
  1778. """!Constract PROJ.4 string"""
  1779. location = self.startpage.location
  1780. proj = self.projpage.p4proj
  1781. projdesc = self.projpage.projdesc
  1782. proj4params = self.paramspage.p4projparams
  1783. # datum = self.datumpage.datum
  1784. if self.datumpage.datumdesc:
  1785. datumdesc = self.datumpage.datumdesc +' - ' + self.datumpage.ellipse
  1786. else:
  1787. datumdesc = ''
  1788. datumparams = self.datumpage.datumparams
  1789. ellipse = self.ellipsepage.ellipse
  1790. ellipsedesc = self.ellipsepage.ellipsedesc
  1791. ellipseparams = self.ellipsepage.ellipseparams
  1792. #
  1793. # creating PROJ.4 string
  1794. #
  1795. proj4string = '%s %s' % (proj, proj4params)
  1796. # set ellipsoid parameters
  1797. if ellipse != '':
  1798. proj4string = '%s +ellps=%s' % (proj4string, ellipse)
  1799. for item in ellipseparams:
  1800. if item[:4] == 'f=1/':
  1801. item = ' +rf=' + item[4:]
  1802. else:
  1803. item = ' +' + item
  1804. proj4string = '%s %s' % (proj4string, item)
  1805. # set datum transform parameters if relevant
  1806. if datumparams:
  1807. for item in datumparams:
  1808. proj4string = '%s +%s' % (proj4string,item)
  1809. proj4string = '%s +no_defs' % proj4string
  1810. return proj4string
  1811. def OnHelp(self, event):
  1812. """'Help' button clicked"""
  1813. # help text in lib/init/helptext.html
  1814. RunCommand('g.manual', entry = 'helptext')
  1815. class WizardWithHelpButton(wiz.Wizard):
  1816. def __init__(self, parent, id, title, bitmap):
  1817. pre = wiz.PreWizard()
  1818. pre.SetExtraStyle(wx.wizard.WIZARD_EX_HELPBUTTON)
  1819. pre.Create(parent = parent, id = id, title = title, bitmap = bitmap)
  1820. self.PostCreate(pre)