wizard.py 95 KB

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