wizard.py 97 KB

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