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