wizard.py 96 KB

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