wizard.py 94 KB

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