wizard.py 93 KB

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