wizard.py 98 KB

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