wizard.py 93 KB

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