wizard.py 99 KB

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