wizard.py 93 KB

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