wizard.py 83 KB

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