wizard.py 85 KB

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