wizard.py 96 KB

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