wizard.py 82 KB

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