wizard.py 98 KB

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