wizard.py 96 KB

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